@bananacool467/authtics-host 0.2.5 → 0.2.6
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 +20 -36
- package/dist/cli.js +25 -15
- package/dist/config.d.ts +3 -1
- package/dist/config.js +6 -0
- package/dist/devPanel.js +65 -11
- package/dist/renderEngine.d.ts +4 -1
- package/dist/renderEngine.js +187 -34
- package/package.json +16 -11
package/README.md
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
# Authtics Host
|
|
2
2
|
|
|
3
|
-
Version 0.2.
|
|
3
|
+
Version 0.2.6, License: MIT
|
|
4
4
|
|
|
5
|
-
Authtics Host is a web framework inspired by Next.js. Authtics Host supports SSR (Server-Side Rendering) and uses
|
|
5
|
+
Authtics Host is a web framework inspired by Next.js. Authtics Host supports SSR (Server-Side Rendering), SSG (Static Site Generation), CSR (Client-Side Rendering) and SRTWS (Site Rendering Through WebSockets) and uses **Esbuild**. This is not to be confused with auth platforms.
|
|
6
6
|
|
|
7
7
|
**(NOTE: The short name for Authtics Host is HostJS or Host.js)**
|
|
8
8
|
|
|
9
|
-
**(From the Developer: I am having 1 small issue:**
|
|
10
|
-
|
|
11
|
-
1. **Web Socket Connections** are **not showing** in the DevPanel's Network tab
|
|
12
|
-
|
|
13
|
-
**I need help fixing that)**
|
|
14
|
-
|
|
15
9
|
## Setting Up
|
|
16
10
|
|
|
17
11
|
To get **Authtics Host** use:
|
|
@@ -51,7 +45,7 @@ import { defineConfig } from "@bananacool467/authtics-host";
|
|
|
51
45
|
|
|
52
46
|
export default defineConfig({
|
|
53
47
|
port: 8000,
|
|
54
|
-
|
|
48
|
+
renderType: 'ssr',
|
|
55
49
|
devMode: true,
|
|
56
50
|
autoFile: true,
|
|
57
51
|
autoPage: true,
|
|
@@ -94,7 +88,7 @@ import { defineConfig } from "@bananacool467/authtics-host";
|
|
|
94
88
|
|
|
95
89
|
export default defineConfig({
|
|
96
90
|
port: 8000,
|
|
97
|
-
|
|
91
|
+
renderType: 'ssr',
|
|
98
92
|
devMode: true,
|
|
99
93
|
autoFile: false,
|
|
100
94
|
autoPage: false
|
|
@@ -154,7 +148,7 @@ Then when you run your project, you will get a page just saying **"Hello, World!
|
|
|
154
148
|
|
|
155
149
|
## About The Developer Panel
|
|
156
150
|
|
|
157
|
-
The Developer Panel is what you could use to help you debug your code. The Developer Panel has **
|
|
151
|
+
The Developer Panel is what you could use to help you debug your code. The Developer Panel has **8 tabs**:
|
|
158
152
|
|
|
159
153
|
* **Console** — Shows current logs.
|
|
160
154
|
* **Route** — Shows information about the current route.
|
|
@@ -163,6 +157,7 @@ The Developer Panel is what you could use to help you debug your code. The Devel
|
|
|
163
157
|
* **Client Info** — Shows information about your client's User Agent, OS **(Operating System)** and FPS **(Frames Per Second)**.
|
|
164
158
|
* **About Host** — Shows the current version of *Authtics Host*.
|
|
165
159
|
* **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
|
+
* **Panel Customization** — Customize the dev panel.
|
|
166
161
|
|
|
167
162
|
The information the Developer Panel shows outside of the tabs is just your **DAT** (Developer Access Token).
|
|
168
163
|
|
|
@@ -186,6 +181,17 @@ and then create a file like `paused.tsx` and then inside `App.tsx`, import `Paus
|
|
|
186
181
|
```
|
|
187
182
|
and if your using `autoPaging` then you just create a file named `_paused.tsx`.
|
|
188
183
|
|
|
184
|
+
## Page Patching
|
|
185
|
+
|
|
186
|
+
Pages may fail to render in SSR if it uses `window` instead of assigning it to a **useState** while in a **useEffect** in *React*
|
|
187
|
+
|
|
188
|
+
You can fix this by adding this to your config file:
|
|
189
|
+
```json
|
|
190
|
+
"patchForSsr": true
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
It makes a fake `window`, `document`, etc for SSR when building for Node. Which fixes the issue.
|
|
194
|
+
|
|
189
195
|
## App APIs
|
|
190
196
|
|
|
191
197
|
You can add APIs like `get`, `post`, `del` and `put`.
|
|
@@ -203,32 +209,10 @@ Hello!
|
|
|
203
209
|
|
|
204
210
|
## Notes about Authtics Host
|
|
205
211
|
|
|
206
|
-
|
|
207
|
-
The developer (The one that is writing this README) may add a feature where Authtics Host converts:
|
|
208
|
-
```tsx
|
|
209
|
-
export function Component() {
|
|
210
|
-
return (
|
|
211
|
-
<p>{window.__status_code__}</p>
|
|
212
|
-
)
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
To:
|
|
216
|
-
```tsx
|
|
217
|
-
import { useState, useEffect } from 'react';
|
|
218
|
-
export function Component() {
|
|
219
|
-
const [__window, __set_window] = useState<any>(null);
|
|
220
|
-
useEffect(() => {
|
|
221
|
-
if (window) __set_window(window);
|
|
222
|
-
}, []);
|
|
223
|
-
return (
|
|
224
|
-
<p>{__window.__status_code__}</p>
|
|
225
|
-
)
|
|
226
|
-
}
|
|
227
|
-
```
|
|
212
|
+
The developer might redesign the UI for the DevPanel using [UI Tools](https://npmjs.com/package/@bananakitssu/ui-tools).
|
|
228
213
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
And also maybe, add Developer Panel customization.
|
|
214
|
+
> **Warning**:
|
|
215
|
+
> SRTWS may not work properly. It may render pages, but not run scripts.
|
|
232
216
|
|
|
233
217
|
> **Warning**:
|
|
234
218
|
> 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**
|
package/dist/cli.js
CHANGED
|
@@ -4,12 +4,14 @@ import { createJiti } from "jiti";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import url from "node:url";
|
|
7
|
-
import { renderPage } from "./renderEngine.js";
|
|
7
|
+
import { renderPage, handleSrtwsConnection } from "./renderEngine.js";
|
|
8
|
+
import { tempFileForTsx } from "./rendererTempFiles.js";
|
|
8
9
|
let config_ = {};
|
|
9
10
|
let uptime = 0;
|
|
10
11
|
let totalRequests = 0;
|
|
11
12
|
let users = 0;
|
|
12
13
|
let usersPaused = false;
|
|
14
|
+
let srtws;
|
|
13
15
|
const jiti = createJiti(import.meta.url);
|
|
14
16
|
const routes = new Map();
|
|
15
17
|
const __filename = url.fileURLToPath(import.meta.url);
|
|
@@ -277,7 +279,7 @@ export default class App {
|
|
|
277
279
|
});
|
|
278
280
|
}
|
|
279
281
|
});
|
|
280
|
-
this.post("/__authtics_host_apis__/get-
|
|
282
|
+
this.post("/__authtics_host_apis__/get-render-type", async (req, res) => {
|
|
281
283
|
let datMatched = false;
|
|
282
284
|
if (config_.dat && config_.dat == req.dat)
|
|
283
285
|
datMatched = true;
|
|
@@ -286,7 +288,7 @@ export default class App {
|
|
|
286
288
|
if (datMatched) {
|
|
287
289
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
288
290
|
res.end(JSON.stringify({
|
|
289
|
-
|
|
291
|
+
renderType: config_.renderType
|
|
290
292
|
}));
|
|
291
293
|
}
|
|
292
294
|
});
|
|
@@ -421,6 +423,7 @@ async function start() {
|
|
|
421
423
|
catch {
|
|
422
424
|
}
|
|
423
425
|
const PORT = config.port ?? 3000;
|
|
426
|
+
const renderType = config.renderType ?? 'ssr';
|
|
424
427
|
const showPanel = config.devMode !== false;
|
|
425
428
|
const autoPaging = config.autoPage !== false;
|
|
426
429
|
const compiler = 'esbuild';
|
|
@@ -565,7 +568,7 @@ async function start() {
|
|
|
565
568
|
res.setHeader("Content-Type", "application/javascript");
|
|
566
569
|
res.end(f);
|
|
567
570
|
});
|
|
568
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.dat, config_.dat);
|
|
571
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
569
572
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
570
573
|
res.setHeader("Content-Type", "text/html");
|
|
571
574
|
return res.end(html);
|
|
@@ -588,7 +591,7 @@ async function start() {
|
|
|
588
591
|
res.setHeader("Content-Type", "application/javascript");
|
|
589
592
|
res.end(f);
|
|
590
593
|
});
|
|
591
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
594
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType);
|
|
592
595
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
593
596
|
res.setHeader("Content-Type", "text/html");
|
|
594
597
|
return res.end(html);
|
|
@@ -618,7 +621,7 @@ async function start() {
|
|
|
618
621
|
res.setHeader("Content-Type", "application/javascript");
|
|
619
622
|
res.end(f);
|
|
620
623
|
});
|
|
621
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
624
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
622
625
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
623
626
|
res.setHeader("Content-Type", "text/html");
|
|
624
627
|
return res.end(html);
|
|
@@ -641,7 +644,7 @@ async function start() {
|
|
|
641
644
|
res.setHeader("Content-Type", "application/javascript");
|
|
642
645
|
res.end(f);
|
|
643
646
|
});
|
|
644
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
647
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType);
|
|
645
648
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
646
649
|
res.setHeader("Content-Type", "text/html");
|
|
647
650
|
return res.end(html);
|
|
@@ -682,6 +685,9 @@ async function start() {
|
|
|
682
685
|
req.on('close', () => { hmrClients.delete(res); users--; });
|
|
683
686
|
return;
|
|
684
687
|
}
|
|
688
|
+
if (pathname === '/__authtics_srtws__') {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
685
691
|
if (Array.isArray(config_.dat) && !config_.dat.includes(req.dat) && usersPaused) {
|
|
686
692
|
if (autoPaging) {
|
|
687
693
|
const pausedTsxFile = pagesDir ? path.join(pagesDir, "_paused.tsx") : undefined;
|
|
@@ -692,7 +698,7 @@ async function start() {
|
|
|
692
698
|
res.setHeader("Content-Type", "application/javascript");
|
|
693
699
|
res.end(f);
|
|
694
700
|
});
|
|
695
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
701
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
696
702
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
697
703
|
res.setHeader("Content-Type", "text/html");
|
|
698
704
|
return res.end(html);
|
|
@@ -715,7 +721,7 @@ async function start() {
|
|
|
715
721
|
res.setHeader("Content-Type", "application/javascript");
|
|
716
722
|
res.end(f);
|
|
717
723
|
});
|
|
718
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
724
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
719
725
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
720
726
|
res.setHeader("Content-Type", "text/html");
|
|
721
727
|
return res.end(html);
|
|
@@ -745,7 +751,7 @@ async function start() {
|
|
|
745
751
|
res.setHeader("Content-Type", "application/javascript");
|
|
746
752
|
res.end(f);
|
|
747
753
|
});
|
|
748
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
754
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
749
755
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
750
756
|
res.setHeader("Content-Type", "text/html");
|
|
751
757
|
return res.end(html);
|
|
@@ -768,7 +774,7 @@ async function start() {
|
|
|
768
774
|
res.setHeader("Content-Type", "application/javascript");
|
|
769
775
|
res.end(f);
|
|
770
776
|
});
|
|
771
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
777
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
772
778
|
html = html.replace("<script>", "<script>\nwindow.__paused__ = true;");
|
|
773
779
|
res.setHeader("Content-Type", "text/html");
|
|
774
780
|
return res.end(html);
|
|
@@ -826,7 +832,7 @@ async function start() {
|
|
|
826
832
|
res.setHeader("Content-Type", "application/javascript");
|
|
827
833
|
res.end(f);
|
|
828
834
|
});
|
|
829
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
835
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
830
836
|
res.setHeader("Content-Type", "text/html");
|
|
831
837
|
res.writeHead(404);
|
|
832
838
|
html = html.replace("<script>", "<script>\nwindow.__status_code__ = 404;");
|
|
@@ -845,6 +851,10 @@ async function start() {
|
|
|
845
851
|
const urlPath = pathname === "/" ? "/index" : pathname;
|
|
846
852
|
const tsxPath = pagesDir ? path.join(pagesDir, `${urlPath}.tsx`) : undefined;
|
|
847
853
|
const htmlPath = pagesDir ? path.join(pagesDir, `${urlPath}.html`) : undefined;
|
|
854
|
+
if (fs.existsSync(tempFileForTsx(tsxPath || ""))) {
|
|
855
|
+
const { default: PageComponent } = await import(tempFileForTsx(tsxPath || "").replace(/\.json$/, ".server.cjs"));
|
|
856
|
+
srtws = handleSrtwsConnection(server, PageComponent);
|
|
857
|
+
}
|
|
848
858
|
if (tsxPath && fs.existsSync(tsxPath)) {
|
|
849
859
|
try {
|
|
850
860
|
const html = await renderPage(tsxPath, url, actuallyShowPanel, compiler, hotReload, autoPaging, config.autoPageDir, (routes.get("APP_TSX") || ""), (f, p) => {
|
|
@@ -852,7 +862,7 @@ async function start() {
|
|
|
852
862
|
res.setHeader("Content-Type", "application/javascript");
|
|
853
863
|
res.end(f);
|
|
854
864
|
});
|
|
855
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
865
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
856
866
|
res.setHeader("Content-Type", "text/html");
|
|
857
867
|
return res.end(html);
|
|
858
868
|
}
|
|
@@ -876,7 +886,7 @@ async function start() {
|
|
|
876
886
|
res.setHeader("Content-Type", "application/javascript");
|
|
877
887
|
res.end(f);
|
|
878
888
|
});
|
|
879
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
889
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
880
890
|
res.setHeader("Content-Type", "text/html");
|
|
881
891
|
return res.end(html);
|
|
882
892
|
}
|
|
@@ -895,7 +905,7 @@ async function start() {
|
|
|
895
905
|
res.setHeader("Content-Type", "application/javascript");
|
|
896
906
|
res.end(f);
|
|
897
907
|
});
|
|
898
|
-
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat);
|
|
908
|
+
}, (p) => { routes.delete(`GET:${p}`); }, req.cookies.dat, config_.dat, config_.renderType, config_.patchForSsr);
|
|
899
909
|
res.setHeader("Content-Type", "text/html");
|
|
900
910
|
res.writeHead(404);
|
|
901
911
|
html = html.replace("<script>", "<script>\nwindow.__status_code__ = 404;");
|
package/dist/config.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export interface AuthticsConfig {
|
|
2
2
|
port?: number;
|
|
3
3
|
ssr?: boolean;
|
|
4
|
+
renderType?: 'ssr' | 'csr' | 'ssg' | 'srtws';
|
|
4
5
|
dir?: string;
|
|
5
6
|
devMode?: boolean;
|
|
6
|
-
compilerType?: 'esbuild'
|
|
7
|
+
compilerType?: 'esbuild';
|
|
7
8
|
hotReload?: boolean;
|
|
8
9
|
dat?: string | string[];
|
|
9
10
|
showError?: boolean;
|
|
@@ -12,5 +13,6 @@ export interface AuthticsConfig {
|
|
|
12
13
|
autoFile?: boolean;
|
|
13
14
|
autoFileDir?: string;
|
|
14
15
|
hasPausedRoute?: boolean;
|
|
16
|
+
patchForSsr?: boolean;
|
|
15
17
|
}
|
|
16
18
|
export declare function defineConfig(config: AuthticsConfig): AuthticsConfig;
|
package/dist/config.js
CHANGED
|
@@ -7,8 +7,14 @@ export function defineConfig(config) {
|
|
|
7
7
|
config.showError = true;
|
|
8
8
|
if (config.compilerType == undefined)
|
|
9
9
|
config.compilerType = 'esbuild';
|
|
10
|
+
if (config.patchForSsr === undefined)
|
|
11
|
+
config.patchForSsr = false;
|
|
10
12
|
if (config.ssr == undefined)
|
|
11
13
|
config.ssr = true;
|
|
14
|
+
else
|
|
15
|
+
console.warn("Using \"ssr\" in Authtics Host Config is deprecated. Use \"renderType\" instead.");
|
|
16
|
+
if (config.renderType == undefined)
|
|
17
|
+
config.renderType = config.ssr ? 'ssr' : 'csr';
|
|
12
18
|
if (config.port == undefined || config.port == 0)
|
|
13
19
|
config.port = 3000;
|
|
14
20
|
if (config.devMode == 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 { useState, useEffect, useRef } from 'react';
|
|
3
|
-
import { ShieldCheck, CircleX, ChevronDown, ChevronUp, Bug, AlertTriangle, Info, ArrowLeft, Wifi, Terminal, Route, Globe, ChevronRight, ChevronLeft, Server, TriangleAlert, Eye, Zap, Power } from 'lucide-react';
|
|
3
|
+
import { ShieldCheck, CircleX, ChevronDown, ChevronUp, Bug, AlertTriangle, Info, ArrowLeft, Wifi, Terminal, Route, Globe, ChevronRight, ChevronLeft, Server, Cog, TriangleAlert, Eye, Zap, Power, Check as Tick } from 'lucide-react';
|
|
4
4
|
function getCallerSource() {
|
|
5
5
|
try {
|
|
6
6
|
const stack = new Error().stack ?? '';
|
|
@@ -349,8 +349,13 @@ export default function DevPanel() {
|
|
|
349
349
|
const [clientInfoOpen, setClientInfoOpen] = useState(false);
|
|
350
350
|
const [aboutHostOpen, setAboutHostOpen] = useState(false);
|
|
351
351
|
const [serverInfoOpen, setServerInfoOpen] = useState(false);
|
|
352
|
+
const [panelCustomOpen, setPanelCustomOpen] = useState(false);
|
|
352
353
|
const [shutdown, setShutdown] = useState(false);
|
|
353
354
|
const [shuttingDown, setShuttingDown] = useState(false);
|
|
355
|
+
const [panelColor, setPanelColor] = useState({
|
|
356
|
+
background: '#111',
|
|
357
|
+
border: '#333'
|
|
358
|
+
});
|
|
354
359
|
const [hasIssue, setHasIssue] = useState(false);
|
|
355
360
|
const [ping__, setPing__] = useState(0);
|
|
356
361
|
const [networkFetches, setNetworkFetches] = useState([]);
|
|
@@ -363,7 +368,7 @@ export default function DevPanel() {
|
|
|
363
368
|
const [hostName, setHostName] = useState(undefined);
|
|
364
369
|
const [protocol, setProtocol] = useState(undefined);
|
|
365
370
|
const [path, setPath] = useState(undefined);
|
|
366
|
-
const [
|
|
371
|
+
const [renderType, setRenderType] = useState("???");
|
|
367
372
|
const datRestrictionMissingRef = useRef(true);
|
|
368
373
|
const cpuUsage = useRef(0);
|
|
369
374
|
const heapUsage = useRef(0);
|
|
@@ -465,15 +470,15 @@ export default function DevPanel() {
|
|
|
465
470
|
}
|
|
466
471
|
return "";
|
|
467
472
|
};
|
|
468
|
-
const
|
|
469
|
-
const resp = await fetch('/__authtics_host_apis__/get-
|
|
473
|
+
const getRenderType = async () => {
|
|
474
|
+
const resp = await fetch('/__authtics_host_apis__/get-render-type', {
|
|
470
475
|
method: 'POST',
|
|
471
476
|
headers: { 'Content-Type': 'application/json' },
|
|
472
477
|
body: JSON.stringify({})
|
|
473
478
|
});
|
|
474
479
|
if (resp.status === 200) {
|
|
475
480
|
const data = await resp.json();
|
|
476
|
-
|
|
481
|
+
setRenderType(data.renderType);
|
|
477
482
|
}
|
|
478
483
|
};
|
|
479
484
|
const getUsage = async () => {
|
|
@@ -561,7 +566,7 @@ export default function DevPanel() {
|
|
|
561
566
|
getPath();
|
|
562
567
|
getCompilerType();
|
|
563
568
|
getHttpVersion();
|
|
564
|
-
|
|
569
|
+
getRenderType();
|
|
565
570
|
userSettings();
|
|
566
571
|
getHostVersion();
|
|
567
572
|
setInterval(() => {
|
|
@@ -683,6 +688,16 @@ export default function DevPanel() {
|
|
|
683
688
|
};
|
|
684
689
|
try {
|
|
685
690
|
const existingToken = cookie.get('dat');
|
|
691
|
+
const panelColor = cookie.get('panelColor');
|
|
692
|
+
if (panelColor) {
|
|
693
|
+
setPanelColor({
|
|
694
|
+
background: panelColor.split('|')[0],
|
|
695
|
+
border: panelColor.split('|')[1]
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
cookie.set('panelColor', '#111|#333');
|
|
700
|
+
}
|
|
686
701
|
if (existingToken) {
|
|
687
702
|
setToken(existingToken);
|
|
688
703
|
let length_ = existingToken?.split('');
|
|
@@ -1056,7 +1071,7 @@ export default function DevPanel() {
|
|
|
1056
1071
|
border: '1px solid #eee',
|
|
1057
1072
|
fontSize: '12px',
|
|
1058
1073
|
color: '#333',
|
|
1059
|
-
}, children: [_jsx("span", { style: { fontSize: '12px', color: '#333' }, children: "Renderer Type" }), _jsx("span", { style: { fontSize: '12px', color: 'blue' }, children:
|
|
1074
|
+
}, children: [_jsx("span", { style: { fontSize: '12px', color: '#333' }, children: "Renderer Type" }), _jsx("span", { style: { fontSize: '12px', color: 'blue' }, children: renderType === "ssr" ? 'SSR (Server-Side Rendering)' : renderType === "csr" ? 'CSR (Client-Side Rendering)' : renderType === "ssg" ? 'SSG (Static Site Generation)' : '???' })] }), _jsxs("div", { style: {
|
|
1060
1075
|
display: 'flex',
|
|
1061
1076
|
justifyContent: 'space-between',
|
|
1062
1077
|
padding: '6px 10px',
|
|
@@ -1099,7 +1114,46 @@ export default function DevPanel() {
|
|
|
1099
1114
|
border: '1px solid #eee',
|
|
1100
1115
|
fontSize: '12px',
|
|
1101
1116
|
color: '#333',
|
|
1102
|
-
}, children: [_jsx("span", { style: { fontSize: '12px', color: '#333' }, children: "OS" }), _jsx("span", { style: { fontSize: '12px', color: 'blue' }, children: getOS() })] }), _jsx(Sparkline, { data: fpsHistory, color: frames.current < 30 ? '#e53e3e' : frames.current < 60 ? '#d97706' : '#16a34a', label: "FPS", unit: " frames" })] })] })) :
|
|
1117
|
+
}, children: [_jsx("span", { style: { fontSize: '12px', color: '#333' }, children: "OS" }), _jsx("span", { style: { fontSize: '12px', color: 'blue' }, children: getOS() })] }), _jsx(Sparkline, { data: fpsHistory, color: frames.current < 30 ? '#e53e3e' : frames.current < 60 ? '#d97706' : '#16a34a', label: "FPS", unit: " frames" })] })] })) : panelCustomOpen && !shutdown ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
|
|
1118
|
+
padding: '6px 8px',
|
|
1119
|
+
borderBottom: '1px solid #ddd',
|
|
1120
|
+
background: '#fff',
|
|
1121
|
+
display: 'flex',
|
|
1122
|
+
alignItems: 'center',
|
|
1123
|
+
gap: '6px',
|
|
1124
|
+
}, children: [_jsxs(PanelButton, { onClick: () => setPanelCustomOpen(false), style: { color: '#555' }, children: [_jsx(ArrowLeft, { size: 12 }), " Back"] }), _jsxs("span", { style: { fontSize: '12px', display: 'flex', alignItems: 'center', gap: '4px', color: '#333' }, children: [_jsx(Cog, { size: 12, color: "#3b82f6" }), " Panel Configuration"] })] }), _jsxs("div", { style: { overflowY: 'auto', maxHeight: '260px', padding: '10px', display: 'flex', flexDirection: 'column', gap: '8px' }, children: [_jsx("span", { style: { fontSize: '12px', color: '#333' }, children: "Colors" }), _jsxs("div", { style: {
|
|
1125
|
+
display: 'flex',
|
|
1126
|
+
justifyContent: 'space-between',
|
|
1127
|
+
padding: '6px 10px',
|
|
1128
|
+
background: '#fff',
|
|
1129
|
+
borderRadius: '10px',
|
|
1130
|
+
border: '1px solid #eee',
|
|
1131
|
+
alignItems: 'center'
|
|
1132
|
+
}, children: [_jsxs("div", { style: { background: '#16a34a', width: '50px', height: '50px', borderRadius: '100px', cursor: 'pointer', alignItems: 'center', textAlign: 'center', justifyContent: 'center', flexDirection: 'column', display: 'flex', border: '1px solid #15803d', gap: '8px' }, onClick: () => {
|
|
1133
|
+
setPanelColor({
|
|
1134
|
+
background: '#16a34a',
|
|
1135
|
+
border: '#15803d'
|
|
1136
|
+
});
|
|
1137
|
+
cookie.set('panelColor', '#16a34a|#15803d');
|
|
1138
|
+
}, children: [panelColor.background === '#16a34a' ? _jsx(Tick, { color: "white", size: 12 }) : _jsx(_Fragment, {}), " ", _jsx("span", { style: { fontSize: '12px', color: '#fff' }, children: "Green" })] }), _jsxs("div", { style: { background: '#111', width: '50px', height: '50px', borderRadius: '100px', cursor: 'pointer', alignItems: 'center', textAlign: 'center', justifyContent: 'center', flexDirection: 'column', display: 'flex', border: '1px solid #333', gap: '8px' }, onClick: () => {
|
|
1139
|
+
setPanelColor({
|
|
1140
|
+
background: '#111',
|
|
1141
|
+
border: '#333'
|
|
1142
|
+
});
|
|
1143
|
+
cookie.set('panelColor', '#111|#333');
|
|
1144
|
+
}, children: [panelColor.background === '#111' ? _jsx(Tick, { color: "white", size: 12 }) : _jsx(_Fragment, {}), " ", _jsx("span", { style: { fontSize: '12px', color: '#fff' }, children: "Black" })] }), _jsxs("div", { style: { background: '#f7c623', width: '50px', height: '50px', borderRadius: '100px', cursor: 'pointer', alignItems: 'center', textAlign: 'center', justifyContent: 'center', flexDirection: 'column', display: 'flex', border: '1px solid #d9ad1e', gap: '8px' }, onClick: () => {
|
|
1145
|
+
setPanelColor({
|
|
1146
|
+
background: '#f7c623',
|
|
1147
|
+
border: '#d9ad1e'
|
|
1148
|
+
});
|
|
1149
|
+
cookie.set('panelColor', '#f7c623|#d9ad1e');
|
|
1150
|
+
}, children: [panelColor.background === '#f7c623' ? _jsx(Tick, { color: "white", size: 12 }) : _jsx(_Fragment, {}), " ", _jsx("span", { style: { fontSize: '12px', color: '#fff' }, children: "Yellow" })] }), _jsxs("div", { style: { background: '#3bb0ff', width: '50px', height: '50px', borderRadius: '100px', cursor: 'pointer', alignItems: 'center', textAlign: 'center', justifyContent: 'center', flexDirection: 'column', display: 'flex', border: '1px solid #3290cf', gap: '8px' }, onClick: () => {
|
|
1151
|
+
setPanelColor({
|
|
1152
|
+
background: '#3bb0ff',
|
|
1153
|
+
border: '#3290cf'
|
|
1154
|
+
});
|
|
1155
|
+
cookie.set('panelColor', '#3bb0ff|#3290cf');
|
|
1156
|
+
}, children: [panelColor.background === '#3bb0ff' ? _jsx(Tick, { color: "white", size: 12 }) : _jsx(_Fragment, {}), " ", _jsx("span", { style: { fontSize: '12px', color: '#fff' }, children: "Blue" })] })] })] })] })) : aboutHostOpen && !shutdown ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
|
|
1103
1157
|
padding: '6px 8px',
|
|
1104
1158
|
borderBottom: '1px solid #ddd',
|
|
1105
1159
|
background: '#fff',
|
|
@@ -1169,7 +1223,7 @@ export default function DevPanel() {
|
|
|
1169
1223
|
border: '1px solid #eee',
|
|
1170
1224
|
fontSize: '12px',
|
|
1171
1225
|
color: '#333',
|
|
1172
|
-
}, children: [_jsx("span", { style: { fontSize: '12px', color: '#333' }, children: "Total current users" }), _jsx("span", { style: { fontSize: '12px', color: 'blue' }, children: users })] }), _jsx(Sparkline, { data: cpuUsageHistory, color: "#3b82f6", label: "CPU Usage", unit: "" }), _jsx(Sparkline, { data: heapUsageHistory, color: "#3b82f6", label: "Heap Used", unit: heapResult })] })] })) : !shutdown ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: { padding: '8px', gap: '4px', display: 'flex', flexDirection: 'column' }, children: [_jsxs(PanelButton, { onClick: () => setConsoleOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Terminal, { size: 12, color: "#16a34a" }), " Console"] }), logs.length > 0 && (_jsx("span", { style: { color: '#111', borderRadius: '100px', padding: '1px 6px', fontSize: '10px' }, children: logs.length }))] }), _jsxs(PanelButton, { onClick: () => setRouteOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Route, { size: 12, color: "#3b82f6" }), " Route"] }), _jsxs("span", { style: { color: '#111', borderRadius: '100px', padding: '1px 6px', fontSize: '10px', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '4px' }, children: [datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" }), pathName] })] }), _jsxs(PanelButton, { onClick: () => setWebpageInfoOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Globe, { size: 12, color: "#3b82f6" }), " Webpage Info"] }), datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" })] }), _jsx(PanelButton, { onClick: () => setNetworkOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: _jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Wifi, { size: 12, color: "#3b82f6" }), " Network"] }) }), _jsxs(PanelButton, { onClick: () => setClientInfoOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Info, { size: 12, color: "#3b82f6" }), " Client Info"] }), datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" })] }), _jsxs(PanelButton, { onClick: () => setAboutHostOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(ShieldCheck, { size: 12, color: "#f472b6" }), " About HostJS"] }), datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" })] }), _jsxs(PanelButton, { onClick: () => setServerInfoOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Server, { size: 12, color: "#f472b6" }), " Server Info"] }), datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" })] })] }), _jsxs("div", { style: {
|
|
1226
|
+
}, children: [_jsx("span", { style: { fontSize: '12px', color: '#333' }, children: "Total current users" }), _jsx("span", { style: { fontSize: '12px', color: 'blue' }, children: users })] }), _jsx(Sparkline, { data: cpuUsageHistory, color: "#3b82f6", label: "CPU Usage", unit: "" }), _jsx(Sparkline, { data: heapUsageHistory, color: "#3b82f6", label: "Heap Used", unit: heapResult })] })] })) : !shutdown ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: { padding: '8px', gap: '4px', display: 'flex', flexDirection: 'column' }, children: [_jsxs(PanelButton, { onClick: () => setConsoleOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Terminal, { size: 12, color: "#16a34a" }), " Console"] }), logs.length > 0 && (_jsx("span", { style: { color: '#111', borderRadius: '100px', padding: '1px 6px', fontSize: '10px' }, children: logs.length }))] }), _jsxs(PanelButton, { onClick: () => setRouteOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Route, { size: 12, color: "#3b82f6" }), " Route"] }), _jsxs("span", { style: { color: '#111', borderRadius: '100px', padding: '1px 6px', fontSize: '10px', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '4px' }, children: [datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" }), pathName] })] }), _jsxs(PanelButton, { onClick: () => setWebpageInfoOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Globe, { size: 12, color: "#3b82f6" }), " Webpage Info"] }), datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" })] }), _jsx(PanelButton, { onClick: () => setPanelCustomOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: _jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Cog, { size: 12, color: "#3b82f6" }), " Panel Customization"] }) }), _jsx(PanelButton, { onClick: () => setNetworkOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: _jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Wifi, { size: 12, color: "#3b82f6" }), " Network"] }) }), _jsxs(PanelButton, { onClick: () => setClientInfoOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Info, { size: 12, color: "#3b82f6" }), " Client Info"] }), datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" })] }), _jsxs(PanelButton, { onClick: () => setAboutHostOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(ShieldCheck, { size: 12, color: "#f472b6" }), " About HostJS"] }), datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" })] }), _jsxs(PanelButton, { onClick: () => setServerInfoOpen(true), fullWidth: true, style: { color: '#333', justifyContent: 'space-between', height: '32px' }, children: [_jsxs("span", { style: { display: 'flex', alignItems: 'center', gap: '6px' }, children: [_jsx(Server, { size: 12, color: "#f472b6" }), " Server Info"] }), datRestrictionMissing && _jsx(TriangleAlert, { size: 12, color: "orange" })] })] }), _jsxs("div", { style: {
|
|
1173
1227
|
padding: '6px 8px',
|
|
1174
1228
|
borderTop: '1px solid #ddd',
|
|
1175
1229
|
background: '#fff',
|
|
@@ -1229,8 +1283,8 @@ export default function DevPanel() {
|
|
|
1229
1283
|
alignItems: 'center',
|
|
1230
1284
|
padding: '8px 12px',
|
|
1231
1285
|
borderRadius: hasIssue ? '100px' : '10px',
|
|
1232
|
-
background: hasIssue ? '#ba4e4e' :
|
|
1233
|
-
border: hasIssue ? '1px solid #991b1b' :
|
|
1286
|
+
background: hasIssue ? '#ba4e4e' : panelColor.background,
|
|
1287
|
+
border: hasIssue ? '1px solid #991b1b' : `1px solid ${panelColor.border ?? '#333'}`,
|
|
1234
1288
|
boxShadow: hasIssue
|
|
1235
1289
|
? '0 4px 20px rgba(220,38,38,0.2)'
|
|
1236
1290
|
: '0 4px 20px rgba(0,0,0,0.4)',
|
package/dist/renderEngine.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export declare function handleSrtwsConnection(server: any, PageComponent: React.ComponentType<any>): boolean;
|
|
1
3
|
export type CompilerType = 'esbuild';
|
|
4
|
+
export type RenderMode = 'ssr' | 'csr' | 'ssg' | 'srtws';
|
|
2
5
|
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, rmvFileHandler?: (p: string) => void, reqDAT?: string | undefined, configDAT?: string | string[] | undefined): Promise<string>;
|
|
6
|
+
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, renderMode?: RenderMode, patchForSsr?: boolean): Promise<string>;
|
package/dist/renderEngine.js
CHANGED
|
@@ -7,6 +7,32 @@ import { renderToString } from "react-dom/server";
|
|
|
7
7
|
import { tempFileForTsx, writeTempFile, readTempFile, ensureTempDirs, } from "./rendererTempFiles.js";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
const customRequire = createRequire(import.meta.url);
|
|
10
|
+
import { WebSocketServer } from "ws";
|
|
11
|
+
export function handleSrtwsConnection(server, PageComponent) {
|
|
12
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
13
|
+
server.on("upgrade", (req, socket, head) => {
|
|
14
|
+
if (req.url === "/__authtics_srtws__") {
|
|
15
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
16
|
+
wss.emit("connection", ws, req);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
wss.on("connection", (ws) => {
|
|
21
|
+
const renderAndSend = () => {
|
|
22
|
+
const pageHtml = renderToString(React.createElement(PageComponent));
|
|
23
|
+
ws.send(JSON.stringify({ type: "DOM", html: pageHtml }));
|
|
24
|
+
};
|
|
25
|
+
ws.on("message", () => {
|
|
26
|
+
try {
|
|
27
|
+
renderAndSend();
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
console.error("[authtics] SRTWS error:", err);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
10
36
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
37
|
const __dirname = path.dirname(__filename);
|
|
12
38
|
const NODE_BUILTINS = [
|
|
@@ -23,6 +49,24 @@ const NODE_BUILTINS = [
|
|
|
23
49
|
'node:module', 'node:process', 'node:string_decoder', 'node:querystring',
|
|
24
50
|
'express', 'jiti',
|
|
25
51
|
];
|
|
52
|
+
const SRTWS_CLIENT_SCRIPT = `<script>
|
|
53
|
+
(function() {
|
|
54
|
+
const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
|
|
55
|
+
const ws = new WebSocket(protocol + window.location.host + '/__authtics_srtws__');
|
|
56
|
+
|
|
57
|
+
ws.onmessage = function(event) {
|
|
58
|
+
const msg = JSON.parse(event.data);
|
|
59
|
+
if (msg.type === 'DOM') {
|
|
60
|
+
const root = document.getElementById('root');
|
|
61
|
+
if (root) root.innerHTML = msg.html;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
ws.onopen = function() {
|
|
66
|
+
ws.send(JSON.stringify({}));
|
|
67
|
+
};
|
|
68
|
+
})();
|
|
69
|
+
</script>`;
|
|
26
70
|
const nodeStubPlugin = {
|
|
27
71
|
name: 'browser-node-stub',
|
|
28
72
|
setup(build) {
|
|
@@ -42,7 +86,7 @@ const nodeStubPlugin = {
|
|
|
42
86
|
}));
|
|
43
87
|
},
|
|
44
88
|
};
|
|
45
|
-
async function buildWithEsbuild(virtualContent, resolveDir, platform) {
|
|
89
|
+
async function buildWithEsbuild(virtualContent, resolveDir, platform, patchForSsr = false) {
|
|
46
90
|
const toFwdSlash = (p) => p.split(path.sep).join('/');
|
|
47
91
|
const isNode = platform === 'node';
|
|
48
92
|
const result = await esbuild.build({
|
|
@@ -56,10 +100,86 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform) {
|
|
|
56
100
|
format: isNode ? 'cjs' : 'iife',
|
|
57
101
|
platform,
|
|
58
102
|
jsx: 'automatic',
|
|
59
|
-
sourcemap:
|
|
103
|
+
sourcemap: false,
|
|
60
104
|
target: isNode ? ['node18'] : ['es2020'],
|
|
61
105
|
minify: true,
|
|
62
106
|
treeShaking: true,
|
|
107
|
+
banner: (isNode && patchForSsr) ? {
|
|
108
|
+
js: `if (typeof globalThis.window === "undefined") {
|
|
109
|
+
const noop = () => {};
|
|
110
|
+
|
|
111
|
+
const createStorageMock = () => {
|
|
112
|
+
let store = {};
|
|
113
|
+
return {
|
|
114
|
+
getItem: (key) => store[key] ?? null,
|
|
115
|
+
setItem: (key, val) => { store[key] = String(val); },
|
|
116
|
+
removeItem: (key) => { delete store[key]; },
|
|
117
|
+
clear: () => { store = {}; },
|
|
118
|
+
key: (i) => Object.keys(store)[i] ?? null,
|
|
119
|
+
get length() { return Object.keys(store).length; },
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const mockLocalStorage = createStorageMock();
|
|
124
|
+
const mockSessionStorage = createStorageMock();
|
|
125
|
+
|
|
126
|
+
const mockElement = {
|
|
127
|
+
setAttribute: noop,
|
|
128
|
+
getAttribute: () => null,
|
|
129
|
+
removeAttribute: noop,
|
|
130
|
+
style: {},
|
|
131
|
+
appendChild: (node) => node,
|
|
132
|
+
removeChild: (node) => node,
|
|
133
|
+
addEventListener: noop,
|
|
134
|
+
removeEventListener: noop,
|
|
135
|
+
classList: { add: noop, remove: noop, toggle: noop, contains: () => false },
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const mockDocument = {
|
|
139
|
+
createElement: () => mockElement,
|
|
140
|
+
querySelector: () => null,
|
|
141
|
+
querySelectorAll: () => [],
|
|
142
|
+
getElementById: () => null,
|
|
143
|
+
getElementsByTagName: () => [],
|
|
144
|
+
getElementsByClassName: () => [],
|
|
145
|
+
cookie: "",
|
|
146
|
+
body: mockElement,
|
|
147
|
+
documentElement: mockElement,
|
|
148
|
+
addEventListener: noop,
|
|
149
|
+
removeEventListener: noop,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const mockNavigator = {
|
|
153
|
+
userAgent: "Mozilla/5.0 (Server-Side)",
|
|
154
|
+
language: "en-US",
|
|
155
|
+
languages: ["en-US"],
|
|
156
|
+
onLine: true,
|
|
157
|
+
clipboard: { writeText: async () => {}, readText: async () => "" },
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
globalThis.document = globalThis.document || mockDocument;
|
|
162
|
+
globalThis.navigator = globalThis.navigator || mockNavigator;
|
|
163
|
+
globalThis.localStorage = globalThis.localStorage || mockLocalStorage;
|
|
164
|
+
globalThis.sessionStorage = globalThis.sessionStorage || mockSessionStorage;
|
|
165
|
+
|
|
166
|
+
globalThis.window = new Proxy(globalThis, {
|
|
167
|
+
get: (target, prop) => {
|
|
168
|
+
if (prop in target) return target[prop];
|
|
169
|
+
if (prop === 'document') return globalThis.document;
|
|
170
|
+
if (prop === 'navigator') return globalThis.navigator;
|
|
171
|
+
if (prop === 'localStorage') return globalThis.localStorage;
|
|
172
|
+
if (prop === 'sessionStorage') return globalThis.sessionStorage;
|
|
173
|
+
if (prop === 'addEventListener' || prop === 'removeEventListener') return noop;
|
|
174
|
+
if (prop === 'location') return { href: '', search: '', pathname: '/', origin: '', assign: noop, replace: noop };
|
|
175
|
+
if (prop === 'matchMedia') return () => ({ matches: false, addEventListener: noop, removeEventListener: noop, dispatchEvent: noop });
|
|
176
|
+
if (prop === 'getComputedStyle') return () => ({ getPropertyValue: () => '' });
|
|
177
|
+
if (prop === 'requestAnimationFrame' || prop === 'cancelAnimationFrame') return (cb) => setTimeout(cb, 0);
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}`
|
|
182
|
+
} : undefined,
|
|
63
183
|
plugins: isNode ? [] : [nodeStubPlugin],
|
|
64
184
|
external: isNode
|
|
65
185
|
? ['react', 'react-dom', 'react-dom/server', 'react/jsx-runtime', 'react/jsx-dev-runtime',
|
|
@@ -108,17 +228,18 @@ function resolveTemplate(appDir, autoPageDir, autoPaging) {
|
|
|
108
228
|
return nextToCli;
|
|
109
229
|
}
|
|
110
230
|
function buildHtml(opts) {
|
|
111
|
-
const { pageHtml, clientCode, devPanelContainer, templatePath, hotReload, fileName, datRestricted, devMode } = opts;
|
|
231
|
+
const { pageHtml, clientCode, devPanelContainer, templatePath, hotReload, fileName, datRestricted, devMode, renderMode } = opts;
|
|
112
232
|
const configScript = `<script>window.__authtics_config__={datRestricted:${datRestricted ? 'true' : 'false'},devMode:${devMode !== false ? 'true' : 'false'}};</script>`;
|
|
113
233
|
const hmrScript = hotReload ? HMR_SCRIPT : '';
|
|
234
|
+
const srtwsScript = renderMode === 'srtws' ? SRTWS_CLIENT_SCRIPT : '';
|
|
114
235
|
if (templatePath && fs.existsSync(templatePath)) {
|
|
115
236
|
let template = fs.readFileSync(templatePath, "utf-8");
|
|
116
237
|
const contentTag = /<authtics_host_content\s*\/>/i;
|
|
117
238
|
if (contentTag.test(template)) {
|
|
118
239
|
template = template.replace(contentTag, `<div id="root">${pageHtml}</div>\n ${devPanelContainer}`);
|
|
119
240
|
template = template.includes("</body>")
|
|
120
|
-
? template.replace("</body>", ` ${configScript}${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}\n${hmrScript}</body>`)
|
|
121
|
-
: template + `\n${configScript}${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}\n${hmrScript}`;
|
|
241
|
+
? template.replace("</body>", ` ${configScript}${srtwsScript}${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}\n${hmrScript}</body>`)
|
|
242
|
+
: template + `\n${configScript}${srtwsScript}${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}\n${hmrScript}`;
|
|
122
243
|
return template;
|
|
123
244
|
}
|
|
124
245
|
console.warn('[authtics] _template.html has no <authtics_host_content /> — using default layout.');
|
|
@@ -133,7 +254,8 @@ function buildHtml(opts) {
|
|
|
133
254
|
<div id="root">${pageHtml}</div>
|
|
134
255
|
${devPanelContainer}
|
|
135
256
|
${configScript}
|
|
136
|
-
${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}
|
|
257
|
+
${renderMode === 'srtws' ? '' : (fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`)}
|
|
258
|
+
${srtwsScript}
|
|
137
259
|
${hmrScript}
|
|
138
260
|
</body>
|
|
139
261
|
</html>`;
|
|
@@ -180,7 +302,7 @@ const HMR_SCRIPT = `<script>(function(){
|
|
|
180
302
|
}
|
|
181
303
|
};
|
|
182
304
|
})();</script>`;
|
|
183
|
-
export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = 'esbuild', hotReload = false, autoPaging = true, autoPageDir, appDir, fileHandler, rmvFileHandler, reqDAT, configDAT) {
|
|
305
|
+
export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = 'esbuild', hotReload = false, autoPaging = true, autoPageDir, appDir, fileHandler, rmvFileHandler, reqDAT, configDAT, renderMode = 'ssr', patchForSsr = false) {
|
|
184
306
|
ensureTempDirs();
|
|
185
307
|
if (!fs.existsSync(tsxPath)) {
|
|
186
308
|
throw new Error(`renderPage: source file not found: ${tsxPath}`);
|
|
@@ -198,6 +320,14 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
|
|
|
198
320
|
const templatePath = resolveTemplate(appDir, autoPageDir, autoPaging);
|
|
199
321
|
const tempPath = tempFileForTsx(tsxPath);
|
|
200
322
|
const serverCjsPath = tempPath.replace(/\.json$/, ".server.cjs");
|
|
323
|
+
const ssgHtmlPath = tempPath.replace(/\.json$/, ".ssg.html");
|
|
324
|
+
if (renderMode === 'ssg' && isCacheValid(tsxPath, ssgHtmlPath, serverCjsPath, templatePath, devPanelPath)) {
|
|
325
|
+
try {
|
|
326
|
+
return fs.readFileSync(ssgHtmlPath, "utf-8");
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
}
|
|
330
|
+
}
|
|
201
331
|
let clientCode;
|
|
202
332
|
let serverCode;
|
|
203
333
|
let freshWrite = false;
|
|
@@ -210,7 +340,8 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
|
|
|
210
340
|
catch {
|
|
211
341
|
}
|
|
212
342
|
}
|
|
213
|
-
|
|
343
|
+
const isCsr = renderMode === 'csr';
|
|
344
|
+
if (!clientCode || (!serverCode && !isCsr)) {
|
|
214
345
|
const virtualServer = `
|
|
215
346
|
${usesDevPanel ? `import DevPanel from ${JSON.stringify(devPanelPath)};` : ""}
|
|
216
347
|
import Page from ${JSON.stringify(tsxPath)};
|
|
@@ -219,49 +350,71 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
|
|
|
219
350
|
`;
|
|
220
351
|
const virtualClient = `
|
|
221
352
|
import React from "react";
|
|
222
|
-
import { hydrateRoot } from "react-dom/client";
|
|
353
|
+
import { ${renderMode === 'csr' ? 'createRoot' : 'hydrateRoot'} } from "react-dom/client";
|
|
223
354
|
${usesDevPanel ? `import DevPanel from ${JSON.stringify(devPanelPath)};` : ""}
|
|
224
355
|
import Page from ${JSON.stringify(tsxPath)};
|
|
225
356
|
|
|
226
357
|
${usesDevPanel ? `
|
|
227
358
|
const panelEl = document.getElementById("authtics-dev-panel-root");
|
|
228
|
-
if (panelEl) hydrateRoot(panelEl, React.createElement(DevPanel))
|
|
359
|
+
if (panelEl) ${renderMode === 'csr' ? `createRoot(panelEl).render(React.createElement(DevPanel));` : `hydrateRoot(panelEl, React.createElement(DevPanel));`}
|
|
229
360
|
` : ""}
|
|
230
361
|
const rootEl = document.getElementById("root");
|
|
231
|
-
if (rootEl)
|
|
362
|
+
if (rootEl) {
|
|
363
|
+
${renderMode === 'csr'
|
|
364
|
+
? `createRoot(rootEl).render(React.createElement(Page));`
|
|
365
|
+
: `hydrateRoot(rootEl, React.createElement(Page));`}
|
|
366
|
+
}
|
|
232
367
|
`;
|
|
233
368
|
if (compiler === 'esbuild') {
|
|
234
|
-
|
|
235
|
-
|
|
369
|
+
if (!isCsr)
|
|
370
|
+
serverCode = await buildWithEsbuild(virtualServer, process.cwd(), 'node', patchForSsr);
|
|
371
|
+
if (renderMode !== 'srtws') {
|
|
372
|
+
clientCode = await buildWithEsbuild(virtualClient, process.cwd(), 'browser');
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
clientCode = "";
|
|
376
|
+
}
|
|
236
377
|
}
|
|
237
|
-
|
|
378
|
+
if (serverCode)
|
|
379
|
+
await fs.promises.writeFile(serverCjsPath, serverCode || "", "utf-8");
|
|
238
380
|
writeTempFile(tempPath, JSON.stringify({ clientCode }));
|
|
239
381
|
freshWrite = true;
|
|
240
382
|
}
|
|
241
383
|
fileHandler ? fileHandler(clientCode || "", `/${path.basename(tempPath).replace(".json", ".js").replace(".tsx_", ".").toLowerCase()}`) : null;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const exports = serverModule.__esModule ? serverModule.default : serverModule;
|
|
246
|
-
const PageComponent = exports?.default?.default ??
|
|
247
|
-
exports?.default ??
|
|
248
|
-
exports?.Page ??
|
|
249
|
-
exports?.page ??
|
|
250
|
-
(typeof exports === 'function' ? exports : null);
|
|
251
|
-
const DevPanelComponent = usesDevPanel ? (exports.DevPanel ?? null) : null;
|
|
252
|
-
if (!PageComponent) {
|
|
253
|
-
throw new Error(`renderPage: no default export found in ${tsxPath}`);
|
|
254
|
-
}
|
|
255
|
-
const pageHtml = renderToString(React.createElement(PageComponent));
|
|
256
|
-
const devPanelHtml = DevPanelComponent
|
|
257
|
-
? renderToString(React.createElement(DevPanelComponent))
|
|
258
|
-
: "";
|
|
259
|
-
const devPanelContainer = usesDevPanel
|
|
260
|
-
? `<div id="authtics-dev-panel-root">${devPanelHtml}</div>`
|
|
384
|
+
let pageHtml = "";
|
|
385
|
+
let devPanelContainer = usesDevPanel
|
|
386
|
+
? `<div id="authtics-dev-panel-root"></div>`
|
|
261
387
|
: "";
|
|
262
|
-
|
|
388
|
+
if (!isCsr) {
|
|
389
|
+
const absoluteServerPath = path.resolve(serverCjsPath);
|
|
390
|
+
delete customRequire.cache[absoluteServerPath];
|
|
391
|
+
const serverModule = customRequire(absoluteServerPath);
|
|
392
|
+
const exports = serverModule.__esModule ? serverModule.default : serverModule;
|
|
393
|
+
const PageComponent = exports?.default?.default ??
|
|
394
|
+
exports?.default ??
|
|
395
|
+
exports?.Page ??
|
|
396
|
+
exports?.page ??
|
|
397
|
+
(typeof exports === 'function' ? exports : null);
|
|
398
|
+
const DevPanelComponent = usesDevPanel ? (exports.DevPanel ?? null) : null;
|
|
399
|
+
if (!PageComponent) {
|
|
400
|
+
throw new Error(`renderPage: no default export found in ${tsxPath}`);
|
|
401
|
+
}
|
|
402
|
+
pageHtml = renderToString(React.createElement(PageComponent));
|
|
403
|
+
const devPanelHtml = DevPanelComponent
|
|
404
|
+
? renderToString(React.createElement(DevPanelComponent))
|
|
405
|
+
: "";
|
|
406
|
+
devPanelContainer = usesDevPanel
|
|
407
|
+
? `<div id="authtics-dev-panel-root">${devPanelHtml}</div>`
|
|
408
|
+
: "";
|
|
409
|
+
}
|
|
410
|
+
const finalHtml = buildHtml({ pageHtml, clientCode: clientCode || "", devPanelContainer, templatePath, hotReload,
|
|
263
411
|
fileName: `/${path.basename(tempPath).replace(".json", ".js").replace(".tsx_", ".").toLowerCase()}`,
|
|
264
412
|
datRestricted: configDAT !== undefined,
|
|
265
413
|
devMode: showPanel,
|
|
414
|
+
renderMode
|
|
266
415
|
});
|
|
416
|
+
if (renderMode === 'ssg') {
|
|
417
|
+
await fs.promises.writeFile(ssgHtmlPath, finalHtml, "utf-8");
|
|
418
|
+
}
|
|
419
|
+
return finalHtml;
|
|
267
420
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananacool467/authtics-host",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Authtics Host, A Next.js-inspired
|
|
3
|
+
"version": "0.2.6",
|
|
4
|
+
"description": "Authtics Host, A Next.js-inspired web framework for Node.js and React with TypeScript and JSX support.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"sideEffects": false,
|
|
9
9
|
"bin": {
|
|
10
|
-
"
|
|
10
|
+
"host": "dist/cli.js"
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
".": "./dist/index.js"
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"host",
|
|
26
26
|
"server-side-rendering",
|
|
27
27
|
"ssr",
|
|
28
|
+
"ssg",
|
|
29
|
+
"csr",
|
|
30
|
+
"srtws",
|
|
28
31
|
"framework",
|
|
29
32
|
"nodejs",
|
|
30
33
|
"typescript",
|
|
@@ -44,26 +47,28 @@
|
|
|
44
47
|
"author": "bananacool467",
|
|
45
48
|
"license": "MIT",
|
|
46
49
|
"dependencies": {
|
|
47
|
-
"esbuild": "^0.
|
|
50
|
+
"esbuild": "^0.28.2",
|
|
48
51
|
"jiti": "^2.6.1",
|
|
49
52
|
"lucide-react": "^0.577.0",
|
|
50
|
-
"prismjs": "^1.30.0"
|
|
53
|
+
"prismjs": "^1.30.0",
|
|
54
|
+
"ws": "^8.21.3"
|
|
51
55
|
},
|
|
52
56
|
"peerDependencies": {
|
|
53
57
|
"react": "^19.2.4",
|
|
54
58
|
"react-dom": "^19.2.4"
|
|
55
59
|
},
|
|
56
60
|
"devDependencies": {
|
|
61
|
+
"@babel/core": "^7.29.0",
|
|
62
|
+
"@babel/preset-env": "^7.29.0",
|
|
63
|
+
"@babel/preset-react": "^7.28.5",
|
|
64
|
+
"@babel/preset-typescript": "^7.28.5",
|
|
65
|
+
"@types/babel__core": "^7.20.5",
|
|
66
|
+
"@types/babel__preset-env": "^7.10.0",
|
|
57
67
|
"@types/node": "^22.13.11",
|
|
58
68
|
"@types/prismjs": "^1.26.6",
|
|
59
69
|
"@types/react": "^19.2.14",
|
|
60
70
|
"@types/react-dom": "^19.2.3",
|
|
61
71
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
62
|
-
"@types/
|
|
63
|
-
"@types/babel__preset-env": "^7.10.0",
|
|
64
|
-
"@babel/core": "^7.29.0",
|
|
65
|
-
"@babel/preset-env": "^7.29.0",
|
|
66
|
-
"@babel/preset-react": "^7.28.5",
|
|
67
|
-
"@babel/preset-typescript": "^7.28.5"
|
|
72
|
+
"@types/ws": "^8.18.1"
|
|
68
73
|
}
|
|
69
74
|
}
|