@bouygues-telecom/staticjs 0.1.11 → 0.1.14
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/_build/helpers/cachePages.d.ts +15 -0
- package/_build/helpers/cachePages.js +162 -0
- package/_build/helpers/createPage.d.ts +18 -0
- package/_build/helpers/createPage.js +41 -0
- package/_build/helpers/layoutDiscovery.d.ts +9 -0
- package/_build/helpers/layoutDiscovery.js +39 -0
- package/_build/helpers/readPages.d.ts +3 -0
- package/_build/helpers/readPages.js +24 -0
- package/_build/helpers/renderPageRuntime.d.ts +13 -0
- package/_build/helpers/renderPageRuntime.js +222 -0
- package/_build/scripts/build-html.d.ts +1 -0
- package/_build/scripts/build-html.js +138 -0
- package/_build/scripts/cli.d.ts +6 -0
- package/_build/scripts/cli.js +119 -0
- package/_build/scripts/create-static-app.d.ts +2 -0
- package/{dist → _build}/scripts/create-static-app.js +0 -0
- package/_build/scripts/generate-test-multiapps.d.ts +6 -0
- package/_build/scripts/generate-test-multiapps.js +79 -0
- package/_build/server/config/index.d.ts +20 -0
- package/_build/server/config/index.js +25 -0
- package/_build/server/config/vite.config.d.ts +2 -0
- package/_build/server/config/vite.config.js +31 -0
- package/_build/server/config/vite.plugin.d.ts +11 -0
- package/_build/server/config/vite.plugin.js +75 -0
- package/_build/server/index.d.ts +22 -0
- package/_build/server/index.js +124 -0
- package/_build/server/middleware/errorHandling.d.ts +22 -0
- package/_build/server/middleware/errorHandling.js +48 -0
- package/_build/server/middleware/hotReload.d.ts +31 -0
- package/_build/server/middleware/hotReload.js +152 -0
- package/_build/server/middleware/logging.d.ts +13 -0
- package/_build/server/middleware/logging.js +23 -0
- package/_build/server/middleware/parsing.d.ts +9 -0
- package/_build/server/middleware/parsing.js +21 -0
- package/_build/server/middleware/performance.d.ts +13 -0
- package/_build/server/middleware/performance.js +26 -0
- package/_build/server/middleware/rateLimiting.d.ts +17 -0
- package/_build/server/middleware/rateLimiting.js +38 -0
- package/_build/server/middleware/runtime.d.ts +23 -0
- package/_build/server/middleware/runtime.js +186 -0
- package/_build/server/middleware/security.d.ts +24 -0
- package/_build/server/middleware/security.js +39 -0
- package/_build/server/middleware/static.d.ts +9 -0
- package/_build/server/middleware/static.js +63 -0
- package/_build/server/routes/api.d.ts +23 -0
- package/_build/server/routes/api.js +124 -0
- package/_build/server/scripts/revalidate.d.ts +2 -0
- package/{dist → _build/server}/scripts/revalidate.js +3 -3
- package/_build/server/utils/fileWatcher.d.ts +27 -0
- package/_build/server/utils/fileWatcher.js +194 -0
- package/_build/server/utils/startup.d.ts +18 -0
- package/_build/server/utils/startup.js +93 -0
- package/_build/server/utils/vite.d.ts +18 -0
- package/_build/server/utils/vite.js +61 -0
- package/_build/server/utils/websocket.d.ts +26 -0
- package/_build/server/utils/websocket.js +140 -0
- package/package.json +28 -16
- package/LICENSE +0 -190
- package/README.md +0 -158
- package/dist/config/vite.plugin.js +0 -80
- package/dist/helpers/cachePages.js +0 -26
- package/dist/helpers/createPage.js +0 -23
- package/dist/helpers/readPages.js +0 -19
- package/dist/scripts/build-html.js +0 -82
- package/dist/scripts/cli.js +0 -36
- package/dist/scripts/generate-test-multiapps.js +0 -51
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling middleware configuration
|
|
3
|
+
* Handles 404 errors and global error handling
|
|
4
|
+
*/
|
|
5
|
+
import { Request, Response, NextFunction, Express } from "express";
|
|
6
|
+
interface CustomError extends Error {
|
|
7
|
+
status?: number;
|
|
8
|
+
statusCode?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 404 handler for unmatched routes
|
|
12
|
+
*/
|
|
13
|
+
export declare const notFoundHandler: (req: Request, res: Response) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Global error handling middleware
|
|
16
|
+
*/
|
|
17
|
+
export declare const globalErrorHandler: (err: CustomError, req: Request, res: Response, next: NextFunction) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Apply error handling middleware to Express app
|
|
20
|
+
*/
|
|
21
|
+
export declare const applyErrorHandling: (app: Express) => void;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling middleware configuration
|
|
3
|
+
* Handles 404 errors and global error handling
|
|
4
|
+
*/
|
|
5
|
+
import { isDevelopment } from "../index.js";
|
|
6
|
+
/**
|
|
7
|
+
* 404 handler for unmatched routes
|
|
8
|
+
*/
|
|
9
|
+
export const notFoundHandler = (req, res) => {
|
|
10
|
+
res.status(404).json({
|
|
11
|
+
success: false,
|
|
12
|
+
error: 'Not Found',
|
|
13
|
+
message: `Cannot ${req.method} ${req.url}`,
|
|
14
|
+
timestamp: new Date().toISOString(),
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Global error handling middleware
|
|
19
|
+
*/
|
|
20
|
+
export const globalErrorHandler = (err, req, res, next) => {
|
|
21
|
+
const timestamp = new Date().toISOString();
|
|
22
|
+
const status = err.status || err.statusCode || 500;
|
|
23
|
+
// Log error details
|
|
24
|
+
console.error(`[${timestamp}] Error ${status}:`, {
|
|
25
|
+
message: err.message,
|
|
26
|
+
stack: isDevelopment ? err.stack : undefined,
|
|
27
|
+
url: req.url,
|
|
28
|
+
method: req.method,
|
|
29
|
+
ip: req.ip,
|
|
30
|
+
});
|
|
31
|
+
// Send error response
|
|
32
|
+
res.status(status).json({
|
|
33
|
+
success: false,
|
|
34
|
+
error: status === 500 ? 'Internal Server Error' : err.message,
|
|
35
|
+
message: isDevelopment ? err.message : 'An error occurred',
|
|
36
|
+
timestamp,
|
|
37
|
+
...(isDevelopment && { stack: err.stack }),
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Apply error handling middleware to Express app
|
|
42
|
+
*/
|
|
43
|
+
export const applyErrorHandling = (app) => {
|
|
44
|
+
// 404 handler for unmatched routes
|
|
45
|
+
app.use(notFoundHandler);
|
|
46
|
+
// Global error handling middleware
|
|
47
|
+
app.use(globalErrorHandler);
|
|
48
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hot reload middleware
|
|
3
|
+
* Handles client script injection and hot reload static file serving
|
|
4
|
+
*/
|
|
5
|
+
import { Request, Response, NextFunction, Express } from 'express';
|
|
6
|
+
interface HotReloadStatus {
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
scriptLoaded: boolean;
|
|
9
|
+
scriptSize: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Middleware to serve hot reload client script
|
|
13
|
+
*/
|
|
14
|
+
export declare const hotReloadStaticMiddleware: (req: Request, res: Response, next: NextFunction) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Middleware to inject hot reload client script into HTML responses
|
|
17
|
+
*/
|
|
18
|
+
export declare const hotReloadInjectionMiddleware: (req: Request, res: Response, next: NextFunction) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Apply hot reload middleware to Express app
|
|
21
|
+
*/
|
|
22
|
+
export declare const applyHotReload: (app: Express) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Clear cached hot reload script (useful for development)
|
|
25
|
+
*/
|
|
26
|
+
export declare const clearHotReloadCache: () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Get hot reload middleware status
|
|
29
|
+
*/
|
|
30
|
+
export declare const getHotReloadStatus: () => HotReloadStatus;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hot reload middleware
|
|
3
|
+
* Handles client script injection and hot reload static file serving
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { isDevelopment } from '../index.js';
|
|
8
|
+
// Cache for rendered pages and module cache invalidation
|
|
9
|
+
let hotReloadClientScript = null;
|
|
10
|
+
/**
|
|
11
|
+
* Load and cache the hot reload client script
|
|
12
|
+
*/
|
|
13
|
+
const loadHotReloadScript = () => {
|
|
14
|
+
if (!hotReloadClientScript) {
|
|
15
|
+
try {
|
|
16
|
+
// Always resolve to the centralized server location
|
|
17
|
+
// Find the project root by looking for the lib/server directory structure
|
|
18
|
+
let projectRoot = process.cwd();
|
|
19
|
+
while (projectRoot !== path.dirname(projectRoot)) {
|
|
20
|
+
const libServerDir = path.join(projectRoot, 'lib/server');
|
|
21
|
+
const packageJson = path.join(projectRoot, 'package.json');
|
|
22
|
+
// Look for lib/server directory structure and package.json to ensure we're at the main project root
|
|
23
|
+
if (fs.existsSync(libServerDir) && fs.existsSync(packageJson)) {
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
projectRoot = path.dirname(projectRoot);
|
|
27
|
+
}
|
|
28
|
+
const scriptPath = path.join(projectRoot, 'lib/server/static/hot-reload-client.js');
|
|
29
|
+
if (fs.existsSync(scriptPath)) {
|
|
30
|
+
hotReloadClientScript = fs.readFileSync(scriptPath, 'utf8');
|
|
31
|
+
// Hot reload client script loaded
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
throw new Error(`Script not found at ${scriptPath}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error('[HotReload] Failed to load hot reload client script:', error);
|
|
39
|
+
hotReloadClientScript = `
|
|
40
|
+
// Hot reload client script fallback
|
|
41
|
+
console.log('[HotReload] Hot reload client script not found, using fallback');
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return hotReloadClientScript;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Middleware to serve hot reload client script
|
|
49
|
+
*/
|
|
50
|
+
export const hotReloadStaticMiddleware = (req, res, next) => {
|
|
51
|
+
if (!isDevelopment) {
|
|
52
|
+
return next();
|
|
53
|
+
}
|
|
54
|
+
// Serve hot reload client script
|
|
55
|
+
if (req.path === '/hot-reload-client.js') {
|
|
56
|
+
const script = loadHotReloadScript();
|
|
57
|
+
res.setHeader('Content-Type', 'application/javascript');
|
|
58
|
+
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
59
|
+
res.setHeader('Pragma', 'no-cache');
|
|
60
|
+
res.setHeader('Expires', '0');
|
|
61
|
+
res.send(script);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
next();
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Middleware to inject hot reload client script into HTML responses
|
|
68
|
+
*/
|
|
69
|
+
export const hotReloadInjectionMiddleware = (req, res, next) => {
|
|
70
|
+
if (!isDevelopment) {
|
|
71
|
+
return next();
|
|
72
|
+
}
|
|
73
|
+
// Only inject into HTML responses
|
|
74
|
+
const originalSend = res.send;
|
|
75
|
+
res.send = function (body) {
|
|
76
|
+
// Check if this is an HTML response
|
|
77
|
+
const contentType = res.getHeader('Content-Type') || '';
|
|
78
|
+
const isHtml = contentType.toString().includes('text/html') ||
|
|
79
|
+
(typeof body === 'string' && (body.trim().startsWith('<!DOCTYPE html>') ||
|
|
80
|
+
body.includes('<div') ||
|
|
81
|
+
body.includes('<script')));
|
|
82
|
+
if (isHtml && typeof body === 'string') {
|
|
83
|
+
// Inject hot reload script
|
|
84
|
+
const hotReloadScript = `
|
|
85
|
+
<!-- Hot Reload Client Script (Development Only) -->
|
|
86
|
+
<script src="/hot-reload-client.js"></script>`;
|
|
87
|
+
// Try different injection strategies
|
|
88
|
+
let injected = false;
|
|
89
|
+
// Strategy 1: Before closing body tag
|
|
90
|
+
const bodyCloseIndex = body.lastIndexOf('</body>');
|
|
91
|
+
if (bodyCloseIndex !== -1) {
|
|
92
|
+
body = body.slice(0, bodyCloseIndex) + hotReloadScript + '\n' + body.slice(bodyCloseIndex);
|
|
93
|
+
injected = true;
|
|
94
|
+
}
|
|
95
|
+
// Strategy 2: Before closing html tag
|
|
96
|
+
if (!injected) {
|
|
97
|
+
const htmlCloseIndex = body.lastIndexOf('</html>');
|
|
98
|
+
if (htmlCloseIndex !== -1) {
|
|
99
|
+
body = body.slice(0, htmlCloseIndex) + hotReloadScript + '\n' + body.slice(htmlCloseIndex);
|
|
100
|
+
injected = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Strategy 3: Before the last closing div (for React fragments)
|
|
104
|
+
if (!injected) {
|
|
105
|
+
const lastDivIndex = body.lastIndexOf('</div>');
|
|
106
|
+
if (lastDivIndex !== -1) {
|
|
107
|
+
body = body.slice(0, lastDivIndex + 6) + hotReloadScript + body.slice(lastDivIndex + 6);
|
|
108
|
+
injected = true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Strategy 4: Append to end as fallback
|
|
112
|
+
if (!injected) {
|
|
113
|
+
body += hotReloadScript;
|
|
114
|
+
}
|
|
115
|
+
// Hot reload script injected
|
|
116
|
+
}
|
|
117
|
+
// Call original send method
|
|
118
|
+
return originalSend.call(this, body);
|
|
119
|
+
};
|
|
120
|
+
next();
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Apply hot reload middleware to Express app
|
|
124
|
+
*/
|
|
125
|
+
export const applyHotReload = (app) => {
|
|
126
|
+
if (!isDevelopment) {
|
|
127
|
+
// Skipping hot reload middleware in production mode
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Applying hot reload middleware
|
|
131
|
+
// Apply static file serving middleware first
|
|
132
|
+
app.use(hotReloadStaticMiddleware);
|
|
133
|
+
// Apply injection middleware
|
|
134
|
+
app.use(hotReloadInjectionMiddleware);
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Clear cached hot reload script (useful for development)
|
|
138
|
+
*/
|
|
139
|
+
export const clearHotReloadCache = () => {
|
|
140
|
+
hotReloadClientScript = null;
|
|
141
|
+
// Hot reload script cache cleared
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Get hot reload middleware status
|
|
145
|
+
*/
|
|
146
|
+
export const getHotReloadStatus = () => {
|
|
147
|
+
return {
|
|
148
|
+
enabled: isDevelopment,
|
|
149
|
+
scriptLoaded: hotReloadClientScript !== null,
|
|
150
|
+
scriptSize: hotReloadClientScript ? hotReloadClientScript.length : 0
|
|
151
|
+
};
|
|
152
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging middleware configuration
|
|
3
|
+
* Handles request logging with timestamps and details
|
|
4
|
+
*/
|
|
5
|
+
import { Request, Response, NextFunction, Express } from "express";
|
|
6
|
+
/**
|
|
7
|
+
* Request logging middleware with timestamps and details
|
|
8
|
+
*/
|
|
9
|
+
export declare const loggingMiddleware: (req: Request, res: Response, next: NextFunction) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Apply logging middleware to Express app
|
|
12
|
+
*/
|
|
13
|
+
export declare const applyLogging: (app: Express) => void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging middleware configuration
|
|
3
|
+
* Handles request logging with timestamps and details
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Request logging middleware with timestamps and details
|
|
7
|
+
*/
|
|
8
|
+
export const loggingMiddleware = (req, res, next) => {
|
|
9
|
+
const timestamp = new Date().toISOString();
|
|
10
|
+
// Log response time
|
|
11
|
+
const startTime = Date.now();
|
|
12
|
+
res.on('finish', () => {
|
|
13
|
+
const duration = Date.now() - startTime;
|
|
14
|
+
console.log(`[${timestamp}] ${req.method} ${req.url} - ${res.statusCode} - ${duration}ms`);
|
|
15
|
+
});
|
|
16
|
+
next();
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Apply logging middleware to Express app
|
|
20
|
+
*/
|
|
21
|
+
export const applyLogging = (app) => {
|
|
22
|
+
app.use(loggingMiddleware);
|
|
23
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request parsing middleware configuration
|
|
3
|
+
* Handles JSON and URL-encoded body parsing
|
|
4
|
+
*/
|
|
5
|
+
import express from "express";
|
|
6
|
+
import { CONFIG } from "../config/index.js";
|
|
7
|
+
/**
|
|
8
|
+
* Apply parsing middleware to Express app
|
|
9
|
+
*/
|
|
10
|
+
export const applyParsing = (app) => {
|
|
11
|
+
// JSON body parser with size limit
|
|
12
|
+
app.use(express.json({
|
|
13
|
+
limit: CONFIG.BODY_SIZE_LIMIT,
|
|
14
|
+
type: 'application/json',
|
|
15
|
+
}));
|
|
16
|
+
// URL-encoded body parser with size limit
|
|
17
|
+
app.use(express.urlencoded({
|
|
18
|
+
extended: true,
|
|
19
|
+
limit: CONFIG.BODY_SIZE_LIMIT,
|
|
20
|
+
}));
|
|
21
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance middleware configuration
|
|
3
|
+
* Handles compression, timeouts, and other performance-related middleware
|
|
4
|
+
*/
|
|
5
|
+
import { Request, Response, NextFunction, Express } from "express";
|
|
6
|
+
/**
|
|
7
|
+
* Request timeout middleware
|
|
8
|
+
*/
|
|
9
|
+
export declare const timeoutMiddleware: (req: Request, res: Response, next: NextFunction) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Apply performance middleware to Express app
|
|
12
|
+
*/
|
|
13
|
+
export declare const applyPerformance: (app: Express) => void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance middleware configuration
|
|
3
|
+
* Handles compression, timeouts, and other performance-related middleware
|
|
4
|
+
*/
|
|
5
|
+
import compression from "compression";
|
|
6
|
+
import { CONFIG } from "../config/index.js";
|
|
7
|
+
/**
|
|
8
|
+
* Request timeout middleware
|
|
9
|
+
*/
|
|
10
|
+
export const timeoutMiddleware = (req, res, next) => {
|
|
11
|
+
req.setTimeout(CONFIG.REQUEST_TIMEOUT, () => {
|
|
12
|
+
const err = new Error('Request timeout');
|
|
13
|
+
err.status = 408;
|
|
14
|
+
next(err);
|
|
15
|
+
});
|
|
16
|
+
next();
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Apply performance middleware to Express app
|
|
20
|
+
*/
|
|
21
|
+
export const applyPerformance = (app) => {
|
|
22
|
+
// Compression middleware for response optimization
|
|
23
|
+
app.use(compression());
|
|
24
|
+
// Request timeout middleware
|
|
25
|
+
app.use(timeoutMiddleware);
|
|
26
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate limiting middleware configuration
|
|
3
|
+
* Handles general and specific rate limiting for different endpoints
|
|
4
|
+
*/
|
|
5
|
+
import { Express } from "express";
|
|
6
|
+
/**
|
|
7
|
+
* General rate limiting for all endpoints
|
|
8
|
+
*/
|
|
9
|
+
export declare const generalLimiter: import("express-rate-limit").RateLimitRequestHandler;
|
|
10
|
+
/**
|
|
11
|
+
* Stricter rate limiting for revalidate endpoint
|
|
12
|
+
*/
|
|
13
|
+
export declare const revalidateLimiter: import("express-rate-limit").RateLimitRequestHandler;
|
|
14
|
+
/**
|
|
15
|
+
* Apply rate limiting middleware to Express app
|
|
16
|
+
*/
|
|
17
|
+
export declare const applyRateLimiting: (app: Express) => void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate limiting middleware configuration
|
|
3
|
+
* Handles general and specific rate limiting for different endpoints
|
|
4
|
+
*/
|
|
5
|
+
import rateLimit from "express-rate-limit";
|
|
6
|
+
import { CONFIG } from "../config/index.js";
|
|
7
|
+
/**
|
|
8
|
+
* General rate limiting for all endpoints
|
|
9
|
+
*/
|
|
10
|
+
export const generalLimiter = rateLimit({
|
|
11
|
+
windowMs: CONFIG.RATE_LIMIT_WINDOW,
|
|
12
|
+
max: CONFIG.RATE_LIMIT_MAX,
|
|
13
|
+
message: {
|
|
14
|
+
error: 'Too many requests from this IP, please try again later.',
|
|
15
|
+
retryAfter: Math.ceil(CONFIG.RATE_LIMIT_WINDOW / 1000),
|
|
16
|
+
},
|
|
17
|
+
standardHeaders: true,
|
|
18
|
+
legacyHeaders: false,
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Stricter rate limiting for revalidate endpoint
|
|
22
|
+
*/
|
|
23
|
+
export const revalidateLimiter = rateLimit({
|
|
24
|
+
windowMs: CONFIG.RATE_LIMIT_WINDOW,
|
|
25
|
+
max: CONFIG.REVALIDATE_RATE_LIMIT_MAX,
|
|
26
|
+
message: {
|
|
27
|
+
error: 'Too many revalidate requests, please try again later.',
|
|
28
|
+
retryAfter: Math.ceil(CONFIG.RATE_LIMIT_WINDOW / 1000),
|
|
29
|
+
},
|
|
30
|
+
standardHeaders: true,
|
|
31
|
+
legacyHeaders: false,
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Apply rate limiting middleware to Express app
|
|
35
|
+
*/
|
|
36
|
+
export const applyRateLimiting = (app) => {
|
|
37
|
+
app.use(generalLimiter);
|
|
38
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime rendering middleware configuration
|
|
3
|
+
* Handles development mode runtime page rendering and JavaScript serving
|
|
4
|
+
*/
|
|
5
|
+
import { Request, Response, NextFunction, Express } from "express";
|
|
6
|
+
import { ViteDevServer } from "vite";
|
|
7
|
+
/**
|
|
8
|
+
* Clear all caches when files change
|
|
9
|
+
*/
|
|
10
|
+
export declare const invalidateRuntimeCache: () => Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Runtime page rendering middleware for development mode
|
|
13
|
+
* Uses renderPageRuntime which handles all page resolution and dynamic routing
|
|
14
|
+
*/
|
|
15
|
+
export declare const runtimeRenderingMiddleware: (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Register JavaScript serving middleware for each page
|
|
18
|
+
*/
|
|
19
|
+
export declare const registerJavaScriptMiddleware: (app: Express, viteServer: ViteDevServer) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Apply runtime middleware to Express app (development mode only)
|
|
22
|
+
*/
|
|
23
|
+
export declare const applyRuntime: (app: Express) => void;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime rendering middleware configuration
|
|
3
|
+
* Handles development mode runtime page rendering and JavaScript serving
|
|
4
|
+
*/
|
|
5
|
+
import { renderPageRuntime } from "../../helpers/renderPageRuntime.js";
|
|
6
|
+
import { isDevelopment } from "../index.js";
|
|
7
|
+
import { CONFIG } from "../config/index.js";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
// Cache for rendered pages and module cache invalidation
|
|
10
|
+
let pageCache = new Map();
|
|
11
|
+
let lastCacheInvalidation = Date.now();
|
|
12
|
+
/**
|
|
13
|
+
* Clear all caches when files change
|
|
14
|
+
*/
|
|
15
|
+
export const invalidateRuntimeCache = async () => {
|
|
16
|
+
try {
|
|
17
|
+
// Cache invalidation (silent)
|
|
18
|
+
pageCache.clear();
|
|
19
|
+
lastCacheInvalidation = Date.now();
|
|
20
|
+
// Import Vite server using ES modules
|
|
21
|
+
const { getViteServer } = await import('../utils/vite.js');
|
|
22
|
+
const viteServer = getViteServer();
|
|
23
|
+
if (viteServer) {
|
|
24
|
+
// Check if Vite has module graph and invalidate caches
|
|
25
|
+
if (viteServer.moduleGraph) {
|
|
26
|
+
viteServer.moduleGraph.invalidateAll();
|
|
27
|
+
// Clear Vite's transform cache if it exists
|
|
28
|
+
if (viteServer.transformCache && typeof viteServer.transformCache.clear === 'function') {
|
|
29
|
+
viteServer.transformCache.clear();
|
|
30
|
+
}
|
|
31
|
+
// Clear Vite's internal caches if they exist
|
|
32
|
+
if (viteServer.ssrTransform && typeof viteServer.ssrTransform.clear === 'function') {
|
|
33
|
+
viteServer.ssrTransform.clear();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Force garbage collection to clear any remaining cached modules
|
|
38
|
+
if (global.gc) {
|
|
39
|
+
global.gc();
|
|
40
|
+
}
|
|
41
|
+
// Cache invalidation completed
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error('[Runtime] Error during cache invalidation:', error);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Runtime page rendering middleware for development mode
|
|
49
|
+
* Uses renderPageRuntime which handles all page resolution and dynamic routing
|
|
50
|
+
*/
|
|
51
|
+
export const runtimeRenderingMiddleware = async (req, res, next) => {
|
|
52
|
+
// Only handle GET requests for pages, but skip static assets and JS files
|
|
53
|
+
if (req.method === 'GET' && !req.path.match(/\.(js|css|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/)) {
|
|
54
|
+
/**
|
|
55
|
+
* Handle runtime rendering for development mode
|
|
56
|
+
* Leverages renderPageRuntime's built-in page resolution and parameter handling
|
|
57
|
+
*/
|
|
58
|
+
try {
|
|
59
|
+
const cacheKey = req.path;
|
|
60
|
+
let htmlContent = null;
|
|
61
|
+
// Check if we have cached content and it's still valid
|
|
62
|
+
if (pageCache.has(cacheKey)) {
|
|
63
|
+
const cached = pageCache.get(cacheKey);
|
|
64
|
+
if (cached.timestamp > lastCacheInvalidation) {
|
|
65
|
+
htmlContent = cached.content;
|
|
66
|
+
// Serving cached content
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// If no valid cached content, render fresh
|
|
70
|
+
if (!htmlContent) {
|
|
71
|
+
// Rendering fresh content
|
|
72
|
+
htmlContent = await renderPageRuntime(req.path);
|
|
73
|
+
if (htmlContent) {
|
|
74
|
+
// Cache the rendered content
|
|
75
|
+
pageCache.set(cacheKey, {
|
|
76
|
+
content: htmlContent,
|
|
77
|
+
timestamp: Date.now()
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (htmlContent) {
|
|
82
|
+
res.setHeader('Content-Type', 'text/html');
|
|
83
|
+
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
84
|
+
res.setHeader('Pragma', 'no-cache');
|
|
85
|
+
res.setHeader('Expires', '0');
|
|
86
|
+
res.send(htmlContent);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// Continue to next middleware (static files or 404)
|
|
91
|
+
return next();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
console.error('[Runtime] Error rendering page:', error);
|
|
96
|
+
// Continue to next middleware on error
|
|
97
|
+
return next();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
next();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Register JavaScript serving middleware for each page
|
|
106
|
+
*/
|
|
107
|
+
export const registerJavaScriptMiddleware = (app, viteServer) => {
|
|
108
|
+
// Register specific routes for each page's JavaScript file
|
|
109
|
+
const pagesCache = JSON.parse(fs.readFileSync(`./${CONFIG.BUILD_DIR}/cache/pagesCache.json`, 'utf8'));
|
|
110
|
+
const excludedFiles = JSON.parse(fs.readFileSync(`./${CONFIG.BUILD_DIR}/cache/excludedFiles.json`, 'utf8'));
|
|
111
|
+
// Register a route for each page's JS file (skip dynamic routes)
|
|
112
|
+
Object.keys(pagesCache).forEach(pageName => {
|
|
113
|
+
if (!excludedFiles.includes(pageName) && !pageName.includes('[') && !pageName.includes(']')) {
|
|
114
|
+
const jsRoute = `/${pageName}.js`;
|
|
115
|
+
// Registering JS route for page
|
|
116
|
+
app.get(jsRoute, async (req, res) => {
|
|
117
|
+
try {
|
|
118
|
+
// Check if the page file has "no scripts" directive
|
|
119
|
+
const pageContent = fs.readFileSync(pagesCache[pageName], 'utf8');
|
|
120
|
+
const firstLine = pageContent.split('\n')[0];
|
|
121
|
+
if (firstLine.includes('no scripts')) {
|
|
122
|
+
return res.status(404).json({
|
|
123
|
+
success: false,
|
|
124
|
+
error: 'Not Found',
|
|
125
|
+
message: `JavaScript disabled for ${pageName}`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
// Check file modification time and invalidate cache if needed
|
|
129
|
+
const stats = fs.statSync(pagesCache[pageName]);
|
|
130
|
+
const fileModTime = stats.mtime.getTime();
|
|
131
|
+
if (fileModTime > lastCacheInvalidation && viteServer.moduleGraph) {
|
|
132
|
+
const moduleId = pagesCache[pageName];
|
|
133
|
+
const module = viteServer.moduleGraph.getModuleById(moduleId);
|
|
134
|
+
if (module) {
|
|
135
|
+
viteServer.moduleGraph.invalidateModule(module);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Transform the page file using Vite
|
|
139
|
+
const transformStartTime = Date.now();
|
|
140
|
+
const result = await viteServer.transformRequest(`${pagesCache[pageName]}?t=${transformStartTime}`, {
|
|
141
|
+
ssr: false
|
|
142
|
+
});
|
|
143
|
+
if (result && result.code) {
|
|
144
|
+
// Create a hash of the code for cache busting
|
|
145
|
+
const crypto = await import('crypto');
|
|
146
|
+
const codeHash = crypto.createHash('md5').update(result.code).digest('hex').slice(0, 8);
|
|
147
|
+
res.setHeader('Content-Type', 'application/javascript');
|
|
148
|
+
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
149
|
+
res.setHeader('Pragma', 'no-cache');
|
|
150
|
+
res.setHeader('Expires', '0');
|
|
151
|
+
res.setHeader('X-Timestamp', Date.now().toString());
|
|
152
|
+
res.setHeader('X-Code-Hash', codeHash);
|
|
153
|
+
res.setHeader('X-File-Modified', fileModTime.toString());
|
|
154
|
+
return res.send(result.code);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
return res.status(404).json({
|
|
158
|
+
success: false,
|
|
159
|
+
error: 'Not Found',
|
|
160
|
+
message: `No JavaScript generated for ${pageName}`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error(`Error compiling JavaScript for ${pageName}:`, error);
|
|
166
|
+
return res.status(500).json({
|
|
167
|
+
success: false,
|
|
168
|
+
error: 'Internal Server Error',
|
|
169
|
+
message: `Failed to compile JavaScript for ${pageName}`,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
else if (pageName.includes('[') || pageName.includes(']')) {
|
|
175
|
+
// Dynamic routes are handled differently - could add logging here if needed
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Apply runtime middleware to Express app (development mode only)
|
|
181
|
+
*/
|
|
182
|
+
export const applyRuntime = (app) => {
|
|
183
|
+
if (isDevelopment) {
|
|
184
|
+
app.use(runtimeRenderingMiddleware);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security middleware configuration
|
|
3
|
+
* Handles helmet, CORS, and other security-related middleware
|
|
4
|
+
*/
|
|
5
|
+
import cors from "cors";
|
|
6
|
+
import { Express } from "express";
|
|
7
|
+
/**
|
|
8
|
+
* Security headers middleware using helmet
|
|
9
|
+
* Configures appropriate security headers for the application
|
|
10
|
+
*/
|
|
11
|
+
export declare const securityMiddleware: (req: import("http").IncomingMessage, res: import("http").ServerResponse, next: (err?: unknown) => void) => void;
|
|
12
|
+
/**
|
|
13
|
+
* CORS configuration for development
|
|
14
|
+
* Allows cross-origin requests in development mode
|
|
15
|
+
*/
|
|
16
|
+
export declare const corsMiddleware: (req: cors.CorsRequest, res: {
|
|
17
|
+
statusCode?: number | undefined;
|
|
18
|
+
setHeader(key: string, value: string): any;
|
|
19
|
+
end(): any;
|
|
20
|
+
}, next: (err?: any) => any) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Apply security middleware to Express app
|
|
23
|
+
*/
|
|
24
|
+
export declare const applySecurity: (app: Express) => void;
|