@bananacool467/authtics-host 0.1.2 → 0.1.4

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.1.2
3
+ Version 0.1.4
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.
@@ -151,15 +151,15 @@ Then when you run your project, you will get a page just saying **"Hello, World!
151
151
 
152
152
  The Developer Panel is what you could use to help you debug your code. The Developer Panel has **7 tabs**:
153
153
 
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 and FPS
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
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 and FPS.
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.
161
161
 
162
- The information the Developer Panel shows is just your **DAT** (Developer Access Token).
162
+ The information the Developer Panel shows outside of the tabs is just your **DAT** (Developer Access Token).
163
163
 
164
164
  **(NOTE: If you do not set DAT restriction (Which will be explained on how to set it up in the next section), you will only get access to the Console and Network tabs)**
165
165
 
package/dist/cli.js CHANGED
@@ -266,8 +266,26 @@ export default class App {
266
266
  const pathname = reqBody.pathname;
267
267
  const pagesDir = config_.autoPage === true ? path.join(process.cwd(), config_.autoPageDir || "") : undefined;
268
268
  const safePath = path.normalize(pathname).replace(/^(\.\.[\/\\])+/, '');
269
- const filePath = pagesDir ? path.join(pagesDir, safePath + ".tsx") : undefined;
270
- const fileExists = filePath ? fs.existsSync(filePath) : true;
269
+ let filePath = pagesDir ? path.join(pagesDir, (safePath !== "/" ? safePath : "index") + ".tsx") : undefined;
270
+ let fileExists = filePath ? fs.existsSync(filePath) : true;
271
+ const publicDir = config_.autoFile === true ? path.join(process.cwd(), config_.autoFileDir || "") : undefined;
272
+ if (publicDir && !fileExists) {
273
+ const staticPath = path.join(publicDir, pathname);
274
+ const safePath = path.resolve(staticPath);
275
+ if (safePath.startsWith(path.resolve(publicDir))) {
276
+ filePath = safePath;
277
+ fileExists = fs.existsSync(safePath) && fs.statSync(safePath).isFile();
278
+ }
279
+ }
280
+ if (pathname.startsWith("/api/") && pagesDir != null && !fileExists) {
281
+ const apiFile = path.join(pagesDir, `${pathname}.ts`);
282
+ const apiFileTsx = path.join(pagesDir, `${pathname}.tsx`);
283
+ filePath = fs.existsSync(apiFile) ? apiFile : fs.existsSync(apiFileTsx) ? apiFileTsx : apiFile;
284
+ fileExists = filePath == apiFileTsx ? fs.existsSync(apiFileTsx) : fs.existsSync(apiFile);
285
+ }
286
+ if (!fileExists) {
287
+ filePath = "File is unresolvable. Could be because it's not found.";
288
+ }
271
289
  res.writeHead(200, { "Content-Type": "application/json" });
272
290
  res.end(JSON.stringify({
273
291
  fileExists,
@@ -402,8 +420,24 @@ async function start() {
402
420
  return res.end(JSON.stringify({ error: e.message }));
403
421
  }
404
422
  }
405
- res.writeHead(404);
406
- return res.end(JSON.stringify({ error: `API route not found: ${pathname}` }));
423
+ const notFoundPath = path.join(__dirname, '404.js');
424
+ if (fs.existsSync(notFoundPath)) {
425
+ try {
426
+ let html = await renderPage(notFoundPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""));
427
+ res.setHeader("Content-Type", "text/html");
428
+ res.writeHead(404);
429
+ html = html.replace("<script>", "<script>\nwindow.__status_code__ = 404;");
430
+ res.end(html);
431
+ }
432
+ catch (e) {
433
+ res.writeHead(404);
434
+ res.end(`<html><head></head><body><center><b>404</b> | Not found: ${url}</center></body></html>`);
435
+ }
436
+ }
437
+ else {
438
+ res.writeHead(404);
439
+ res.end(`<html><head></head><body><center><b>404</b> | Not found: ${url}</center></body></html>`);
440
+ }
407
441
  }
408
442
  const urlPath = pathname === "/" ? "/index" : pathname;
409
443
  const tsxPath = pagesDir ? path.join(pagesDir, `${urlPath}.tsx`) : undefined;
package/dist/devPanel.js CHANGED
@@ -1,6 +1,6 @@
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
- import { ShieldCheck, CircleX, ChevronDown, ChevronUp, Bug, AlertTriangle, Info, ArrowLeft, Wifi, Terminal, Route, Globe, ChevronRight, ChevronLeft, Server, TriangleAlert, Eye } from 'lucide-react';
3
+ import { ShieldCheck, CircleX, ChevronDown, ChevronUp, Bug, AlertTriangle, Info, ArrowLeft, Wifi, Terminal, Route, Globe, ChevronRight, ChevronLeft, Server, TriangleAlert, Eye, Zap } from 'lucide-react';
4
4
  import Prism from 'prismjs';
5
5
  import 'prismjs/components/prism-javascript';
6
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/;
@@ -63,7 +63,7 @@ function Sparkline({ data, color, label, unit }) {
63
63
  return _jsx("circle", { cx: last[0], cy: last[1], r: "2.5", fill: color });
64
64
  })()] }))] })] }));
65
65
  }
66
- function installNetworkInterceptor(onFetch, setFetch, onBytesClient, onBytesServer) {
66
+ function installNetworkInterceptor(onFetch, setFetch, onEventSource, setEventSource, onWebSocket, setWebSocket, onResource, setResource, onBytesClient, onBytesServer) {
67
67
  if (window.__authtics_network_intercepted__)
68
68
  return;
69
69
  window.__authtics_network_intercepted__ = true;
@@ -100,6 +100,142 @@ function installNetworkInterceptor(onFetch, setFetch, onBytesClient, onBytesServ
100
100
  throw e;
101
101
  }
102
102
  };
103
+ const OriginalWebSocket = window.WebSocket;
104
+ window.WebSocket = function (url, protocols) {
105
+ fetchId++;
106
+ const savedId = fetchId;
107
+ const ws = new OriginalWebSocket(url, protocols);
108
+ if (url.includes('__authtics'))
109
+ return ws;
110
+ onWebSocket({
111
+ url,
112
+ status: 'Connecting...',
113
+ id: savedId,
114
+ type: "WebSocket"
115
+ });
116
+ ws.addEventListener('open', () => {
117
+ setWebSocket({
118
+ url,
119
+ status: 'Connected',
120
+ id: savedId,
121
+ type: "WebSocket"
122
+ });
123
+ });
124
+ ws.addEventListener('close', () => {
125
+ setWebSocket({
126
+ url,
127
+ status: 'Disconnected',
128
+ id: savedId,
129
+ type: "WebSocket"
130
+ });
131
+ });
132
+ ws.addEventListener('message', (event) => {
133
+ const bytes = new Blob([event.data]).size;
134
+ onBytesServer(bytes);
135
+ setWebSocket({
136
+ url,
137
+ status: 'Connected',
138
+ id: savedId,
139
+ type: "WebSocket"
140
+ });
141
+ });
142
+ ws.addEventListener('error', () => {
143
+ const status = ws.readyState === OriginalWebSocket.CLOSED
144
+ ? 'Disconnected'
145
+ : 'Connection Failed';
146
+ setWebSocket({
147
+ url,
148
+ status,
149
+ id: savedId,
150
+ type: "WebSocket"
151
+ });
152
+ });
153
+ const OriginalSend = ws.send;
154
+ ws.send = function (data) {
155
+ let bytes = 0;
156
+ if (typeof data === 'string') {
157
+ bytes = new Blob([data]).size;
158
+ }
159
+ else if (data instanceof Blob) {
160
+ bytes = data.size;
161
+ }
162
+ else if (data instanceof ArrayBuffer || data instanceof SharedArrayBuffer) {
163
+ bytes = data.byteLength;
164
+ }
165
+ else if (ArrayBuffer.isView(data)) {
166
+ bytes = data.byteLength;
167
+ }
168
+ onBytesClient(bytes);
169
+ OriginalSend.call(this, data);
170
+ };
171
+ return ws;
172
+ };
173
+ Object.defineProperty(window.WebSocket, 'prototype', {
174
+ value: OriginalWebSocket.prototype,
175
+ writable: false
176
+ });
177
+ Object.assign(window.WebSocket, OriginalWebSocket);
178
+ const OriginalEventSource = window.EventSource;
179
+ window.EventSource = function (url, eventSourceInitDict) {
180
+ fetchId++;
181
+ const savedId = fetchId;
182
+ const es = new OriginalEventSource(url, eventSourceInitDict);
183
+ if (url.includes('__authtics'))
184
+ return es;
185
+ onEventSource({
186
+ url,
187
+ status: 'Connecting...',
188
+ id: savedId,
189
+ type: "EventSource"
190
+ });
191
+ es.addEventListener('open', () => {
192
+ setEventSource({
193
+ url,
194
+ status: 'Connected',
195
+ id: savedId,
196
+ type: "EventSource"
197
+ });
198
+ });
199
+ es.addEventListener('message', (event) => {
200
+ const bytes = new Blob([event.data]).size;
201
+ onBytesServer(bytes);
202
+ setEventSource({
203
+ url,
204
+ status: 'Connected',
205
+ id: savedId,
206
+ type: "EventSource"
207
+ });
208
+ });
209
+ es.addEventListener('error', () => {
210
+ const status = es.readyState === OriginalEventSource.CLOSED
211
+ ? 'Disconnected'
212
+ : 'Connection Failed';
213
+ setEventSource({
214
+ url,
215
+ status: status,
216
+ id: savedId,
217
+ type: "EventSource"
218
+ });
219
+ });
220
+ return es;
221
+ };
222
+ window.EventSource.prototype = OriginalEventSource.prototype;
223
+ Object.assign(window.EventSource, OriginalEventSource);
224
+ const queryImg = document.querySelectorAll('img');
225
+ const queryScript = document.querySelectorAll('script');
226
+ const queryLink = document.querySelectorAll('link');
227
+ queryImg.forEach((img) => {
228
+ if (img.src)
229
+ onResource({ url: img.src, type: 'img', id: fetchId++ });
230
+ });
231
+ queryScript.forEach((script) => {
232
+ if (script.src)
233
+ onResource({ url: script.src, type: 'script', id: fetchId++ });
234
+ });
235
+ queryLink.forEach((link) => {
236
+ if (link.href)
237
+ onResource({ url: link.href, type: 'link', id: fetchId++ });
238
+ });
103
239
  }
104
240
  function installConsoleInterceptor() {
105
241
  if (window.__authtics_intercepted__)
@@ -171,6 +307,9 @@ export default function DevPanel() {
171
307
  const [hasIssue, setHasIssue] = useState(false);
172
308
  const [ping__, setPing__] = useState(0);
173
309
  const [networkFetches, setNetworkFetches] = useState([]);
310
+ const [eventSources, setEventSources] = useState([]);
311
+ const [webSockets, setWebSockets] = useState([]);
312
+ const [resourcesFound, setResourcesFound] = useState([]);
174
313
  const [status, setStatus] = useState(200);
175
314
  const [code, setCode] = useState('');
176
315
  const [pathName, setPathName] = useState(undefined);
@@ -369,11 +508,9 @@ export default function DevPanel() {
369
508
  isSSR();
370
509
  userSettings();
371
510
  getHostVersion();
372
- setInterval(() => {
373
- getUsage();
374
- }, 250);
375
511
  setInterval(() => {
376
512
  update();
513
+ getUsage();
377
514
  }, 500);
378
515
  }
379
516
  }, 250);
@@ -469,7 +606,7 @@ export default function DevPanel() {
469
606
  }, []);
470
607
  useEffect(() => {
471
608
  installConsoleInterceptor();
472
- installNetworkInterceptor((f) => setNetworkFetches(prev => [...prev.slice(-49), f]), (f) => setNetworkFetches(prev => prev.map(p => p.id === f.id ? f : p)), (sent) => {
609
+ 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) => {
473
610
  pendingSent.current += sent;
474
611
  }, (received) => {
475
612
  pendingReceived.current += received;
@@ -733,7 +870,7 @@ export default function DevPanel() {
733
870
  border: '1px solid #eee',
734
871
  fontSize: '12px',
735
872
  color: '#333',
736
- }, children: [_jsx("span", { children: "Ping" }), _jsx("span", { style: { color: ping__ < 100 ? '#16a34a' : ping__ < 300 ? '#d97706' : '#e53e3e', fontFamily: 'monospace' }, children: ping__ > 0 ? `${ping__}ms` : '??ms' })] }), datRestrictionMissing ? (_jsxs("span", { style: { display: 'flex', opacity: 0.5, fontSize: 10, justifyContent: 'center', alignItems: 'center', gap: '4px' }, children: [_jsx(Info, { size: 10 }), "this analytic is unavaliable. Set \"dat\" in the Authtics Host config to set restriction and then restart."] })) : (_jsx(_Fragment, {})), _jsx(Sparkline, { data: receivedHistory, color: "#3b82f6", label: "Received", unit: " bytes" }), _jsx(Sparkline, { data: sentHistory, color: "#16a34a", label: "Sent", unit: " bytes" }), networkFetches.length === 0
873
+ }, children: [_jsx("span", { children: "Ping" }), _jsx("span", { style: { color: ping__ < 100 ? '#16a34a' : ping__ < 300 ? '#d97706' : '#e53e3e', fontFamily: 'monospace' }, children: ping__ > 0 ? `${ping__}ms` : '??ms' })] }), datRestrictionMissing ? (_jsxs("span", { style: { display: 'flex', opacity: 0.5, fontSize: 10, justifyContent: 'center', alignItems: 'center', gap: '4px' }, children: [_jsx(Info, { size: 10 }), "this analytic is unavaliable. Set \"dat\" in the Authtics Host config to set restriction and then restart."] })) : (_jsx(_Fragment, {})), _jsx(Sparkline, { data: receivedHistory, color: "#3b82f6", label: "Received", unit: " bytes" }), _jsx(Sparkline, { data: sentHistory, color: "#16a34a", label: "Sent", unit: " bytes" }), _jsxs("span", { style: { fontSize: '15px', color: '#333', fontWeight: 'bold' }, children: ["Fetches \u2014 ", networkFetches.length, " total fetches"] }), networkFetches.length === 0
737
874
  ? _jsx("div", { style: { color: '#aaa', fontSize: '12px', padding: '4px 2px' }, children: "No network requests yet." })
738
875
  : [...networkFetches].reverse().map((f, i) => (_jsxs("div", { style: {
739
876
  background: '#fff',
@@ -753,7 +890,25 @@ export default function DevPanel() {
753
890
  obj___.innerText = f.response?.slice(0, 100) + "...";
754
891
  obj___.removeAttribute('expanded');
755
892
  e.currentTarget.innerText = `View more (${f.response ? (f.response.split('').length - 100) : 0} bytes hidden)`;
756
- } }, style: { padding: '8px 10px', borderRadius: '10px', background: '#34a8eb', color: '#fff', border: 'none', cursor: 'pointer', margin: '10px', textAlign: 'center', fontFamily: 'system-ui, sans-serif' }, children: ["View more (", f.response ? (f.response.split('').length - 100) : 0, " bytes hidden)"] })) : (_jsx(_Fragment, {}))) : (_jsx(_Fragment, {}))] }, i)))] })] })) : routeOpen ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
893
+ } }, style: { padding: '8px 10px', borderRadius: '10px', background: '#34a8eb', color: '#fff', border: 'none', cursor: 'pointer', margin: '10px', textAlign: 'center', fontFamily: 'system-ui, sans-serif' }, children: ["View more (", f.response ? (f.response.split('').length - 100) : 0, " bytes hidden)"] })) : (_jsx(_Fragment, {}))) : (_jsx(_Fragment, {}))] }, i))), _jsx("span", { style: { fontSize: '15px', color: '#333', fontWeight: 'bold' }, children: "Active connections" }), eventSources.length === 0 ? (_jsx("div", { style: { color: '#aaa', fontSize: '12px', padding: '4px 2px' }, children: "No active connections." })) :
894
+ [...eventSources, ...webSockets].reverse().map((es, i) => (_jsx("div", { style: {
895
+ background: '#fff',
896
+ display: 'flex',
897
+ flexDirection: 'column',
898
+ borderRadius: '10px',
899
+ border: '1px solid #eee',
900
+ fontSize: '11px',
901
+ fontFamily: 'monospace'
902
+ }, children: _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', gap: '8px', padding: '6px 10px' }, children: [_jsxs("span", { style: { color: '#3b82f6', display: 'flex', gap: '4px', justifyContent: 'center', alignItems: 'center' }, children: [es.type === "EventSource" ? _jsx(Zap, { color: "green", size: 12 }) : es.type === "WebSocket" ? _jsx(Globe, { color: "green", size: 12 }) : _jsx(_Fragment, {}), es.type === "EventSource" ? "Event Source" : es.type === "WebSocket" ? "Web Socket" : "???"] }), _jsx("span", { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', color: '#333' }, children: es.url }), _jsx("span", { style: { color: es.status === 'Connected' ? '#16a34a' : es.status === 'Disconnected' ? '#e53e3e' : 'orange' }, children: es.status === 'Connected' ? 'Active' : es.status === 'Disconnected' ? 'Disconnected' : es.status === 'Connecting...' ? '...' : es.status === 'Connection Failed' ? 'Failed' : 'Unknown' })] }) }, i))), _jsxs("span", { style: { fontSize: '15px', color: '#333', fontWeight: 'bold' }, children: ["Resources Found \u2014 ", resourcesFound.length, " total resources"] }), resourcesFound.length === 0 ? (_jsx("div", { style: { color: '#aaa', fontSize: '12px', padding: '4px 2px' }, children: "No resources found." })) :
903
+ [...resourcesFound].reverse().map((r, i) => (_jsx("div", { style: {
904
+ background: '#fff',
905
+ display: 'flex',
906
+ flexDirection: 'column',
907
+ borderRadius: '10px',
908
+ border: '1px solid #eee',
909
+ fontSize: '11px',
910
+ fontFamily: 'monospace'
911
+ }, children: _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', gap: '8px', padding: '6px 10px' }, children: [_jsx("span", { style: { color: '#3b82f6', display: 'flex', gap: '4px', justifyContent: 'center', alignItems: 'center' }, children: r.type }), _jsx("span", { style: { flex: 1, overflow: 'hidden', color: '#333' }, children: r.url })] }) }, i)))] })] })) : routeOpen ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
757
912
  padding: '6px 8px',
758
913
  borderBottom: '1px solid #ddd',
759
914
  background: '#fff',
@@ -162,23 +162,23 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
162
162
  }
163
163
  if (!clientCode || !serverCode) {
164
164
  const virtualServer = `
165
- import Page from ${JSON.stringify(tsxPath)};
166
165
  ${usesDevPanel ? `import DevPanel from ${JSON.stringify(devPanelPath)};` : ""}
166
+ import Page from ${JSON.stringify(tsxPath)};
167
167
  export default Page;
168
168
  ${usesDevPanel ? `export { DevPanel };` : ""}
169
169
  `;
170
170
  const virtualClient = `
171
171
  import React from "react";
172
172
  import { hydrateRoot } from "react-dom/client";
173
- import Page from ${JSON.stringify(tsxPath)};
174
173
  ${usesDevPanel ? `import DevPanel from ${JSON.stringify(devPanelPath)};` : ""}
174
+ import Page from ${JSON.stringify(tsxPath)};
175
175
 
176
- const rootEl = document.getElementById("root");
177
- if (rootEl) hydrateRoot(rootEl, React.createElement(Page));
178
176
  ${usesDevPanel ? `
179
177
  const panelEl = document.getElementById("authtics-dev-panel-root");
180
178
  if (panelEl) hydrateRoot(panelEl, React.createElement(DevPanel));
181
179
  ` : ""}
180
+ const rootEl = document.getElementById("root");
181
+ if (rootEl) hydrateRoot(rootEl, React.createElement(Page));
182
182
  `;
183
183
  if (compiler === 'babel') {
184
184
  serverCode = await buildWithBabel(tsxPath, virtualServer, 'node');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananacool467/authtics-host",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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",
@@ -46,8 +46,6 @@
46
46
  "@babel/preset-env": "^7.29.0",
47
47
  "@babel/preset-react": "^7.28.5",
48
48
  "@babel/preset-typescript": "^7.28.5",
49
- "@types/babel__core": "^7.20.5",
50
- "@types/babel__preset-env": "^7.10.0",
51
49
  "esbuild": "^0.27.3",
52
50
  "jiti": "^2.6.1",
53
51
  "lucide-react": "^0.577.0",
@@ -63,6 +61,8 @@
63
61
  "@types/prismjs": "^1.26.6",
64
62
  "@types/react": "^19.2.14",
65
63
  "@types/react-dom": "^19.2.3",
66
- "@types/react-syntax-highlighter": "^15.5.13"
64
+ "@types/react-syntax-highlighter": "^15.5.13",
65
+ "@types/babel__core": "^7.20.5",
66
+ "@types/babel__preset-env": "^7.10.0"
67
67
  }
68
68
  }