@bananacool467/authtics-host 0.1.6 → 0.1.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.
package/README.md CHANGED
@@ -1,12 +1,18 @@
1
1
  # Authtics Host
2
2
 
3
- Version 0.1.6
3
+ Version 0.1.8
4
4
  License: MIT
5
5
 
6
6
  Authtics Host is a framework inspired by Next.js. Authtics Host supports SSR (Server-Side Rendering) and uses esbuild.
7
7
 
8
8
  **(NOTE: The short name for Authtics Host is HostJS or Host.js)**
9
9
 
10
+ **(Fixes we're applied on this version with security fixes too)**
11
+ **(From the Developer: I am having 2 small issues:**
12
+ 1. **Web Socket Connections** are **not showing** in the DevPanel's Network tab
13
+ 2. The page doesnt **refresh properly**. (Have to refresh by doing: refresh -> cancel -> refresh to prevent halting)
14
+ **I need help fixing those)**
15
+
10
16
  ## Setting Up
11
17
 
12
18
  To get **Authtics Host** use:
@@ -151,13 +157,13 @@ Then when you run your project, you will get a page just saying **"Hello, World!
151
157
 
152
158
  The Developer Panel is what you could use to help you debug your code. The Developer Panel has **7 tabs**:
153
159
 
154
- **Console** — Shows current logs.
155
- **Route** — Shows information about the current route.
156
- **Webpage Info** — Shows information about the website/webpage.
157
- **Network** — Shows the current ping and shows sent fetches from the client and shows received from the server after fetching.
158
- **Client Info** — Shows information about your client's User Agent, OS **(Operating System)** and FPS **(Frames Per Second)**.
159
- **About Host** — Shows the current version of *Authtics Host*.
160
- **Server Info** — Shows how much heap was used and how much CPU was used on the server and how much requests there were and how much users are currently on the site and how long the site has been up.
160
+ * **Console** — Shows current logs.
161
+ * **Route** — Shows information about the current route.
162
+ * **Webpage Info** — Shows information about the website/webpage.
163
+ * **Network** — Shows the current ping and shows sent fetches from the client and shows received from the server after fetching.
164
+ * **Client Info** — Shows information about your client's User Agent, OS **(Operating System)** and FPS **(Frames Per Second)**.
165
+ * **About Host** — Shows the current version of *Authtics Host*.
166
+ * **Server Info** — Shows how much heap was used and how much CPU was used on the server and how much requests there were and how much users are currently on the site and how long the site has been up.
161
167
 
162
168
  The information the Developer Panel shows outside of the tabs is just your **DAT** (Developer Access Token).
163
169
 
@@ -165,9 +171,9 @@ The information the Developer Panel shows outside of the tabs is just your **DAT
165
171
 
166
172
  The Developer Panel also have remote buttons:
167
173
 
168
- **Restart App** — Restarts the website.
169
- **Shut Down App** — Stops the website.
170
- **Pause Users**/**Resume Users** — Stops users from reaching a site and they will get a JSON error message like this when visiting if Pause Users is on:
174
+ * **Restart App** — Restarts the website.
175
+ * **Shut Down App** — Stops the website.
176
+ * **Pause Users**/**Resume Users** — Stops users from reaching a site and they will get a JSON error message like this when visiting if Pause Users is on:
171
177
  ```json
172
178
  { "message": "unhandled request" }
173
179
  ```
@@ -181,6 +187,51 @@ and then create a file like `paused.tsx` and then inside `App.tsx`, import `Paus
181
187
  ```
182
188
  and if your using `autoPaging` then you just create a file named `_paused.tsx`.
183
189
 
190
+ ## App APIs
191
+
192
+ You can add APIs like `get`, `post`, `del` and `put`.
193
+
194
+ You can use the get API, for example:
195
+ ```typescript
196
+ app.get('/hi', (req, res) => {
197
+ res.text("Hello!");
198
+ })
199
+ ```
200
+ Then when you go to `/hi` you see:
201
+ ```
202
+ Hello!
203
+ ```
204
+
205
+ ## Notes about Authtics Host
206
+
207
+ Since Authtics Host is SSR, stuff like NPM packages that uses **Browser APIs** (e.g., `window`, `document`) will cause errors.
208
+ The developer (The one that is writing this README) may add a feature where Authtics Host converts:
209
+ ```tsx
210
+ export function Component() {
211
+ return (
212
+ <p>{window.__status_code__}</p>
213
+ )
214
+ }
215
+ ```
216
+ To:
217
+ ```tsx
218
+ import { useState, useEffect } from 'react';
219
+ export function Component() {
220
+ const [__window, __set_window] = useState<any>(null);
221
+ useEffect(() => {
222
+ if (window) __set_window(window);
223
+ }, []);
224
+ return (
225
+ <p>{__window.__status_code__}</p>
226
+ )
227
+ }
228
+ ```
229
+
230
+ The developer might also redesign the UI for the DevPanel using [UI Tools](https://npmjs.com/package/@bananakitssu/ui-tools).
231
+
232
+ > **Warning**:
233
+ > Asking AI about HostJS is not recommended, it will give an Auth platform and call HostJS/Authtics Host a typo, say it doesn't exist, say it was renamed, it is private, or the account doesn't exist. Or if it knows, it may swap your server script with regular **Node.js**/**ExpressJS**/**Next.js** or use **Next.js**-like code because this framework is **Next.js-inspired**
234
+
184
235
  ## Developer Panel Notes And DAT (Developer Access Token)
185
236
 
186
237
  A DAT (Developer Access Token) is your token that is used to access the Developer Panel.
@@ -207,4 +258,4 @@ There isn't any limit of DATs you can add.
207
258
 
208
259
  ## Contacting The Developer Of Authtics Host
209
260
 
210
- If you ever encounter a bug, report it to (or have questions, contact): authtics@proton.me
261
+ If you have any questions, or encountered a bug while using HostJS, contact: authtics@proton.me
package/dist/cli.d.ts CHANGED
@@ -8,4 +8,5 @@ export default class App {
8
8
  post(routePath: string, handler: (req: IncomingMessage, res: ServerResponse) => void): void;
9
9
  put(routePath: string, handler: (req: IncomingMessage, res: ServerResponse) => void): void;
10
10
  delete(routePath: string, handler: (req: IncomingMessage, res: ServerResponse) => void): void;
11
+ remove(method: string, routePath: string): void;
11
12
  }
package/dist/cli.js CHANGED
@@ -15,16 +15,67 @@ const routes = new Map();
15
15
  const __filename = url.fileURLToPath(import.meta.url);
16
16
  const __dirname = path.dirname(__filename);
17
17
  const hmrClients = new Set();
18
- function notifyHmrClients() {
19
- for (const client of hmrClients) {
20
- try {
21
- client.write('data: reload\n\n');
18
+ function safeWatch(target, onChange) {
19
+ const shouldReact = (filename) => {
20
+ if (!filename)
21
+ return false;
22
+ if (filename.includes('.authtics_host'))
23
+ return false;
24
+ if (filename.includes('node_modules'))
25
+ return false;
26
+ return /\.(tsx?|jsx?|html|css)$/i.test(filename);
27
+ };
28
+ try {
29
+ const stat = fs.statSync(target);
30
+ if (stat.isFile()) {
31
+ fs.watch(target, (_et, filename) => { if (shouldReact(filename ?? target))
32
+ onChange(); });
33
+ return;
22
34
  }
23
- catch {
24
- hmrClients.delete(client);
35
+ fs.watch(target, { recursive: true }, (_et, filename) => {
36
+ if (shouldReact(filename))
37
+ onChange();
38
+ });
39
+ }
40
+ catch (err) {
41
+ if (err.code === 'ERR_FEATURE_UNAVAILABLE_ON_PLATFORM' || err.code === 'EINVAL' || err.code === 'ENOSYS') {
42
+ try {
43
+ fs.watch(target, (_et, f) => { if (shouldReact(f))
44
+ onChange(); });
45
+ }
46
+ catch { }
47
+ try {
48
+ for (const entry of fs.readdirSync(target, { withFileTypes: true })) {
49
+ if (entry.isDirectory()) {
50
+ try {
51
+ const sub = path.join(target, entry.name);
52
+ fs.watch(sub, (_et, f) => { if (shouldReact(f))
53
+ onChange(); });
54
+ }
55
+ catch { }
56
+ }
57
+ }
58
+ }
59
+ catch { }
25
60
  }
26
61
  }
27
62
  }
63
+ let hmrDebounceTimer = null;
64
+ function notifyHmrClients() {
65
+ if (hmrDebounceTimer)
66
+ clearTimeout(hmrDebounceTimer);
67
+ hmrDebounceTimer = setTimeout(() => {
68
+ hmrDebounceTimer = null;
69
+ for (const client of hmrClients) {
70
+ try {
71
+ client.write('data: reload\n\n');
72
+ }
73
+ catch {
74
+ hmrClients.delete(client);
75
+ }
76
+ }
77
+ }, 120);
78
+ }
28
79
  const MIME_TYPES = {
29
80
  '.html': 'text/html',
30
81
  '.css': 'text/css',
@@ -343,6 +394,10 @@ export default class App {
343
394
  delete(routePath, handler) {
344
395
  routes.set(`DELETE:${routePath}`, handler);
345
396
  }
397
+ remove(method, routePath) {
398
+ if (routes.has(`${method}:${routePath}`))
399
+ routes.delete(`${method}:${routePath}`);
400
+ }
346
401
  }
347
402
  async function start() {
348
403
  let config = {};
@@ -483,7 +538,7 @@ async function start() {
483
538
  pathname = safePath;
484
539
  if (pathname !== "/" && pathname.endsWith("/"))
485
540
  pathname = pathname.slice(0, -1);
486
- if (!pathname.includes('/__authtics')) {
541
+ if (!pathname.includes('/__authtics') && !pathname.includes('/hmr')) {
487
542
  totalRequests++;
488
543
  }
489
544
  const routeKey = `${method}:${pathname}`;
@@ -498,7 +553,7 @@ async function start() {
498
553
  res.setHeader("Content-Type", "application/javascript");
499
554
  res.end(f);
500
555
  });
501
- }, req.cookies.dat, config_.dat);
556
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
502
557
  html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
503
558
  res.setHeader("Content-Type", "text/html");
504
559
  return res.end(html);
@@ -521,7 +576,7 @@ async function start() {
521
576
  res.setHeader("Content-Type", "application/javascript");
522
577
  res.end(f);
523
578
  });
524
- }, req.cookies.dat, config_.dat);
579
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
525
580
  html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
526
581
  res.setHeader("Content-Type", "text/html");
527
582
  return res.end(html);
@@ -551,7 +606,7 @@ async function start() {
551
606
  res.setHeader("Content-Type", "application/javascript");
552
607
  res.end(f);
553
608
  });
554
- }, req.cookies.dat, config_.dat);
609
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
555
610
  html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
556
611
  res.setHeader("Content-Type", "text/html");
557
612
  return res.end(html);
@@ -574,7 +629,7 @@ async function start() {
574
629
  res.setHeader("Content-Type", "application/javascript");
575
630
  res.end(f);
576
631
  });
577
- }, req.cookies.dat, config_.dat);
632
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
578
633
  html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
579
634
  res.setHeader("Content-Type", "text/html");
580
635
  return res.end(html);
@@ -602,7 +657,7 @@ async function start() {
602
657
  return handler(req, res);
603
658
  }
604
659
  }
605
- if (pathname === '/__authtics_hmr__') {
660
+ if (pathname === '/hmr') {
606
661
  res.writeHead(200, {
607
662
  'Content-Type': 'text/event-stream',
608
663
  'Cache-Control': 'no-cache',
@@ -625,7 +680,7 @@ async function start() {
625
680
  res.setHeader("Content-Type", "application/javascript");
626
681
  res.end(f);
627
682
  });
628
- }, req.cookies.dat, config_.dat);
683
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
629
684
  html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
630
685
  res.setHeader("Content-Type", "text/html");
631
686
  return res.end(html);
@@ -648,7 +703,7 @@ async function start() {
648
703
  res.setHeader("Content-Type", "application/javascript");
649
704
  res.end(f);
650
705
  });
651
- }, req.cookies.dat, config_.dat);
706
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
652
707
  html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
653
708
  res.setHeader("Content-Type", "text/html");
654
709
  return res.end(html);
@@ -678,7 +733,7 @@ async function start() {
678
733
  res.setHeader("Content-Type", "application/javascript");
679
734
  res.end(f);
680
735
  });
681
- }, req.cookies.dat, config_.dat);
736
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
682
737
  html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
683
738
  res.setHeader("Content-Type", "text/html");
684
739
  return res.end(html);
@@ -701,7 +756,7 @@ async function start() {
701
756
  res.setHeader("Content-Type", "application/javascript");
702
757
  res.end(f);
703
758
  });
704
- }, req.cookies.dat, config_.dat);
759
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
705
760
  html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
706
761
  res.setHeader("Content-Type", "text/html");
707
762
  return res.end(html);
@@ -759,7 +814,7 @@ async function start() {
759
814
  res.setHeader("Content-Type", "application/javascript");
760
815
  res.end(f);
761
816
  });
762
- }, req.cookies.dat, config_.dat);
817
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
763
818
  res.setHeader("Content-Type", "text/html");
764
819
  res.writeHead(404);
765
820
  html = html.replace("<script>", "<script>\nwindow.__status_code__ = 404;");
@@ -785,7 +840,7 @@ async function start() {
785
840
  res.setHeader("Content-Type", "application/javascript");
786
841
  res.end(f);
787
842
  });
788
- }, req.cookies.dat, config_.dat);
843
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
789
844
  res.setHeader("Content-Type", "text/html");
790
845
  return res.end(html);
791
846
  }
@@ -809,7 +864,7 @@ async function start() {
809
864
  res.setHeader("Content-Type", "application/javascript");
810
865
  res.end(f);
811
866
  });
812
- }, req.cookies.dat, config_.dat);
867
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
813
868
  res.setHeader("Content-Type", "text/html");
814
869
  return res.end(html);
815
870
  }
@@ -828,7 +883,7 @@ async function start() {
828
883
  res.setHeader("Content-Type", "application/javascript");
829
884
  res.end(f);
830
885
  });
831
- }, req.cookies.dat, config_.dat);
886
+ }, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
832
887
  res.setHeader("Content-Type", "text/html");
833
888
  res.writeHead(404);
834
889
  html = html.replace("<script>", "<script>\nwindow.__status_code__ = 404;");
@@ -850,34 +905,24 @@ async function start() {
850
905
  }, 1000);
851
906
  });
852
907
  if (config.hotReload === true) {
853
- const watchDirs = [pagesDir];
854
- const devPanelDir = path.join(__dirname, 'devPanel.js');
855
- const _404Dir = path.join(__dirname, '404.js');
856
- if (fs.existsSync(devPanelDir))
857
- watchDirs.push(devPanelDir);
858
- for (const dir of watchDirs) {
859
- if (dir && !fs.existsSync(dir))
860
- continue;
861
- if (dir) {
862
- fs.watch(dir, { recursive: true }, (eventType, filename) => {
863
- if (!filename)
864
- return;
865
- notifyHmrClients();
866
- });
867
- }
908
+ const watchSet = new Set();
909
+ watchSet.add(process.cwd());
910
+ if (pagesDir && fs.existsSync(pagesDir))
911
+ watchSet.add(pagesDir);
912
+ const appTsx = routes.get("APP_TSX");
913
+ if (appTsx) {
914
+ const appTsxDir = path.dirname(path.join(process.cwd(), appTsx));
915
+ if (fs.existsSync(appTsxDir))
916
+ watchSet.add(appTsxDir);
868
917
  }
869
- if (fs.existsSync(_404Dir))
870
- watchDirs.push(_404Dir);
871
- for (const dir of watchDirs) {
872
- if (dir && !fs.existsSync(dir))
873
- continue;
874
- if (dir) {
875
- fs.watch(dir, { recursive: true }, (eventType, filename) => {
876
- if (!filename)
877
- return;
878
- notifyHmrClients();
879
- });
880
- }
918
+ const devPanelBuilt = path.join(__dirname, 'devPanel.js');
919
+ const _404Built = path.join(__dirname, '404.js');
920
+ if (fs.existsSync(devPanelBuilt))
921
+ watchSet.add(devPanelBuilt);
922
+ if (fs.existsSync(_404Built))
923
+ watchSet.add(_404Built);
924
+ for (const dir of watchSet) {
925
+ safeWatch(dir, notifyHmrClients);
881
926
  }
882
927
  }
883
928
  return server;
@@ -1,2 +1 @@
1
- import 'prismjs/components/prism-javascript';
2
1
  export default function DevPanel(): import("react/jsx-runtime").JSX.Element;
package/dist/devPanel.js CHANGED
@@ -1,9 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useState, useEffect, useRef } from 'react';
3
3
  import { ShieldCheck, CircleX, ChevronDown, ChevronUp, Bug, AlertTriangle, Info, ArrowLeft, Wifi, Terminal, Route, Globe, ChevronRight, ChevronLeft, Server, TriangleAlert, Eye, Zap } from 'lucide-react';
4
- import Prism from 'prismjs';
5
- import 'prismjs/components/prism-javascript';
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/;
7
4
  function getCallerSource() {
8
5
  try {
9
6
  const stack = new Error().stack ?? '';
@@ -63,7 +60,7 @@ function Sparkline({ data, color, label, unit }) {
63
60
  return _jsx("circle", { cx: last[0], cy: last[1], r: "2.5", fill: color });
64
61
  })()] }))] })] }));
65
62
  }
66
- function installNetworkInterceptor(onFetch, setFetch, onEventSource, setEventSource, onWebSocket, setWebSocket, onResource, setResource, onBytesClient, onBytesServer) {
63
+ function installNetworkInterceptor(onFetch, setFetch, onEventSource, setEventSource, onWebSocket, setWebSocket, onResource, setResource, rmvResource, onBytesClient, onBytesServer) {
67
64
  if (window.__authtics_network_intercepted__)
68
65
  return;
69
66
  window.__authtics_network_intercepted__ = true;
@@ -87,7 +84,7 @@ function installNetworkInterceptor(onFetch, setFetch, onEventSource, setEventSou
87
84
  try {
88
85
  const resp = await originalFetch(...args);
89
86
  const clone = resp.clone();
90
- setFetch({ url, method, status: resp.status, id: savedFetchId, response: await resp.text(), sent: body ? JSON.stringify(body, null, 2).replace('"{', '{')
87
+ setFetch({ url, method, status: clone.status, id: savedFetchId, response: await clone.text(), sent: body ? JSON.stringify(body, null, 2).replace('"{', '{')
91
88
  .replace('}"', '}')
92
89
  .replaceAll('\\"', '"') : null });
93
90
  clone.arrayBuffer().then(buf => {
@@ -221,23 +218,60 @@ function installNetworkInterceptor(onFetch, setFetch, onEventSource, setEventSou
221
218
  };
222
219
  window.EventSource.prototype = OriginalEventSource.prototype;
223
220
  Object.assign(window.EventSource, OriginalEventSource);
224
- setTimeout(() => {
221
+ let usedResourcesSlots = [];
222
+ let beforeResources = [];
223
+ let currentResources = [];
224
+ setInterval(() => {
225
+ beforeResources = currentResources;
226
+ currentResources = [];
225
227
  const queryImg = document.querySelectorAll('img');
226
228
  const queryScript = document.querySelectorAll('script');
227
229
  const queryLink = document.querySelectorAll('link');
228
230
  queryImg.forEach((img) => {
229
- if (img.src)
230
- onResource({ url: img.src, type: 'img', id: fetchId++ });
231
+ if (img.src) {
232
+ fetchId++;
233
+ let savedFetchId = fetchId;
234
+ if (!usedResourcesSlots.includes({ url: img.src, type: 'img', id: savedFetchId }))
235
+ onResource({ url: img.src, type: 'img', id: savedFetchId });
236
+ currentResources.push({ url: img.src, type: 'img', id: savedFetchId });
237
+ if (usedResourcesSlots.includes({ url: img.src, type: 'img', id: savedFetchId }))
238
+ setResource({ url: img.src, type: 'img', id: savedFetchId });
239
+ if (!usedResourcesSlots.includes({ url: img.src, type: 'img', id: savedFetchId }))
240
+ usedResourcesSlots.push({ url: img.src, type: 'img', id: savedFetchId });
241
+ }
231
242
  });
232
243
  queryScript.forEach((script) => {
233
- if (script.src)
234
- onResource({ url: script.src, type: 'script', id: fetchId++ });
244
+ if (script.src) {
245
+ fetchId++;
246
+ let savedFetchId = fetchId;
247
+ if (!usedResourcesSlots.includes({ url: script.src, type: 'script', id: savedFetchId }))
248
+ onResource({ url: script.src, type: 'script', id: savedFetchId });
249
+ currentResources.push({ url: script.src, type: 'script', id: savedFetchId });
250
+ if (usedResourcesSlots.includes({ url: script.src, type: 'script', id: savedFetchId }))
251
+ setResource({ url: script.src, type: 'script', id: savedFetchId });
252
+ if (!usedResourcesSlots.includes({ url: script.src, type: 'script', id: savedFetchId }))
253
+ usedResourcesSlots.push({ url: script.src, type: 'script', id: savedFetchId });
254
+ }
235
255
  });
236
256
  queryLink.forEach((link) => {
237
- if (link.href)
238
- onResource({ url: link.href, type: 'link', id: fetchId++ });
257
+ if (link.href) {
258
+ fetchId++;
259
+ let savedFetchId = fetchId;
260
+ if (!usedResourcesSlots.includes({ url: link.href, type: 'link', id: savedFetchId }))
261
+ onResource({ url: link.href, type: 'link', id: savedFetchId });
262
+ currentResources.push({ url: link.href, type: 'link', id: savedFetchId });
263
+ if (usedResourcesSlots.includes({ url: link.href, type: 'link', id: savedFetchId }))
264
+ setResource({ url: link.href, type: 'link', id: savedFetchId });
265
+ if (!usedResourcesSlots.includes({ url: link.href, type: 'link', id: savedFetchId }))
266
+ usedResourcesSlots.push({ url: link.href, type: 'link', id: savedFetchId });
267
+ }
239
268
  });
240
- }, 5000);
269
+ beforeResources.forEach((resource) => {
270
+ if (!currentResources.includes(resource)) {
271
+ rmvResource(resource);
272
+ }
273
+ });
274
+ }, 250);
241
275
  }
242
276
  function installConsoleInterceptor() {
243
277
  if (window.__authtics_intercepted__)
@@ -260,7 +294,7 @@ function installConsoleInterceptor() {
260
294
  });
261
295
  }
262
296
  function SecuredTab() {
263
- return (_jsxs("div", { style: { display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column', padding: '8px 10px', textAlign: 'center', gap: '8px' }, children: [_jsx(CircleX, { size: 32, color: 'red' }), _jsx("span", { style: { color: 'red', fontSize: 24 }, children: "This Tab Is Currently Not Accessible" }), _jsx("span", { style: { color: 'red', fontSize: 12 }, children: "To access this tab, you need to set \"dat\" in the Authtics Host config to set restriction and then restart." })] }));
297
+ return (_jsxs("div", { style: { display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column', padding: '10px 10px', textAlign: 'center', gap: '8px' }, children: [_jsx(CircleX, { size: 32, color: 'red' }), _jsx("span", { style: { color: 'red', fontSize: 24 }, children: "This Tab Is Currently Not Accessible" }), _jsx("span", { style: { color: 'red', fontSize: 12 }, children: "To access this tab, you need to set \"dat\" in the Authtics Host config to set restriction and then restart." })] }));
264
298
  }
265
299
  function PanelButton({ onClick, onClick2, children, style, fullWidth, disabled }) {
266
300
  const [hovered, setHovered] = useState(false);
@@ -309,7 +343,6 @@ export default function DevPanel() {
309
343
  const [ping__, setPing__] = useState(0);
310
344
  const [networkFetches, setNetworkFetches] = useState([]);
311
345
  const [eventSources, setEventSources] = useState([]);
312
- const [usersPaused, setUsersPaused] = useState(false);
313
346
  const [usersPausedRealInfo, setUsersPausedRealInfo] = useState(false);
314
347
  const [webSockets, setWebSockets] = useState([]);
315
348
  const [resourcesFound, setResourcesFound] = useState([]);
@@ -324,7 +357,6 @@ export default function DevPanel() {
324
357
  const heapUsage = useRef(0);
325
358
  const [datMatch, setDatMatch] = useState(false);
326
359
  const [compilerType, setCompilerType] = useState("?");
327
- const [errorDetail, setErrorDetail] = useState(undefined);
328
360
  const [httpVersion, setHttpVersion] = useState("v?.?");
329
361
  const [errorBoxOpenable, setErrorBoxOpenable] = useState(false);
330
362
  const [uptime, setUptime] = useState("00:00:00:00");
@@ -400,7 +432,6 @@ export default function DevPanel() {
400
432
  if (resp3.status === 200) {
401
433
  const data = await resp3.json();
402
434
  paused = data.paused;
403
- setUsersPaused(paused);
404
435
  setUsersPausedRealInfo(data.realInfo);
405
436
  }
406
437
  if (!data.isMatching && !paused) {
@@ -603,7 +634,7 @@ export default function DevPanel() {
603
634
  }, []);
604
635
  useEffect(() => {
605
636
  installConsoleInterceptor();
606
- installNetworkInterceptor((f) => setNetworkFetches(prev => [...prev.slice(-49), f]), (f) => setNetworkFetches(prev => prev.map(p => p.id === f.id ? f : p)), (f) => setEventSources(prev => [...prev.slice(-49), f]), (f) => setEventSources(prev => prev.map(p => p.id === f.id ? f : p)), (f) => setWebSockets(prev => [...prev.slice(-49), f]), (f) => setWebSockets(prev => prev.map(p => p.id === f.id ? f : p)), (f) => setResourcesFound(prev => [...prev.slice(-49), f]), (f) => setResourcesFound(prev => prev.map(p => p.id === f.id ? f : p)), (sent) => {
637
+ installNetworkInterceptor((f) => setNetworkFetches(prev => [...prev.slice(-49), f]), (f) => setNetworkFetches(prev => prev.map(p => p.id === f.id ? f : p)), (f) => setEventSources(prev => [...prev.slice(-49), f]), (f) => setEventSources(prev => prev.map(p => p.id === f.id ? f : p)), (f) => setWebSockets(prev => [...prev.slice(-49), f]), (f) => setWebSockets(prev => prev.map(p => p.id === f.id ? f : p)), (f) => setResourcesFound(prev => [...prev.slice(-49), f]), (f) => setResourcesFound(prev => prev.map(p => p.id === f.id ? f : p)), (f) => setResourcesFound(prev => prev.filter(p => p.id !== f.id)), (sent) => {
607
638
  pendingSent.current += sent;
608
639
  }, (received) => {
609
640
  pendingReceived.current += received;
@@ -615,7 +646,6 @@ export default function DevPanel() {
615
646
  setHasIssue(true);
616
647
  setErrorMsgs(prev => [...prev, detail.message]);
617
648
  setErrorBoxOpen(true);
618
- setErrorDetail(detail.source);
619
649
  }
620
650
  ;
621
651
  };
@@ -629,23 +659,27 @@ export default function DevPanel() {
629
659
  setTokenLength(length);
630
660
  }
631
661
  else {
632
- const newToken = generateToken();
633
- try {
634
- let localStorageToken;
662
+ const cfg = window.__authtics_config__;
663
+ const serverDevMode = cfg?.devMode !== false;
664
+ const serverDatRestricted = cfg?.datRestricted === true;
665
+ if (serverDevMode && !serverDatRestricted) {
666
+ const newToken = generateToken();
635
667
  try {
636
- localStorageToken = localStorage.getItem('dat');
668
+ let localStorageToken;
669
+ try {
670
+ localStorageToken = localStorage.getItem('dat');
671
+ }
672
+ catch (e) { }
673
+ cookie.set('dat', localStorageToken || newToken);
674
+ setToken(newToken);
675
+ let length_ = newToken?.split('');
676
+ let length;
677
+ length = length_?.length;
678
+ setTokenLength(length);
637
679
  }
638
- catch (e) {
680
+ catch (error) {
681
+ console.error('[Authtics Host] Error storing token in localStorage:', error);
639
682
  }
640
- cookie.set('dat', localStorageToken || newToken);
641
- setToken(newToken);
642
- let length_ = newToken?.split('');
643
- let length;
644
- length = length_?.length;
645
- setTokenLength(length);
646
- }
647
- catch (error) {
648
- console.error('[Authtics Host] Error storing token in localStorage:', error);
649
683
  }
650
684
  }
651
685
  }
@@ -1,3 +1,3 @@
1
1
  export type CompilerType = 'esbuild' | 'babel';
2
2
  export declare function findFileUpwards(startDir: string, relativePath: string): string | null;
3
- export declare function renderPage(tsxPath: string, reqUrl?: string, showPanel?: boolean, compiler?: CompilerType, hotReload?: boolean, autoPaging?: boolean, autoPageDir?: string, appDir?: string, fileHandler?: (f: string, p: string) => void, reqDAT?: string | undefined, configDAT?: string | string[] | undefined): Promise<string>;
3
+ export declare function renderPage(tsxPath: string, reqUrl?: string, showPanel?: boolean, compiler?: CompilerType, hotReload?: boolean, autoPaging?: boolean, autoPageDir?: string, appDir?: string, fileHandler?: (f: string, p: string) => void, rmvFileHandler?: (p: string) => void, reqDAT?: string | undefined, configDAT?: string | string[] | undefined): Promise<string>;
@@ -9,6 +9,39 @@ import { renderToString } from "react-dom/server";
9
9
  import { tempFileForTsx, writeTempFile, readTempFile, ensureTempDirs, } from "./rendererTempFiles.js";
10
10
  const __filename = fileURLToPath(import.meta.url);
11
11
  const __dirname = path.dirname(__filename);
12
+ const NODE_BUILTINS = [
13
+ 'fs', 'path', 'http', 'https', 'net', 'tls', 'os', 'url', 'dns',
14
+ 'crypto', 'stream', 'buffer', 'events', 'util', 'zlib', 'assert',
15
+ 'child_process', 'cluster', 'dgram', 'readline', 'repl', 'vm',
16
+ 'worker_threads', 'timers', 'module', 'process', 'string_decoder',
17
+ 'querystring', 'punycode', 'domain', 'constants', 'sys',
18
+ 'node:fs', 'node:path', 'node:http', 'node:https', 'node:net', 'node:tls',
19
+ 'node:os', 'node:url', 'node:dns', 'node:crypto', 'node:stream',
20
+ 'node:buffer', 'node:events', 'node:util', 'node:zlib', 'node:assert',
21
+ 'node:child_process', 'node:cluster', 'node:dgram', 'node:readline',
22
+ 'node:repl', 'node:vm', 'node:worker_threads', 'node:timers',
23
+ 'node:module', 'node:process', 'node:string_decoder', 'node:querystring',
24
+ 'express', 'jiti',
25
+ ];
26
+ const nodeStubPlugin = {
27
+ name: 'browser-node-stub',
28
+ setup(build) {
29
+ const escapedNames = NODE_BUILTINS.map(n => n.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
30
+ const filter = new RegExp(`^(${escapedNames.join('|')})$`);
31
+ build.onResolve({ filter }, args => ({
32
+ path: args.path,
33
+ namespace: 'browser-node-stub',
34
+ }));
35
+ build.onLoad({ filter: /.*/, namespace: 'browser-node-stub' }, () => ({
36
+ contents: `
37
+ // Node.js built-in stub for browser builds
38
+ const stub = new Proxy({}, { get: () => () => undefined });
39
+ export default stub;
40
+ `,
41
+ loader: 'js',
42
+ }));
43
+ },
44
+ };
12
45
  async function buildWithBabel(entryPath, virtualContent, platform) {
13
46
  const presets = [
14
47
  [babelPresetEnv, {
@@ -26,6 +59,7 @@ async function buildWithBabel(entryPath, virtualContent, platform) {
26
59
  return result.code;
27
60
  }
28
61
  async function buildWithEsbuild(virtualContent, resolveDir, platform) {
62
+ const toFwdSlash = (p) => p.split(path.sep).join('/');
29
63
  const isNode = platform === 'node';
30
64
  const result = await esbuild.build({
31
65
  stdin: {
@@ -38,19 +72,23 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform) {
38
72
  format: isNode ? 'cjs' : 'iife',
39
73
  platform,
40
74
  jsx: 'automatic',
41
- sourcemap: isNode ? false : 'inline',
75
+ sourcemap: isNode ? false : false,
42
76
  target: isNode ? ['node18'] : ['es2020'],
43
- minify: false,
77
+ minify: true,
44
78
  treeShaking: true,
79
+ plugins: isNode ? [] : [nodeStubPlugin],
45
80
  external: isNode
46
- ? ['react', 'react-dom', 'react-dom/server', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'node:http',
47
- 'node:path',
48
- 'node:fs',
49
- 'node:url',
50
- 'tls',
51
- path.join(__dirname, 'cli.js')]
81
+ ? ['react', 'react-dom', 'react-dom/server', 'react/jsx-runtime', 'react/jsx-dev-runtime',
82
+ 'node:http', 'node:path', 'node:fs', 'node:url', 'tls',
83
+ toFwdSlash(path.join(__dirname, 'cli.js')),
84
+ toFwdSlash(path.join(__dirname, 'config.js')),
85
+ toFwdSlash(path.join(__dirname, 'renderEngine.js')),
86
+ toFwdSlash(path.join(__dirname, 'rendererTempFiles.js'))]
52
87
  : [
53
- path.join(__dirname, 'cli.js')
88
+ toFwdSlash(path.join(__dirname, 'cli.js')),
89
+ toFwdSlash(path.join(__dirname, 'config.js')),
90
+ toFwdSlash(path.join(__dirname, 'renderEngine.js')),
91
+ toFwdSlash(path.join(__dirname, 'rendererTempFiles.js'))
54
92
  ],
55
93
  });
56
94
  return result.outputFiles[0].text;
@@ -86,7 +124,8 @@ function resolveTemplate(appDir, autoPageDir, autoPaging) {
86
124
  return nextToCli;
87
125
  }
88
126
  function buildHtml(opts) {
89
- const { pageHtml, clientCode, devPanelContainer, templatePath, hotReload, fileName } = opts;
127
+ const { pageHtml, clientCode, devPanelContainer, templatePath, hotReload, fileName, datRestricted, devMode } = opts;
128
+ const configScript = `<script>window.__authtics_config__={datRestricted:${datRestricted ? 'true' : 'false'},devMode:${devMode !== false ? 'true' : 'false'}};</script>`;
90
129
  const hmrScript = hotReload ? HMR_SCRIPT : '';
91
130
  if (templatePath && fs.existsSync(templatePath)) {
92
131
  let template = fs.readFileSync(templatePath, "utf-8");
@@ -94,8 +133,8 @@ function buildHtml(opts) {
94
133
  if (contentTag.test(template)) {
95
134
  template = template.replace(contentTag, `<div id="root">${pageHtml}</div>\n ${devPanelContainer}`);
96
135
  template = template.includes("</body>")
97
- ? template.replace("</body>", ` ${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}\n${hmrScript}</body>`)
98
- : template + `\n${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}${hmrScript}`;
136
+ ? template.replace("</body>", ` ${configScript}${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}\n${hmrScript}</body>`)
137
+ : template + `\n${configScript}${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}\n${hmrScript}`;
99
138
  return template;
100
139
  }
101
140
  console.warn('[authtics] _template.html has no <authtics_host_content /> — using default layout.');
@@ -109,6 +148,7 @@ function buildHtml(opts) {
109
148
  <body>
110
149
  <div id="root">${pageHtml}</div>
111
150
  ${devPanelContainer}
151
+ ${configScript}
112
152
  ${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}
113
153
  ${hmrScript}
114
154
  </body>
@@ -131,39 +171,47 @@ function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPa
131
171
  return false;
132
172
  }
133
173
  }
134
- const HMR_SCRIPT = `
135
- <script>
136
- (function() {
137
- const es = new EventSource('/__authtics_hmr__');
138
- es.onmessage = function(e) { if (e.data === 'reload') window.location.reload(); };
139
- es.onerror = function() { setTimeout(function() { window.location.reload(); }, 1000); };
140
- window.onbeforeunload = function() {
141
- es.close();
142
- es = null;
143
- };
144
- })();
145
- </script>
146
- `;
147
- export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = 'esbuild', hotReload = false, autoPaging = true, autoPageDir, appDir, fileHandler, reqDAT, configDAT) {
174
+ const HMR_SCRIPT = `<script>(function(){
175
+ var reloading=false;
176
+ var es=new EventSource('/hmr');
177
+ es.onmessage=function(e){
178
+ if(e.data==='reload'&&!reloading){
179
+ reloading=true;
180
+ es.close();
181
+ window.location.reload();
182
+ }
183
+ };
184
+ es.onerror=function(){
185
+ // onerror fires on both server-down AND on page-unload (SSE closed by navigation).
186
+ // Guard with reloading so a navigate-away never schedules a second reload.
187
+ if(!reloading){
188
+ setTimeout(function(){
189
+ if(!reloading){reloading=true;window.location.reload();}
190
+ },1500);
191
+ }
192
+ };
193
+ })();</script>`;
194
+ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = 'esbuild', hotReload = false, autoPaging = true, autoPageDir, appDir, fileHandler, rmvFileHandler, reqDAT, configDAT) {
148
195
  ensureTempDirs();
149
196
  if (!fs.existsSync(tsxPath)) {
150
197
  throw new Error(`renderPage: source file not found: ${tsxPath}`);
151
198
  }
152
199
  let usesDevPanel = showPanel && devPanelPath !== null;
153
- let datMatched = false;
154
- if (configDAT && configDAT == reqDAT)
155
- datMatched = true;
156
- if (configDAT && Array.isArray(configDAT) && configDAT.includes(reqDAT || ""))
157
- datMatched = true;
158
- if (configDAT == undefined)
159
- datMatched = true;
160
- if (!datMatched && usesDevPanel)
161
- usesDevPanel = false;
200
+ if (usesDevPanel) {
201
+ let datMatched = configDAT === undefined;
202
+ if (configDAT && configDAT === reqDAT)
203
+ datMatched = true;
204
+ if (configDAT && Array.isArray(configDAT) && configDAT.includes(reqDAT ?? ""))
205
+ datMatched = true;
206
+ if (!datMatched)
207
+ usesDevPanel = false;
208
+ }
162
209
  const templatePath = resolveTemplate(appDir, autoPageDir, autoPaging);
163
210
  const tempPath = tempFileForTsx(tsxPath);
164
211
  const serverCjsPath = tempPath.replace(/\.json$/, ".server.cjs");
165
212
  let clientCode;
166
213
  let serverCode;
214
+ let freshWrite = false;
167
215
  if (isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath) && autoPaging) {
168
216
  try {
169
217
  const parsed = JSON.parse(readTempFile(tempPath));
@@ -203,8 +251,10 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
203
251
  }
204
252
  fs.writeFileSync(serverCjsPath, serverCode, "utf-8");
205
253
  writeTempFile(tempPath, JSON.stringify({ clientCode }));
254
+ freshWrite = true;
206
255
  }
207
- const serverModule = await import(`${serverCjsPath}?t=${Date.now()}`);
256
+ fileHandler ? fileHandler(clientCode, `/${path.basename(tempPath).replace(".json", ".js").replace(".tsx_", ".").toLowerCase()}`) : null;
257
+ const serverModule = await import(freshWrite ? `${serverCjsPath}?t=${Date.now()}` : serverCjsPath);
208
258
  const exports = serverModule.default ?? serverModule;
209
259
  const PageComponent = exports.default ?? exports.Page ?? exports.page ?? null;
210
260
  const DevPanelComponent = usesDevPanel ? (exports.DevPanel ?? null) : null;
@@ -218,6 +268,9 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
218
268
  const devPanelContainer = usesDevPanel
219
269
  ? `<div id="authtics-dev-panel-root">${devPanelHtml}</div>`
220
270
  : "";
221
- fileHandler ? fileHandler(clientCode.replace('\n', ''), `/${tsxPath.substring(tsxPath.lastIndexOf('/') + 1).replace(".tsx", ".js").toLowerCase()}`) : null;
222
- return buildHtml({ pageHtml, clientCode, devPanelContainer, templatePath, hotReload, fileName: `/${tsxPath.substring(tsxPath.lastIndexOf('/') + 1).replace(".tsx", ".js").toLowerCase()}` });
271
+ return buildHtml({ pageHtml, clientCode, devPanelContainer, templatePath, hotReload,
272
+ fileName: `/${path.basename(tempPath).replace(".json", ".js").replace(".tsx_", ".").toLowerCase()}`,
273
+ datRestricted: configDAT !== undefined,
274
+ devMode: showPanel,
275
+ });
223
276
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananacool467/authtics-host",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Authtics Host, 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",