@bananacool467/authtics-host 0.1.7 → 0.1.9
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 +51 -12
- package/dist/cli.js +74 -33
- package/dist/devPanel.js +20 -16
- package/dist/renderEngine.js +88 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
# Authtics Host
|
|
2
2
|
|
|
3
|
-
Version 0.1.
|
|
3
|
+
Version 0.1.9
|
|
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
|
+
|
|
12
|
+
**(From the Developer: I am having 2 small issues:**
|
|
13
|
+
|
|
14
|
+
1. **Web Socket Connections** are **not showing** in the DevPanel's Network tab
|
|
15
|
+
2. The page doesnt **refresh properly**. (Have to refresh by doing: refresh -> cancel -> refresh to prevent halting)
|
|
16
|
+
|
|
17
|
+
**I need help fixing those)**
|
|
18
|
+
|
|
10
19
|
## Setting Up
|
|
11
20
|
|
|
12
21
|
To get **Authtics Host** use:
|
|
@@ -151,13 +160,13 @@ Then when you run your project, you will get a page just saying **"Hello, World!
|
|
|
151
160
|
|
|
152
161
|
The Developer Panel is what you could use to help you debug your code. The Developer Panel has **7 tabs**:
|
|
153
162
|
|
|
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.
|
|
163
|
+
* **Console** — Shows current logs.
|
|
164
|
+
* **Route** — Shows information about the current route.
|
|
165
|
+
* **Webpage Info** — Shows information about the website/webpage.
|
|
166
|
+
* **Network** — Shows the current ping and shows sent fetches from the client and shows received from the server after fetching.
|
|
167
|
+
* **Client Info** — Shows information about your client's User Agent, OS **(Operating System)** and FPS **(Frames Per Second)**.
|
|
168
|
+
* **About Host** — Shows the current version of *Authtics Host*.
|
|
169
|
+
* **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
170
|
|
|
162
171
|
The information the Developer Panel shows outside of the tabs is just your **DAT** (Developer Access Token).
|
|
163
172
|
|
|
@@ -165,9 +174,9 @@ The information the Developer Panel shows outside of the tabs is just your **DAT
|
|
|
165
174
|
|
|
166
175
|
The Developer Panel also have remote buttons:
|
|
167
176
|
|
|
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:
|
|
177
|
+
* **Restart App** — Restarts the website.
|
|
178
|
+
* **Shut Down App** — Stops the website.
|
|
179
|
+
* **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
180
|
```json
|
|
172
181
|
{ "message": "unhandled request" }
|
|
173
182
|
```
|
|
@@ -196,6 +205,36 @@ Then when you go to `/hi` you see:
|
|
|
196
205
|
Hello!
|
|
197
206
|
```
|
|
198
207
|
|
|
208
|
+
## Notes about Authtics Host
|
|
209
|
+
|
|
210
|
+
Since Authtics Host is SSR, stuff like NPM packages that uses **Browser APIs** (e.g., `window`, `document`) will cause errors.
|
|
211
|
+
The developer (The one that is writing this README) may add a feature where Authtics Host converts:
|
|
212
|
+
```tsx
|
|
213
|
+
export function Component() {
|
|
214
|
+
return (
|
|
215
|
+
<p>{window.__status_code__}</p>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
To:
|
|
220
|
+
```tsx
|
|
221
|
+
import { useState, useEffect } from 'react';
|
|
222
|
+
export function Component() {
|
|
223
|
+
const [__window, __set_window] = useState<any>(null);
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
if (window) __set_window(window);
|
|
226
|
+
}, []);
|
|
227
|
+
return (
|
|
228
|
+
<p>{__window.__status_code__}</p>
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The developer might also redesign the UI for the DevPanel using [UI Tools](https://npmjs.com/package/@bananakitssu/ui-tools).
|
|
234
|
+
|
|
235
|
+
> **Warning**:
|
|
236
|
+
> 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**
|
|
237
|
+
|
|
199
238
|
## Developer Panel Notes And DAT (Developer Access Token)
|
|
200
239
|
|
|
201
240
|
A DAT (Developer Access Token) is your token that is used to access the Developer Panel.
|
|
@@ -222,4 +261,4 @@ There isn't any limit of DATs you can add.
|
|
|
222
261
|
|
|
223
262
|
## Contacting The Developer Of Authtics Host
|
|
224
263
|
|
|
225
|
-
If you
|
|
264
|
+
If you have any questions, or encountered a bug while using HostJS, contact: authtics@proton.me
|
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
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
24
|
-
|
|
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',
|
|
@@ -854,34 +905,24 @@ async function start() {
|
|
|
854
905
|
}, 1000);
|
|
855
906
|
});
|
|
856
907
|
if (config.hotReload === true) {
|
|
857
|
-
const
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
fs.watch(dir, { recursive: true }, (eventType, filename) => {
|
|
867
|
-
if (!filename)
|
|
868
|
-
return;
|
|
869
|
-
notifyHmrClients();
|
|
870
|
-
});
|
|
871
|
-
}
|
|
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);
|
|
872
917
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
return;
|
|
882
|
-
notifyHmrClients();
|
|
883
|
-
});
|
|
884
|
-
}
|
|
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);
|
|
885
926
|
}
|
|
886
927
|
}
|
|
887
928
|
return server;
|
package/dist/devPanel.js
CHANGED
|
@@ -84,7 +84,7 @@ function installNetworkInterceptor(onFetch, setFetch, onEventSource, setEventSou
|
|
|
84
84
|
try {
|
|
85
85
|
const resp = await originalFetch(...args);
|
|
86
86
|
const clone = resp.clone();
|
|
87
|
-
setFetch({ url, method, status:
|
|
87
|
+
setFetch({ url, method, status: clone.status, id: savedFetchId, response: await clone.text(), sent: body ? JSON.stringify(body, null, 2).replace('"{', '{')
|
|
88
88
|
.replace('}"', '}')
|
|
89
89
|
.replaceAll('\\"', '"') : null });
|
|
90
90
|
clone.arrayBuffer().then(buf => {
|
|
@@ -294,7 +294,7 @@ function installConsoleInterceptor() {
|
|
|
294
294
|
});
|
|
295
295
|
}
|
|
296
296
|
function SecuredTab() {
|
|
297
|
-
return (_jsxs("div", { style: { display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column', padding: '
|
|
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." })] }));
|
|
298
298
|
}
|
|
299
299
|
function PanelButton({ onClick, onClick2, children, style, fullWidth, disabled }) {
|
|
300
300
|
const [hovered, setHovered] = useState(false);
|
|
@@ -659,23 +659,27 @@ export default function DevPanel() {
|
|
|
659
659
|
setTokenLength(length);
|
|
660
660
|
}
|
|
661
661
|
else {
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
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();
|
|
665
667
|
try {
|
|
666
|
-
localStorageToken
|
|
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);
|
|
667
679
|
}
|
|
668
|
-
catch (
|
|
680
|
+
catch (error) {
|
|
681
|
+
console.error('[Authtics Host] Error storing token in localStorage:', error);
|
|
669
682
|
}
|
|
670
|
-
cookie.set('dat', localStorageToken || newToken);
|
|
671
|
-
setToken(newToken);
|
|
672
|
-
let length_ = newToken?.split('');
|
|
673
|
-
let length;
|
|
674
|
-
length = length_?.length;
|
|
675
|
-
setTokenLength(length);
|
|
676
|
-
}
|
|
677
|
-
catch (error) {
|
|
678
|
-
console.error('[Authtics Host] Error storing token in localStorage:', error);
|
|
679
683
|
}
|
|
680
684
|
}
|
|
681
685
|
}
|
package/dist/renderEngine.js
CHANGED
|
@@ -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: {
|
|
@@ -42,22 +76,19 @@ async function buildWithEsbuild(virtualContent, resolveDir, platform) {
|
|
|
42
76
|
target: isNode ? ['node18'] : ['es2020'],
|
|
43
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',
|
|
47
|
-
'node:path',
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
path.join(__dirname, '
|
|
52
|
-
path.join(__dirname, 'config.js'),
|
|
53
|
-
path.join(__dirname, 'renderEngine.js'),
|
|
54
|
-
path.join(__dirname, 'rendererTempFiles.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'))]
|
|
55
87
|
: [
|
|
56
|
-
path.join(__dirname, 'cli.js'),
|
|
57
|
-
path.join(__dirname, '
|
|
58
|
-
path.join(__dirname, '
|
|
59
|
-
path.join(__dirname, '
|
|
60
|
-
path.join(__dirname, 'rendererTempFiles.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'))
|
|
61
92
|
],
|
|
62
93
|
});
|
|
63
94
|
return result.outputFiles[0].text;
|
|
@@ -93,7 +124,8 @@ function resolveTemplate(appDir, autoPageDir, autoPaging) {
|
|
|
93
124
|
return nextToCli;
|
|
94
125
|
}
|
|
95
126
|
function buildHtml(opts) {
|
|
96
|
-
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>`;
|
|
97
129
|
const hmrScript = hotReload ? HMR_SCRIPT : '';
|
|
98
130
|
if (templatePath && fs.existsSync(templatePath)) {
|
|
99
131
|
let template = fs.readFileSync(templatePath, "utf-8");
|
|
@@ -101,8 +133,8 @@ function buildHtml(opts) {
|
|
|
101
133
|
if (contentTag.test(template)) {
|
|
102
134
|
template = template.replace(contentTag, `<div id="root">${pageHtml}</div>\n ${devPanelContainer}`);
|
|
103
135
|
template = template.includes("</body>")
|
|
104
|
-
? template.replace("</body>", ` ${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}\n${hmrScript}</body>`)
|
|
105
|
-
: template + `\n${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}\n${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}`;
|
|
106
138
|
return template;
|
|
107
139
|
}
|
|
108
140
|
console.warn('[authtics] _template.html has no <authtics_host_content /> — using default layout.');
|
|
@@ -116,6 +148,7 @@ function buildHtml(opts) {
|
|
|
116
148
|
<body>
|
|
117
149
|
<div id="root">${pageHtml}</div>
|
|
118
150
|
${devPanelContainer}
|
|
151
|
+
${configScript}
|
|
119
152
|
${fileName ? `<script src="${fileName}"></script>` : `<script>${clientCode}</script>`}
|
|
120
153
|
${hmrScript}
|
|
121
154
|
</body>
|
|
@@ -138,18 +171,47 @@ function isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPa
|
|
|
138
171
|
return false;
|
|
139
172
|
}
|
|
140
173
|
}
|
|
141
|
-
const HMR_SCRIPT = `<script>(function(){
|
|
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>`;
|
|
142
194
|
export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = 'esbuild', hotReload = false, autoPaging = true, autoPageDir, appDir, fileHandler, rmvFileHandler, reqDAT, configDAT) {
|
|
143
195
|
ensureTempDirs();
|
|
144
196
|
if (!fs.existsSync(tsxPath)) {
|
|
145
197
|
throw new Error(`renderPage: source file not found: ${tsxPath}`);
|
|
146
198
|
}
|
|
147
199
|
let usesDevPanel = showPanel && devPanelPath !== null;
|
|
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
|
+
}
|
|
148
209
|
const templatePath = resolveTemplate(appDir, autoPageDir, autoPaging);
|
|
149
210
|
const tempPath = tempFileForTsx(tsxPath);
|
|
150
211
|
const serverCjsPath = tempPath.replace(/\.json$/, ".server.cjs");
|
|
151
212
|
let clientCode;
|
|
152
213
|
let serverCode;
|
|
214
|
+
let freshWrite = false;
|
|
153
215
|
if (isCacheValid(tsxPath, tempPath, serverCjsPath, templatePath, devPanelPath) && autoPaging) {
|
|
154
216
|
try {
|
|
155
217
|
const parsed = JSON.parse(readTempFile(tempPath));
|
|
@@ -189,9 +251,10 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
|
|
|
189
251
|
}
|
|
190
252
|
fs.writeFileSync(serverCjsPath, serverCode, "utf-8");
|
|
191
253
|
writeTempFile(tempPath, JSON.stringify({ clientCode }));
|
|
254
|
+
freshWrite = true;
|
|
192
255
|
}
|
|
193
|
-
fileHandler ? fileHandler(clientCode, `/${
|
|
194
|
-
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);
|
|
195
258
|
const exports = serverModule.default ?? serverModule;
|
|
196
259
|
const PageComponent = exports.default ?? exports.Page ?? exports.page ?? null;
|
|
197
260
|
const DevPanelComponent = usesDevPanel ? (exports.DevPanel ?? null) : null;
|
|
@@ -205,5 +268,9 @@ export async function renderPage(tsxPath, reqUrl, showPanel = true, compiler = '
|
|
|
205
268
|
const devPanelContainer = usesDevPanel
|
|
206
269
|
? `<div id="authtics-dev-panel-root">${devPanelHtml}</div>`
|
|
207
270
|
: "";
|
|
208
|
-
return buildHtml({ pageHtml, clientCode, devPanelContainer, templatePath, hotReload,
|
|
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
|
+
});
|
|
209
276
|
}
|
package/package.json
CHANGED