@bananacool467/authtics-host 0.0.1-beta.8 → 0.0.2-beta.1

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Authtics Host
2
2
 
3
- Version 0.0.1-beta.8
3
+ Version 0.0.2-beta.1
4
4
  License: MIT
5
5
 
6
6
  **(NOTE: This version is in BETA, bugs are expected)**
package/dist/cli.js CHANGED
@@ -5,7 +5,6 @@ import path from "node:path";
5
5
  import fs from "node:fs";
6
6
  import url from "node:url";
7
7
  import { renderPage } from "./renderEngine.js";
8
- import tls from 'tls';
9
8
  let config_ = {};
10
9
  let uptime = 0;
11
10
  let totalRequests = 0;
@@ -14,7 +13,6 @@ const jiti = createJiti(import.meta.url);
14
13
  const routes = new Map();
15
14
  const __filename = url.fileURLToPath(import.meta.url);
16
15
  const __dirname = path.dirname(__filename);
17
- // SSE clients waiting for hot reload signals
18
16
  const hmrClients = new Set();
19
17
  function notifyHmrClients() {
20
18
  for (const client of hmrClients) {
@@ -25,11 +23,7 @@ function notifyHmrClients() {
25
23
  hmrClients.delete(client);
26
24
  }
27
25
  }
28
- //console.log(`[authtics] hot reload triggered (${hmrClients.size} client(s))`);
29
26
  }
30
- // ---------------------------------------------------------------------------
31
- // MIME types for static files in public/
32
- // ---------------------------------------------------------------------------
33
27
  const MIME_TYPES = {
34
28
  '.html': 'text/html',
35
29
  '.css': 'text/css',
@@ -50,9 +44,6 @@ const MIME_TYPES = {
50
44
  '.txt': 'text/plain',
51
45
  '.pdf': 'application/pdf',
52
46
  };
53
- // ---------------------------------------------------------------------------
54
- // Augment ServerResponse with helper methods
55
- // ---------------------------------------------------------------------------
56
47
  function augmentRes(res) {
57
48
  const r = res;
58
49
  r.html = (html) => { res.setHeader("Content-Type", "text/html"); res.end(html); };
@@ -73,13 +64,11 @@ const getCpu = () => {
73
64
  };
74
65
  async function getJsonBody(req) {
75
66
  return new Promise((resolve, reject) => {
76
- // 1. Immediate exit for methods that shouldn't have a body
77
67
  const hasBody = req.headers['content-length'] || req.headers['transfer-encoding'];
78
68
  if (!hasBody && (req.method === 'GET' || req.method === 'DELETE')) {
79
69
  return resolve({});
80
70
  }
81
71
  const chunks = [];
82
- // 2. Safety Timeout: If it takes more than 5s, kill the request
83
72
  const timeout = setTimeout(() => {
84
73
  req.destroy();
85
74
  reject(new Error('Request Timeout: Body took too long to arrive'));
@@ -103,13 +92,29 @@ async function getJsonBody(req) {
103
92
  });
104
93
  });
105
94
  }
106
- // ---------------------------------------------------------------------------
107
- // App class
108
- // ---------------------------------------------------------------------------
109
95
  export default class App {
110
96
  server;
111
97
  constructor() {
112
98
  this.server = start();
99
+ this.post('/__authtics_host_apis__/get-dat-restriction-missing', async (req, res) => {
100
+ const reqBody = (await getJsonBody(req));
101
+ let datMatched = false;
102
+ let datRestrictedEnv = false;
103
+ if (config_.dat && config_.dat == reqBody.dat)
104
+ datMatched = true;
105
+ if (config_.dat == undefined)
106
+ datMatched = true;
107
+ if (config_.dat && Array.isArray(config_.dat) && config_.dat.includes(reqBody.dat))
108
+ datMatched = true;
109
+ if (datMatched) {
110
+ if (config_.dat !== undefined)
111
+ datRestrictedEnv = true;
112
+ res.writeHead(200, { "Content-Type": "application/json" });
113
+ res.end(JSON.stringify({
114
+ enabled: datRestrictedEnv
115
+ }));
116
+ }
117
+ });
113
118
  this.post('/__authtics_host_apis__/get-dat-matching', async (req, res) => {
114
119
  const reqBody = (await getJsonBody(req));
115
120
  let datMatched = false;
@@ -180,8 +185,6 @@ export default class App {
180
185
  let datMatched = false;
181
186
  if (config_.dat && config_.dat == reqBody.dat)
182
187
  datMatched = true;
183
- if (config_.dat == undefined)
184
- datMatched = true;
185
188
  if (config_.dat && Array.isArray(config_.dat) && config_.dat.includes(reqBody.dat))
186
189
  datMatched = true;
187
190
  if (datMatched) {
@@ -193,9 +196,7 @@ export default class App {
193
196
  stdio: 'inherit'
194
197
  });
195
198
  child.unref();
196
- //this.server.close(() => {
197
199
  process.kill(process.pid, 'SIGTERM');
198
- //})
199
200
  }
200
201
  });
201
202
  this.post("/__authtics_host_apis__/shutdown", async (req, res) => {
@@ -203,8 +204,6 @@ export default class App {
203
204
  let datMatched = false;
204
205
  if (config_.dat && config_.dat == reqBody.dat)
205
206
  datMatched = true;
206
- if (config_.dat == undefined)
207
- datMatched = true;
208
207
  if (config_.dat && Array.isArray(config_.dat) && config_.dat.includes(reqBody.dat))
209
208
  datMatched = true;
210
209
  if (datMatched) {
@@ -314,22 +313,10 @@ export default class App {
314
313
  if (config_.dat && Array.isArray(config_.dat) && config_.dat.includes(reqBody.dat))
315
314
  datMatched = true;
316
315
  if (datMatched) {
317
- if (req.socket instanceof tls.TLSSocket) {
318
- const protocol = req.socket.getProtocol();
319
- const cipher = req.socket.getCipher();
320
- res.writeHead(200, { "Content-Type": "application/json" });
321
- res.end(JSON.stringify({
322
- protocol,
323
- cipher: cipher.name,
324
- httpVersion: req.httpVersion
325
- }));
326
- }
327
- else {
328
- res.writeHead(200, { "Content-Type": "application/json" });
329
- res.end(JSON.stringify({
330
- httpVersion: req.httpVersion
331
- }));
332
- }
316
+ res.writeHead(200, { "Content-Type": "application/json" });
317
+ res.end(JSON.stringify({
318
+ httpVersion: req.httpVersion
319
+ }));
333
320
  }
334
321
  else {
335
322
  const { url = "/", method } = req;
@@ -373,11 +360,7 @@ export default class App {
373
360
  routes.set(`DELETE:${routePath}`, handler);
374
361
  }
375
362
  }
376
- // ---------------------------------------------------------------------------
377
- // Server
378
- // ---------------------------------------------------------------------------
379
363
  async function start() {
380
- //console.log("🛠️ Initializing Authtics Host...");
381
364
  let config = {};
382
365
  try {
383
366
  const configPath = path.join(process.cwd(), "authtics_host.config.ts");
@@ -385,7 +368,6 @@ async function start() {
385
368
  config_ = config;
386
369
  }
387
370
  catch {
388
- //console.log("ℹ️ No config found, using defaults.");
389
371
  }
390
372
  if (config.autoPage == null)
391
373
  config.autoPage = true;
@@ -401,21 +383,15 @@ async function start() {
401
383
  const publicDir = config.autoFile === true ? path.join(process.cwd(), config.autoFileDir || "") : undefined;
402
384
  const apiDir = pagesDir ? path.join(pagesDir, "api") : undefined;
403
385
  const server = createServer(async (req, res) => {
404
- //const body = await getJsonBody(req) as any;
405
- const actuallyShowPanel = showPanel; /* && (config.dat != undefined ? (body.dat == config.dat || (Array.isArray(config.dat) && config.dat.includes(body.dat))) : true)*/
386
+ const actuallyShowPanel = showPanel;
406
387
  const r = augmentRes(res);
407
388
  const { url = "/", method } = req;
408
- // Strip query string, hash, trailing slash
409
389
  let pathname = url.split("?")[0].split("#")[0];
410
390
  if (pathname !== "/" && pathname.endsWith("/"))
411
391
  pathname = pathname.slice(0, -1);
412
- /*if (pathname !== "/__authtics_host_apis__/ping" && pathname !== "/__authtics_host_apis__/get-connection" && pathname !== "/__authtics_hmr__" && pathname !== "/__authtics_host_apis__/get-file-by-pathname" && pathname !== "/__authtics_host_apis__/get-is-ssr" && pathname !== "/__authtics_host_apis__/get-compiler" && pathname !== "/__authtics_host_apis__/restart" && pathname !== "/__authtics_host_apis__/shutdown" && pathname !== "/__authtics_host_apis__/get-server-info" && pathname !== "/__authtics_host_apis__/get-cpu_memory") {
413
- totalRequests++;
414
- }*/
415
392
  if (!pathname.includes('/__authtics_host_apis__/')) {
416
393
  totalRequests++;
417
394
  }
418
- // ── 1. Registered routes (from app.get/post/etc.) ──────────────────────
419
395
  const routeKey = `${method}:${pathname}`;
420
396
  if (routes.has(routeKey)) {
421
397
  const handler = routes.get(routeKey);
@@ -426,8 +402,6 @@ async function start() {
426
402
  return handler(req, r);
427
403
  }
428
404
  }
429
- // ── 2. Hot reload SSE endpoint ─────────────────────────────────────────
430
- // Browser connects here and waits for a 'reload' event
431
405
  if (pathname === '/__authtics_hmr__') {
432
406
  res.writeHead(200, {
433
407
  'Content-Type': 'text/event-stream',
@@ -441,11 +415,8 @@ async function start() {
441
415
  req.on('close', () => { hmrClients.delete(res); users--; });
442
416
  return;
443
417
  }
444
- // ── 3. Public folder — serves files from /public ────────────────────────
445
- // e.g. GET /logo.png → public/logo.png
446
418
  if (publicDir && fs.existsSync(publicDir)) {
447
419
  const staticPath = path.join(publicDir, pathname);
448
- // Make sure the resolved path is still inside publicDir (prevent traversal)
449
420
  const safePath = path.resolve(staticPath);
450
421
  if (safePath.startsWith(path.resolve(publicDir)) && fs.existsSync(safePath) && fs.statSync(safePath).isFile()) {
451
422
  const ext = path.extname(safePath).toLowerCase();
@@ -454,15 +425,12 @@ async function start() {
454
425
  return fs.createReadStream(safePath).pipe(res);
455
426
  }
456
427
  }
457
- // ── 4. API routes — pages/api/*.ts files ────────────────────────────────
458
- // e.g. GET /api/users → pages/api/users.ts
459
428
  if (pathname.startsWith("/api/") && pagesDir != null) {
460
429
  const apiFile = path.join(pagesDir, `${pathname}.ts`);
461
430
  const apiFileTsx = path.join(pagesDir, `${pathname}.tsx`);
462
431
  const resolvedApi = fs.existsSync(apiFile) ? apiFile : fs.existsSync(apiFileTsx) ? apiFileTsx : null;
463
432
  if (resolvedApi) {
464
433
  try {
465
- // Use jiti to import the API route handler on the fly
466
434
  const apiModule = await jiti.import(resolvedApi, { default: true });
467
435
  const handler = apiModule.default ?? apiModule.handler ?? null;
468
436
  if (typeof handler !== 'function') {
@@ -477,11 +445,9 @@ async function start() {
477
445
  return res.end(JSON.stringify({ error: e.message }));
478
446
  }
479
447
  }
480
- // /api/ path but no matching file → 404
481
448
  res.writeHead(404);
482
449
  return res.end(JSON.stringify({ error: `API route not found: ${pathname}` }));
483
450
  }
484
- // ── 5. TSX pages (SSR) ──────────────────────────────────────────────────
485
451
  const urlPath = pathname === "/" ? "/index" : pathname;
486
452
  const tsxPath = pagesDir ? path.join(pagesDir, `${urlPath}.tsx`) : undefined;
487
453
  const htmlPath = pagesDir ? path.join(pagesDir, `${urlPath}.html`) : undefined;
@@ -497,12 +463,10 @@ async function start() {
497
463
  return res.end(`<html><body><pre>500 | Render error:\n${e.message}</pre></body></html>`);
498
464
  }
499
465
  }
500
- // ── 6. Static HTML pages ────────────────────────────────────────────────
501
466
  if (htmlPath && fs.existsSync(htmlPath)) {
502
467
  res.setHeader("Content-Type", "text/html");
503
468
  return res.end(fs.readFileSync(htmlPath, "utf-8"));
504
469
  }
505
- // ── 7. App.tsx file handling ──────────────────────────────────────────────────────────────
506
470
  if (routes.has("APP_TSX")) {
507
471
  const appTsxPath = routes.get("APP_TSX");
508
472
  const appPath = path.join(process.cwd(), appTsxPath);
@@ -519,7 +483,6 @@ async function start() {
519
483
  }
520
484
  }
521
485
  }
522
- // ── 8. 404 ──────────────────────────────────────────────────────────────
523
486
  const notFoundPath = path.join(__dirname, '404.js');
524
487
  if (fs.existsSync(notFoundPath)) {
525
488
  try {
@@ -540,15 +503,12 @@ async function start() {
540
503
  }
541
504
  });
542
505
  server.listen(PORT, () => {
543
- //console.log(`🚀 Authtics Host live at http://localhost:${PORT}`);
544
506
  setInterval(() => {
545
507
  uptime++;
546
508
  }, 1000);
547
509
  });
548
- // ── Hot reload file watcher ─────────────────────────────────────────────
549
510
  if (config.hotReload === true) {
550
511
  const watchDirs = [pagesDir];
551
- // Also watch src/devPanel.tsx if it exists
552
512
  const devPanelDir = path.join(__dirname, 'devPanel.js');
553
513
  const _404Dir = path.join(__dirname, '404.js');
554
514
  if (fs.existsSync(devPanelDir))
@@ -560,8 +520,6 @@ async function start() {
560
520
  fs.watch(dir, { recursive: true }, (eventType, filename) => {
561
521
  if (!filename)
562
522
  return;
563
- //console.log(`[authtics] file changed: ${filename}`);
564
- // Clear the cache for any .tsx/.ts/.html file change
565
523
  notifyHmrClients();
566
524
  });
567
525
  }
@@ -575,13 +533,10 @@ async function start() {
575
533
  fs.watch(dir, { recursive: true }, (eventType, filename) => {
576
534
  if (!filename)
577
535
  return;
578
- //console.log(`[authtics] file changed: ${filename}`);
579
- // Clear the cache for any .tsx/.ts/.html file change
580
536
  notifyHmrClients();
581
537
  });
582
538
  }
583
539
  }
584
- //console.log('🔥 Hot reload enabled — watching for file changes...');
585
540
  }
586
541
  return server;
587
542
  }
package/dist/devPanel.js CHANGED
@@ -1,31 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import React, { useState, useEffect, useRef } from 'react';
3
3
  import { ShieldCheck, CircleX, ChevronDown, ChevronUp, Bug, AlertTriangle, Info, ArrowLeft, Wifi, Terminal, Route, Globe, ChevronRight, ChevronLeft, Server } from 'lucide-react';
4
- //import { isMobile } from 'react-device-detect';
5
4
  import Prism from 'prismjs';
6
5
  import 'prismjs/components/prism-javascript';
7
6
  Prism.languages.javascript['builtin'] = /\b(window|document|console|navigator|localStorage|sessionStorage|fetch|Promise|setTimeout|setInterval|clearTimeout|clearInterval|requestAnimationFrame|Math|JSON|Object|Array|String|Number|Boolean|Date|RegExp|Error|Map|Set|Symbol|Proxy|Reflect|performance|history|location|screen|crypto|indexedDB|alert|confirm|prompt)\b/;
8
- //const isMobile = true;
9
7
  function getCallerSource() {
10
8
  try {
11
9
  const stack = new Error().stack ?? '';
12
10
  const lines = stack.split('\n');
13
- // Skip frames: Error, getCallerSource, the patched console[type], and installConsoleInterceptor
14
11
  for (let i = 3; i < lines.length; i++) {
15
12
  const line = lines[i];
16
- // Skip internals and DevPanel's own frames
17
- if (
18
- /*line.includes('installConsoleInterceptor') ||
19
- line.includes('devPanel') ||
20
- line.includes('<anonymous>') ||*/
21
- line.trim() === '')
13
+ if (line.trim() === '')
22
14
  continue;
23
- // Extract filename:line from something like "at foo (http://localhost:5000/pages/index.tsx:42:10)"
24
15
  const match = line.match(/\(?(https?:\/\/[^)]+)\)?$/) ??
25
16
  line.match(/at\s+(https?:\/\/\S+)/);
26
17
  if (match) {
27
18
  const url = match[1];
28
- // Turn full URL into just "filename:line"
29
19
  const parts = url.replace(/\?.*/, '').split('/');
30
20
  const file = parts[parts.length - 1];
31
21
  const colonLine = url.match(/:(\d+):\d+$/);
@@ -33,17 +23,15 @@ function getCallerSource() {
33
23
  }
34
24
  }
35
25
  }
36
- catch { /* ignore */ }
26
+ catch { }
37
27
  return '';
38
28
  }
39
29
  const LOG_TYPES = ['Log', 'Info', 'Warn', 'Error', 'Debug'];
40
- const LOG_TYPE_2 = ['Eval'];
41
30
  let fetchId = 0;
42
31
  const TYPE_STYLE = {
43
32
  Error: { bg: '#fff0f0', color: '#e53e3e', icon: _jsx(CircleX, { size: 11 }) },
44
33
  Warn: { bg: '#fffbea', color: '#d97706', icon: _jsx(AlertTriangle, { size: 11 }) },
45
34
  Log: { bg: '#ffffff', color: '#888', textColor: '#333', icon: null },
46
- Eval: { bg: '#ffffff', color: '#888', textColor: '#333', icon: null },
47
35
  Info: { bg: '#eff6ff', color: '#3b82f6', icon: _jsx(Info, { size: 11 }) },
48
36
  Debug: { bg: '#f0fdf4', color: '#16a34a', icon: _jsx(Bug, { size: 11 }) },
49
37
  };
@@ -51,7 +39,6 @@ const FILTER_COLORS = {
51
39
  Error: '#e53e3e',
52
40
  Warn: '#d97706',
53
41
  Log: '#888',
54
- Eval: '#888',
55
42
  Info: '#3b82f6',
56
43
  Debug: '#16a34a',
57
44
  };
@@ -62,7 +49,6 @@ const TruncatedText = ({ content, id }) => {
62
49
  : content;
63
50
  return _jsx("div", { id: id, children: truncated });
64
51
  };
65
- // ── Sparkline chart (SVG, no dependencies) ──────────────────────────────────
66
52
  function Sparkline({ data, color, label, unit }) {
67
53
  const W = 280, H = 48;
68
54
  const max = Math.max(...data, 1);
@@ -87,14 +73,11 @@ function installNetworkInterceptor(onFetch, setFetch, onBytesClient, onBytesServ
87
73
  const savedFetchId = fetchId;
88
74
  const url = typeof args[0] === 'string' ? args[0] : args[0].url;
89
75
  const method = (args[1]?.method ?? 'GET').toUpperCase();
90
- // Filter out DevPanel's own internal pings so they don't show up in the network tab
91
76
  if (url.startsWith('/__authtics_host_apis__/') || url === "/__authtics_hmr__") {
92
77
  return originalFetch(...args);
93
78
  }
94
- // Estimate bytes sent: body size + rough header/URL overhead
95
79
  const body = args[1]?.body;
96
80
  const bodyBytes = body ? new Blob([body]).size : 0;
97
- // Headers + URL as a rough proxy for request overhead (like Chrome devtools does)
98
81
  const headerStr = JSON.stringify(args[1]?.headers ?? {});
99
82
  const sentBytes = bodyBytes + url.length + headerStr.length;
100
83
  onBytesClient(sentBytes);
@@ -138,14 +121,14 @@ function installConsoleInterceptor() {
138
121
  };
139
122
  });
140
123
  }
141
- function PanelButton({ onClick, onClick2, children, style, fullWidth }) {
124
+ function PanelButton({ onClick, onClick2, children, style, fullWidth, disabled }) {
142
125
  const [hovered, setHovered] = useState(false);
143
126
  const [pressed, setPressed] = useState(false);
144
127
  return (_jsx("div", { onClick: (e) => {
145
- if (onClick) {
128
+ if (onClick && !disabled) {
146
129
  onClick();
147
130
  }
148
- else if (onClick2) {
131
+ else if (onClick2 && !disabled) {
149
132
  onClick2(e);
150
133
  }
151
134
  }, onMouseEnter: () => setHovered(true), onMouseLeave: () => { setHovered(false); setPressed(false); }, onMouseDown: () => setPressed(true), onMouseUp: () => setPressed(false), style: {
@@ -164,7 +147,7 @@ function PanelButton({ onClick, onClick2, children, style, fullWidth }) {
164
147
  ...style,
165
148
  }, children: children }));
166
149
  }
167
- const MAX_POINTS = 20; // number of data points shown in charts
150
+ const MAX_POINTS = 20;
168
151
  const generateToken = () => {
169
152
  return crypto.randomUUID().replace(/-/g, '').substring(0, 16);
170
153
  };
@@ -213,6 +196,7 @@ export default function DevPanel() {
213
196
  const frames = useRef(0);
214
197
  const consoleScrollRef = useRef(null);
215
198
  const [isAtBottom, setIsAtBottom] = useState(true);
199
+ const [datRestrictionMissing, setDatRestrictionMissing] = useState(true);
216
200
  const [pos, setPos] = React.useState({ x: 20, y: 20 });
217
201
  const [dragging, setDragging] = React.useState(false);
218
202
  const offset = React.useRef({ x: 0, y: 0 });
@@ -220,7 +204,7 @@ export default function DevPanel() {
220
204
  const [rootEl, setRootEl] = useState(null);
221
205
  const [doc, setDoc] = useState(null);
222
206
  const [filters, setFilters] = useState({
223
- Log: true, Info: true, Warn: true, Error: true, Debug: false, Eval: true
207
+ Log: true, Info: true, Warn: true, Error: true, Debug: false
224
208
  });
225
209
  const errorCount = logs.filter(l => l.type === 'Error').length;
226
210
  const warnCount = logs.filter(l => l.type === 'Warn').length;
@@ -228,7 +212,6 @@ export default function DevPanel() {
228
212
  const handleScroll = () => {
229
213
  if (consoleScrollRef.current) {
230
214
  const { scrollTop, scrollHeight, clientHeight } = consoleScrollRef.current;
231
- // Buffer of 5px to handle zoom or rounding issues
232
215
  const reachedBottom = scrollHeight - scrollTop - clientHeight < 5;
233
216
  setIsAtBottom(reachedBottom);
234
217
  }
@@ -238,230 +221,6 @@ export default function DevPanel() {
238
221
  consoleScrollRef.current.scrollTop = consoleScrollRef.current.scrollHeight;
239
222
  }
240
223
  }, [filteredLogs, isAtBottom]);
241
- /*useEffect(() => {
242
- setRootEl(doc?.documentElement as HTMLElement);
243
- }, []);
244
- const [picking, setPicking] = React.useState(false);*/
245
- /*const onMouseDown = (e: React.MouseEvent) => {
246
- dragging || setDragging(true);
247
- offset.current = { x: e.clientX - pos.x, y: e.clientY - pos.y };
248
- e.preventDefault();
249
- };
250
-
251
- const onTouchStart = (e: React.TouchEvent) => {
252
- const t = e.touches[0];
253
- offset.current = { x: t.clientX - pos.x, y: t.clientY - pos.y };
254
- };
255
-
256
- const [rect, setRect] = React.useState<DOMRect | null>(null);
257
-
258
- const [expandedPaths, setExpandedPaths] = useState<Set<string>>(new Set());
259
- const [domVersion, setDomVersion] = useState(0);
260
-
261
- const [currentNode, setCurrentNode] = useState<Element>(doc?.documentElement as HTMLElement);
262
- const [history, setHistory] = useState<Element[]>([]);
263
-
264
- const goInto = (el: Element) => {
265
- setHistory(prev => [...prev, currentNode]);
266
- setCurrentNode(el);
267
- };
268
-
269
- const goBack = () => {
270
- setHistory(prev => {
271
- const next = [...prev];
272
- setCurrentNode(next.pop()!);
273
- return next;
274
- });
275
- };
276
-
277
- useEffect(() => {
278
- const observer = new MutationObserver(() => setDomVersion(v => v + 1));
279
- observer.observe(doc?.documentElement as HTMLElement, { childList: true, subtree: true, attributes: true });
280
- return () => observer.disconnect();
281
- }, []);
282
-
283
- const getPath = (el: Element, depth: number) => el.tagName + depth + el.className + el.id;
284
-
285
- const toggle = (path: string) => {
286
- setExpandedPaths(prev => {
287
- const next = new Set(prev);
288
- next.has(path) ? next.delete(path) : next.add(path);
289
- return next;
290
- });
291
- };
292
-
293
- const colorizeJS = (code: string): string => {
294
- const escaped = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
295
- return escaped
296
- .replace(/(\/\/[^\n]*)/g, '<span style="color:#888">$1</span>')
297
- .replace(/(".*?"|'.*?')/g, '<span style="color:#0a0">$1</span>')
298
- .replace(/\b(const|let|var|function|return|if|else|for|while|import|export|default|class|new)\b/g, '<span style="color:#e05">$1</span>')
299
- .replace(/\b(\d+)\b/g, '<span style="color:#f90">$1</span>');
300
- };
301
-
302
- const colorizeCSS = (code: string): string => {
303
- const escaped = code.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
304
- return escaped
305
- .replace(/([\w-]+)(\s*:)/g, '<span style="color:#f90">$1</span>$2')
306
- .replace(/([.#]?[\w-]+)\s*\{/g, '<span style="color:#e05">$1</span> {');
307
- };
308
-
309
- const colorizeHtml = (code: string): React.ReactElement => {
310
- const tokens: React.ReactElement[] = [];
311
- let rest = code;
312
- let i = 0;
313
-
314
- const tagRegex = /(<\/?)([a-zA-Z][a-zA-Z0-9]*)([^>]*)(>)/g;
315
- let last = 0;
316
- let match;
317
-
318
- while ((match = tagRegex.exec(code)) !== null) {
319
- if (match.index > last)
320
- tokens.push(<span key={i++} style={{ color: '#555' }}>{code.slice(last, match.index)}</span>);
321
- tokens.push(<span key={i++} style={{ color: '#666' }}>{match[1]}</span>);
322
- tokens.push(<span key={i++} style={{ color: '#e05' }}>{match[2]}</span>);
323
- // colorize attributes
324
- const attrStr = match[3];
325
- const attrRegex = /(\s[a-zA-Z-]+)(="([^"]*)")?/g;
326
- let am;
327
- while ((am = attrRegex.exec(attrStr)) !== null) {
328
- tokens.push(<span key={i++} style={{ color: '#f90' }}>{am[1]}</span>);
329
- if (am[2]) {
330
- tokens.push(<span key={i++} style={{ color: '#999' }}>="</span>);
331
- tokens.push(<span key={i++} style={{ color: '#0a0' }}>{am[3]}</span>);
332
- tokens.push(<span key={i++} style={{ color: '#999' }}>"</span>);
333
- }
334
- }
335
- tokens.push(<span key={i++} style={{ color: '#666' }}>{match[4]}</span>);
336
- last = match.index + match[0].length;
337
- }
338
-
339
- if (last < code.length)
340
- tokens.push(<span key={i++} style={{ color: '#555' }}>{code.slice(last)}</span>);
341
-
342
- return <>{tokens}</>;
343
- };
344
-
345
- const renderNode = (el: Element, depth = 0, path = el.tagName): React.ReactElement => {
346
- const isSelected = selected === el;
347
- const tag = el.tagName.toLowerCase();
348
- ;const idAttr = el.id ? el.id : null;
349
- const classAttr = el.className ? String(el.className).slice(0, 15) : null;
350
- const otherAttrs = Array.from(el.attributes)
351
- .filter(a => a.name !== 'id' && a.name !== 'class')
352
- .slice(0, 2);
353
- const text = Array.from(el.childNodes)
354
- .filter(n => n.nodeType === Node.TEXT_NODE)
355
- .map(n => n.textContent?.trim())
356
- .filter(Boolean)
357
- .join(' ');
358
-
359
- const hasChildren = el.children.length > 0;
360
- const hasText = text.length > 0;
361
- const expandable = hasChildren || hasText;
362
- const isExpanded = expandedPaths.has(path);*/
363
- /*return (
364
- <div key={path}>
365
- {*/ /* opening tag */ /*}
366
- <div
367
- onClick={() => { setSelected(el); setRect(el.getBoundingClientRect()); if (expandable) toggle(path); }}
368
- style={{ paddingLeft: depth * 10 + 4, cursor: 'pointer', background: isSelected ? '#e0edff' : 'transparent', borderRadius: 4, fontSize: 11, fontFamily: 'monospace', whiteSpace: 'nowrap' }}
369
- >
370
- <span style={{ color: '#999' }}>{expandable ? (isExpanded ? '▾' : '▸') : '·'} </span>
371
- <span style={{ color: '#666' }}>&lt;</span>
372
- <span style={{ color: '#e05' }}>{tag}</span>
373
- {idAttr && <><span style={{ color: '#f90' }}> id</span><span style={{ color: '#999' }}>="</span><span style={{ color: '#0a0' }}>{idAttr}</span><span style={{ color: '#999' }}>"</span></>}
374
- {classAttr && <><span style={{ color: '#f90' }}> class</span><span style={{ color: '#999' }}>="</span><span style={{ color: '#0a0' }}>{classAttr}</span><span style={{ color: '#999' }}>"</span></>}
375
- {otherAttrs.map(a => (
376
- <span key={a.name}>
377
- <span style={{ color: '#f90' }}> {a.name}</span>
378
- <span style={{ color: '#999' }}>="</span>
379
- <span style={{ color: '#0a0' }}>{a.value.slice(0, 20)}</span>
380
- <span style={{ color: '#999' }}>"</span>
381
- </span>
382
- ))}
383
- <span style={{ color: '#666' }}>&gt;</span>
384
- {!isExpanded && hasText && <span style={{ color: '#333' }}>{text.slice(0, 30)}{text.length > 30 ? '...' : ''}</span>}
385
- {!isExpanded && <span style={{ color: '#e05' }}>&lt;/{tag}&gt;</span>}
386
- </div>
387
-
388
- {/* expanded content */ /*}
389
- {isExpanded && (
390
- <>
391
- {hasText && (
392
- <div
393
- style={{ paddingLeft: (depth + 2) * 10 + 4, fontSize: 11, fontFamily: 'monospace', whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}
394
- dangerouslySetInnerHTML={{
395
- __html: tag === 'script'
396
- ? colorizeJS(text)
397
- : tag === 'style'
398
- ? colorizeCSS(text)
399
- : `"${text}"`
400
- }}
401
- />
402
- )}
403
- {hasChildren && Array.from(el.children).map((child, i) =>
404
- renderNode(child, depth + 1, path + '>' + child.tagName + i)
405
- )}
406
- {/* closing tag only shows separately when expanded */ /*}
407
- <div style={{ paddingLeft: depth * 10 + 4, fontSize: 11, fontFamily: 'monospace', color: '#e05' }}>
408
- <span style={{ color: '#666' }}>&lt;/</span>
409
- <span style={{ color: '#e05' }}>{tag}</span>
410
- <span style={{ color: '#666' }}>&gt;</span>
411
- </div>
412
- </>
413
- )}
414
- </div>
415
- );
416
- };
417
-
418
- useEffect(() => {
419
- if (!picking) return;
420
- const onOver = (e: MouseEvent) => (e.target as HTMLElement).style.outline = '2px solid #e05';
421
- const onOut = (e: MouseEvent) => (e.target as HTMLElement).style.outline = '';
422
- const onClick = (e: MouseEvent) => {
423
- e.preventDefault();
424
- e.stopPropagation();
425
- const el = e.target as HTMLElement;
426
- el.style.outline = '';
427
- setSelected(el);
428
- setRect(el.getBoundingClientRect());
429
- setPicking(false);
430
- };
431
- document.addEventListener('mouseover', onOver);
432
- document.addEventListener('mouseout', onOut);
433
- document.addEventListener('click', onClick, true);
434
- return () => {
435
- document.removeEventListener('mouseover', onOver);
436
- document.removeEventListener('mouseout', onOut);
437
- document.removeEventListener('click', onClick, true);
438
- };
439
- }, [picking]);
440
-
441
- const styles = selected ? window.getComputedStyle(selected) : null;
442
- const isFlex = styles?.display === 'flex';
443
- const isGrid = styles?.display === 'grid';
444
- const rotation = styles?.transform !== 'none' ? styles?.transform : null;
445
- const styleKeys = ['color','background','fontSize','margin','padding','display','width','height','position','borderRadius'];
446
-
447
- useEffect(() => {
448
- const onMove = (e: MouseEvent | TouchEvent) => {
449
- if (!dragging) return;
450
- const point = 'touches' in e ? e.touches[0] : e;
451
- setPos({ x: point.clientX - offset.current.x, y: point.clientY - offset.current.y });
452
- };
453
- const onUp = () => setDragging(false);
454
- window.addEventListener('mousemove', onMove as any);
455
- window.addEventListener('mouseup', onUp);
456
- window.addEventListener('touchmove', onMove as any);
457
- window.addEventListener('touchend', onUp);
458
- return () => {
459
- window.removeEventListener('mousemove', onMove as any);
460
- window.removeEventListener('mouseup', onUp);
461
- window.removeEventListener('touchmove', onMove as any);
462
- window.removeEventListener('touchend', onUp);
463
- };
464
- }, [dragging]);*/
465
224
  const loop = () => {
466
225
  frameCount.current++;
467
226
  requestAnimationFrame(loop);
@@ -489,6 +248,16 @@ useEffect(() => {
489
248
  setDatMatch(data.isMatching);
490
249
  if (!data.isMatching) {
491
250
  doc?.getElementById('authtics-dev-panel-root')?.remove();
251
+ return;
252
+ }
253
+ const resp2 = await fetch('/__authtics_host_apis__/get-dat-restriction-missing', {
254
+ method: 'POST',
255
+ headers: { 'Content-Type': 'application/json' },
256
+ body: JSON.stringify({ dat: localStorage.getItem('dat') })
257
+ });
258
+ if (resp2.status === 200) {
259
+ const data = await resp2.json();
260
+ setDatRestrictionMissing(!data.enabled);
492
261
  }
493
262
  }
494
263
  };
@@ -595,18 +364,6 @@ useEffect(() => {
595
364
  update();
596
365
  }, 500);
597
366
  }, 250);
598
- /*setInterval(() => {
599
- //setFps(Math.round(1000 / (performance.now() - (window as any).lastFrameTime)));
600
- const now = performance.now();
601
- const last = (window as any).lastFrameTime;
602
- if (typeof last === 'number') {
603
- const _ = now - last;
604
- if (fpsSpanRef.current) {
605
- fpsSpanRef.current.textContent = _ > 0 ? Math.round(1000 / _).toString() : '0';
606
- }
607
- }
608
- (window as any).lastFrameTime = now;
609
- }, 1000)*/
610
367
  }, []);
611
368
  const restart = async () => {
612
369
  const resp = await fetch('/__authtics_host_apis__/restart', {
@@ -636,7 +393,6 @@ useEffect(() => {
636
393
  clearInterval(intervalId);
637
394
  };
638
395
  }, []);
639
- // Accumulated bytes per 2s tick
640
396
  const pendingSent = useRef(0);
641
397
  const pendingReceived = useRef(0);
642
398
  const [sentHistory, setSentHistory] = useState(Array(MAX_POINTS).fill(0));
@@ -648,7 +404,6 @@ useEffect(() => {
648
404
  : consoleOpen || networkOpen || routeOpen || webpageInfoOpen || clientInfoOpen || aboutHostOpen
649
405
  ? '320px'
650
406
  : '320px';
651
- // Ping every second
652
407
  useEffect(() => {
653
408
  const dat = localStorage.getItem('dat');
654
409
  const interval = setInterval(async () => {
@@ -665,23 +420,11 @@ useEffect(() => {
665
420
  setPing__(Date.now() - timeStart);
666
421
  }
667
422
  }
668
- catch { /* server not reachable */ }
423
+ catch { }
669
424
  }, 1000);
670
425
  return () => clearInterval(interval);
671
426
  }, []);
672
- // Chart tick every 2 seconds — snapshot pending bytes then reset
673
427
  useEffect(() => {
674
- /*let nE;
675
- const navigationEntries = performance.getEntriesByType("navigation");
676
- if (navigationEntries.length > 0) {
677
- const navigationEntry = navigationEntries[0] as PerformanceNavigationTiming;
678
- nE = navigationEntry;
679
- }
680
- let statusCode = 404;
681
- if (nE instanceof PerformanceNavigationTiming) {
682
- statusCode = nE.responseStatus;
683
- }
684
- setStatus(statusCode);*/
685
428
  const interval = setInterval(() => {
686
429
  const s = pendingSent.current;
687
430
  const r = pendingReceived.current;
@@ -698,7 +441,6 @@ useEffect(() => {
698
441
  }, 1000);
699
442
  return () => clearInterval(interval);
700
443
  }, []);
701
- // Console + network interceptors
702
444
  useEffect(() => {
703
445
  installConsoleInterceptor();
704
446
  installNetworkInterceptor((f) => setNetworkFetches(prev => [...prev.slice(-49), f]), (f) => setNetworkFetches(prev => prev.map(p => p.id === f.id ? f : p)), (sent) => {
@@ -749,13 +491,6 @@ useEffect(() => {
749
491
  setHostName(window.location.hostname);
750
492
  setDoc(document);
751
493
  setProtocol(window.location.protocol.replace(':', '').toUpperCase());
752
- // Test logs
753
- //setTimeout(() => console.error('DevPanel rendered.'), 2000);
754
- //setTimeout(() => console.error('DevPanel rendered. 2'), 2000);
755
- /*setTimeout(() => console.debug('DevPanel rendered.'), 2000);
756
- setTimeout(() => console.info('DevPanel rendered.'), 2000);
757
- setTimeout(() => console.log('DevPanel rendered.'), 2000);
758
- setTimeout(() => console.warn('DevPanel rendered.'), 2000);*/
759
494
  return () => window.removeEventListener('authtics:log', handler);
760
495
  }, []);
761
496
  const getOS = () => {
@@ -941,7 +676,7 @@ useEffect(() => {
941
676
  userSelect: 'none',
942
677
  }, children: [_jsx("input", { type: "checkbox", checked: filters[type], onChange: () => toggleFilter(type), style: { display: 'none' } }), type] }, type))) }), _jsx("div", { style: { overflowY: 'auto', maxHeight: '220px' }, ref: consoleScrollRef, onScroll: handleScroll, children: filteredLogs.length === 0
943
678
  ? _jsx("div", { style: { padding: '16px 12px', color: '#aaa', fontSize: '12px' }, children: "No logs yet." })
944
- : [...filteredLogs] /*.reverse()*/.map(log => {
679
+ : [...filteredLogs].map(log => {
945
680
  const s = TYPE_STYLE[log.type];
946
681
  return (_jsxs("div", { style: {
947
682
  display: 'flex',
@@ -955,12 +690,7 @@ useEffect(() => {
955
690
  fontSize: '11px',
956
691
  fontFamily: 'monospace',
957
692
  wordBreak: 'break-all',
958
- }, children: log.type === "Eval" ? (_jsxs("div", { style: { display: 'flex', flexDirection: 'row', gap: '4px', justifyContent: 'center', alignItems: 'center' }, children: ["(You): ", _jsx("div", { style: {
959
- fontFamily: 'monospace',
960
- fontSize: '12px',
961
- whiteSpace: 'pre-wrap',
962
- wordBreak: 'break-all',
963
- }, dangerouslySetInnerHTML: { __html: Prism.highlight(log.message, Prism.languages.javascript, 'javascript') } })] })) : log.message })] }, log.id));
693
+ }, children: log.message })] }, log.id));
964
694
  }) })] })) : networkOpen ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
965
695
  padding: '6px 8px',
966
696
  borderBottom: '1px solid #ddd',
@@ -1215,7 +945,17 @@ useEffect(() => {
1215
945
  gap: '6px',
1216
946
  color: '#fff',
1217
947
  flexDirection: 'row',
1218
- }, children: [_jsx(CircleX, { size: 12 }), "Invalid Developer Access Token (DAT)"] }) })) : (_jsx(_Fragment, {})), _jsx(PanelButton, { onClick: () => { restart(); }, fullWidth: true, style: { color: '#fff', justifyContent: 'center', height: '32px', background: '#ba4e4e' }, children: _jsx("span", { style: { display: 'flex', alignItems: 'center', gap: '6px', textAlign: 'center' }, children: "Restart App" }) }), _jsx(PanelButton, { onClick: () => { shutDown(); }, fullWidth: true, style: { color: '#fff', justifyContent: 'center', height: '32px', background: '#ba4e4e' }, children: _jsx("span", { style: { display: 'flex', alignItems: 'center', gap: '6px', textAlign: 'center' }, children: "Shut Down App" }) })] })] })) }) }), datMatch ? (_jsxs("div", { onClick: () => setOpen(o => !o), style: {
948
+ }, children: [_jsx(CircleX, { size: 12 }), "Invalid Developer Access Token (DAT)"] }) })) : (_jsx(_Fragment, {})), hostVersion === "0.0.1-beta.9" ? (_jsx(_Fragment, { children: _jsx("div", { style: {
949
+ display: 'flex',
950
+ background: 'orange',
951
+ borderRadius: '10px',
952
+ padding: '6px 10px',
953
+ justifyContent: 'left',
954
+ alignItems: 'center',
955
+ gap: '6px',
956
+ color: '#fff',
957
+ flexDirection: 'row',
958
+ }, children: "Updates to security. Apps with version 0.0.1-beta.9 and over will now require DAT restriction to access remote features like restarting and shutting down the app." }) })) : (_jsx(_Fragment, {})), _jsx(PanelButton, { onClick: () => { restart(); }, fullWidth: true, style: { color: '#fff', justifyContent: 'center', height: '32px', background: '#ba4e4e' }, disabled: datRestrictionMissing, children: _jsx("span", { style: { display: 'flex', alignItems: 'center', gap: '6px', textAlign: 'center' }, children: "Restart App" }) }), datRestrictionMissing ? (_jsxs("span", { style: { display: 'flex', opacity: 0.5, fontSize: 10, justifyContent: 'center', alignItems: 'center', gap: '4px' }, children: [_jsx(Info, { size: 10 }), "Restarting the app is unavaliable. Set \"dat\" in the Authtics Host config to set restriction."] })) : (_jsx(_Fragment, {})), _jsx(PanelButton, { onClick: () => { shutDown(); }, fullWidth: true, style: { color: '#fff', justifyContent: 'center', height: '32px', background: '#ba4e4e' }, disabled: datRestrictionMissing, children: _jsx("span", { style: { display: 'flex', alignItems: 'center', gap: '6px', textAlign: 'center' }, children: "Shut Down App" }) }), datRestrictionMissing ? (_jsxs("span", { style: { display: 'flex', opacity: 0.5, fontSize: 10, justifyContent: 'center', alignItems: 'center', gap: '4px' }, children: [_jsx(Info, { size: 10 }), "Shutting down the app is unavaliable. Set \"dat\" in the Authtics Host config to set restriction."] })) : (_jsx(_Fragment, {}))] })] })) }) }), datMatch ? (_jsxs("div", { onClick: () => setOpen(o => !o), style: {
1219
959
  display: 'flex',
1220
960
  flexDirection: 'row',
1221
961
  alignItems: 'center',
@@ -1,4 +1,3 @@
1
- // src/renderEngine.ts
2
1
  import path from "node:path";
3
2
  import fs from "node:fs";
4
3
  import { fileURLToPath } from "node:url";
@@ -9,12 +8,7 @@ import { renderToString } from "react-dom/server";
9
8
  import { tempFileForTsx, writeTempFile, readTempFile, ensureTempDirs, } from "./rendererTempFiles.js";
10
9
  const __filename = fileURLToPath(import.meta.url);
11
10
  const __dirname = path.dirname(__filename);
12
- //const __dirname = fileURLToPath(new URL(".", import.meta.url));
13
11
  const require = createRequire(import.meta.url);
14
- // ---------------------------------------------------------------------------
15
- // Babel compiler (optional — only used if compilerType: 'babel' in config)
16
- // Lazily required so babel doesn't need to be installed for esbuild users.
17
- // ---------------------------------------------------------------------------
18
12
  async function buildWithBabel(entryPath, virtualContent, platform) {
19
13
  let babel;
20
14
  try {
@@ -41,9 +35,6 @@ async function buildWithBabel(entryPath, virtualContent, platform) {
41
35
  throw new Error('[authtics] Babel produced no output');
42
36
  return result.code;
43
37
  }
44
- // ---------------------------------------------------------------------------
45
- // esbuild compiler (default)
46
- // ---------------------------------------------------------------------------
47
38
  async function buildWithEsbuild(virtualContent, resolveDir, platform) {
48
39
  const isNode = platform === 'node';
49
40
  const result = await esbuild.build({
@@ -54,8 +45,6 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform) {
54
45
  },
55
46
  bundle: true,
56
47
  write: false,
57
- // Server: CJS so require() works (e.g. lucide-react)
58
- // Client: IIFE so it runs as a plain <script> tag
59
48
  format: isNode ? 'cjs' : 'iife',
60
49
  platform,
61
50
  jsx: 'automatic',
@@ -63,8 +52,6 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform) {
63
52
  target: isNode ? ['node18'] : ['es2020'],
64
53
  minify: false,
65
54
  treeShaking: true,
66
- // Server: externalize React to avoid duplicate instance → "Invalid hook call"
67
- // Client: bundle React in so the browser needs no CDN or importmap
68
55
  external: isNode
69
56
  ? ['react', 'react-dom', 'react-dom/server', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'node:http',
70
57
  'node:path',
@@ -78,9 +65,6 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform) {
78
65
  });
79
66
  return result.outputFiles[0].text;
80
67
  }
81
- // ---------------------------------------------------------------------------
82
- // Resolve devPanel.tsx
83
- // ---------------------------------------------------------------------------
84
68
  export function findFileUpwards(startDir, relativePath) {
85
69
  let dir = path.resolve(startDir);
86
70
  const root = path.parse(dir).root;
@@ -95,19 +79,12 @@ export function findFileUpwards(startDir, relativePath) {
95
79
  return null;
96
80
  }
97
81
  function resolveDevPanelPath() {
98
- /*const fromCwd = findFileUpwards(process.cwd(), path.join("src", "devPanel.tsx"));
99
- if (fromCwd) return fromCwd;
100
- const fromParent = findFileUpwards(path.join(process.cwd(), ".."), path.join("src", "devPanel.tsx"));
101
- if (fromParent) return fromParent;*/
102
82
  const nextToCli = path.join(__dirname, "devPanel.js");
103
83
  if (fs.existsSync(nextToCli))
104
84
  return nextToCli;
105
85
  return null;
106
86
  }
107
87
  const devPanelPath = resolveDevPanelPath();
108
- // ---------------------------------------------------------------------------
109
- // Template resolution
110
- // ---------------------------------------------------------------------------
111
88
  function resolveTemplate(tsxPath) {
112
89
  const pagesDir = path.join(process.cwd(), "pages");
113
90
  let dir = path.dirname(tsxPath);
@@ -154,21 +131,15 @@ function buildHtml(opts) {
154
131
  </body>
155
132
  </html>`;
156
133
  }
157
- // ---------------------------------------------------------------------------
158
- // Cache validity
159
- // ---------------------------------------------------------------------------
160
134
  function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath) {
161
135
  if (!fs.existsSync(tempPath) || !fs.existsSync(serverCjsPath))
162
136
  return false;
163
137
  try {
164
138
  const cacheMtime = fs.statSync(tempPath).mtimeMs;
165
- // Page file changed?
166
139
  if (fs.statSync(tsxPath).mtimeMs > cacheMtime)
167
140
  return false;
168
- // Template changed?
169
141
  if (templatePath && fs.existsSync(templatePath) && fs.statSync(templatePath).mtimeMs > cacheMtime)
170
142
  return false;
171
- // DevPanel changed?
172
143
  if (devPanelPath && fs.existsSync(devPanelPath) && fs.statSync(devPanelPath).mtimeMs > cacheMtime)
173
144
  return false;
174
145
  return true;
@@ -177,11 +148,6 @@ function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPa
177
148
  return false;
178
149
  }
179
150
  }
180
- // ---------------------------------------------------------------------------
181
- // renderPage
182
- // ---------------------------------------------------------------------------
183
- // Small script injected into every page when hotReload is enabled.
184
- // It opens an SSE connection to /__authtics_hmr__ and reloads on signal.
185
151
  const HMR_SCRIPT = `
186
152
  <script>
187
153
  (function() {
@@ -198,10 +164,6 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
198
164
  }
199
165
  const usesDevPanel = showPanel && devPanelPath !== null;
200
166
  const templatePath = resolveTemplate(tsxPath);
201
- /*console.log(`[authtics] compiler = ${compiler}`);
202
- console.log(`[authtics] devPanelPath = ${devPanelPath ?? "(not found)"}`);
203
- console.log(`[authtics] usesDevPanel = ${usesDevPanel}`);
204
- console.log(`[authtics] template = ${templatePath ?? "(none, using default)"}`);*/
205
167
  const tempPath = tempFileForTsx(tsxPath);
206
168
  const serverCjsPath = tempPath.replace(/\.json$/, ".server.cjs");
207
169
  let clientCode;
@@ -211,14 +173,11 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
211
173
  const parsed = JSON.parse(readTempFile(tempPath));
212
174
  clientCode = parsed.clientCode;
213
175
  serverCode = fs.readFileSync(serverCjsPath, "utf-8");
214
- //console.log(`[authtics] using cached bundle for ${path.basename(tsxPath)}`);
215
176
  }
216
177
  catch {
217
- // corrupt — rebuild
218
178
  }
219
179
  }
220
180
  if (!clientCode || !serverCode) {
221
- //console.log(`[authtics] building bundle (${compiler}) for ${path.basename(tsxPath)}...`);
222
181
  const virtualServer = `
223
182
  import Page from ${JSON.stringify(tsxPath)};
224
183
  ${usesDevPanel ? `import DevPanel from ${JSON.stringify(devPanelPath)};` : ""}
@@ -248,9 +207,7 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
248
207
  }
249
208
  fs.writeFileSync(serverCjsPath, serverCode, "utf-8");
250
209
  writeTempFile(tempPath, JSON.stringify({ clientCode }));
251
- //console.log(`[authtics] bundle cached for ${path.basename(tsxPath)}`);
252
210
  }
253
- // SSR
254
211
  const serverModule = await import(`${serverCjsPath}?t=${Date.now()}`);
255
212
  const exports = serverModule.default ?? serverModule;
256
213
  const PageComponent = exports.default ?? exports.Page ?? exports.page ?? null;
@@ -1,19 +1,6 @@
1
1
  export declare function ensureTempDirs(): void;
2
- /**
3
- * Returns the cache file path for a given source .tsx file.
4
- * Uses the basename + a short hash of the full path to avoid collisions
5
- * between files named the same thing in different directories.
6
- *
7
- * Example: /project/pages/about.tsx → .authtics_host/cache/about.tsx_a3f9b2.json
8
- */
9
2
  export declare function tempFileForTsx(tsxPath: string): string;
10
- /**
11
- * Returns true if the cache file exists and is at least as new as the source file.
12
- */
13
3
  export declare function isTempUpToDate(tsxPath: string, tempPath: string): boolean;
14
4
  export declare function writeTempFile(tempPath: string, content: string): void;
15
5
  export declare function readTempFile(tempPath: string): string;
16
- /**
17
- * Wipes the entire cache directory. Useful for a `authtics clean` CLI command.
18
- */
19
6
  export declare function clearCache(): void;
@@ -1,11 +1,5 @@
1
- // src/rendererTempFiles.ts
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
- // ---------------------------------------------------------------------------
5
- // Directory layout:
6
- // <project>/.authtics_host/
7
- // cache/ ← compiled server+client bundles (JSON, keyed by source)
8
- // ---------------------------------------------------------------------------
9
3
  const AUTHTICS_DIR = path.join(process.cwd(), ".authtics_host");
10
4
  const CACHE_DIR = path.join(AUTHTICS_DIR, "cache");
11
5
  export function ensureTempDirs() {
@@ -14,22 +8,12 @@ export function ensureTempDirs() {
14
8
  if (!fs.existsSync(CACHE_DIR))
15
9
  fs.mkdirSync(CACHE_DIR, { recursive: true });
16
10
  }
17
- /**
18
- * Returns the cache file path for a given source .tsx file.
19
- * Uses the basename + a short hash of the full path to avoid collisions
20
- * between files named the same thing in different directories.
21
- *
22
- * Example: /project/pages/about.tsx → .authtics_host/cache/about.tsx_a3f9b2.json
23
- */
24
11
  export function tempFileForTsx(tsxPath) {
25
12
  ensureTempDirs();
26
13
  const base = path.basename(tsxPath);
27
14
  const hash = simpleHash(tsxPath);
28
15
  return path.join(CACHE_DIR, `${base}_${hash}.json`);
29
16
  }
30
- /**
31
- * Returns true if the cache file exists and is at least as new as the source file.
32
- */
33
17
  export function isTempUpToDate(tsxPath, tempPath) {
34
18
  try {
35
19
  const srcMtime = fs.statSync(tsxPath).mtimeMs;
@@ -46,19 +30,12 @@ export function writeTempFile(tempPath, content) {
46
30
  export function readTempFile(tempPath) {
47
31
  return fs.readFileSync(tempPath, "utf-8");
48
32
  }
49
- /**
50
- * Wipes the entire cache directory. Useful for a `authtics clean` CLI command.
51
- */
52
33
  export function clearCache() {
53
34
  if (fs.existsSync(CACHE_DIR)) {
54
35
  fs.rmSync(CACHE_DIR, { recursive: true, force: true });
55
36
  }
56
37
  ensureTempDirs();
57
38
  }
58
- // ---------------------------------------------------------------------------
59
- // Internal helpers
60
- // ---------------------------------------------------------------------------
61
- /** Tiny non-cryptographic hash — good enough to avoid filename collisions. */
62
39
  function simpleHash(str) {
63
40
  let h = 0;
64
41
  for (let i = 0; i < str.length; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananacool467/authtics-host",
3
- "version": "0.0.1-beta.8",
3
+ "version": "0.0.2-beta.1",
4
4
  "description": "A Next.js-inspired SSR framework for Node.js with TypeScript and JSX support.",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",