@bouygues-telecom/staticjs 0.1.11 → 0.1.15

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.
Files changed (66) hide show
  1. package/_build/helpers/cachePages.d.ts +15 -0
  2. package/_build/helpers/cachePages.js +162 -0
  3. package/_build/helpers/createPage.d.ts +18 -0
  4. package/_build/helpers/createPage.js +41 -0
  5. package/_build/helpers/layoutDiscovery.d.ts +9 -0
  6. package/_build/helpers/layoutDiscovery.js +39 -0
  7. package/_build/helpers/readPages.d.ts +3 -0
  8. package/_build/helpers/readPages.js +24 -0
  9. package/_build/helpers/renderPageRuntime.d.ts +13 -0
  10. package/_build/helpers/renderPageRuntime.js +222 -0
  11. package/_build/scripts/build-html.d.ts +1 -0
  12. package/_build/scripts/build-html.js +138 -0
  13. package/_build/scripts/cli.d.ts +6 -0
  14. package/_build/scripts/cli.js +119 -0
  15. package/_build/scripts/create-static-app.d.ts +2 -0
  16. package/{dist → _build}/scripts/create-static-app.js +0 -0
  17. package/_build/scripts/generate-test-multiapps.d.ts +6 -0
  18. package/_build/scripts/generate-test-multiapps.js +79 -0
  19. package/_build/server/config/index.d.ts +20 -0
  20. package/_build/server/config/index.js +25 -0
  21. package/_build/server/config/vite.config.d.ts +2 -0
  22. package/_build/server/config/vite.config.js +31 -0
  23. package/_build/server/config/vite.plugin.d.ts +11 -0
  24. package/_build/server/config/vite.plugin.js +75 -0
  25. package/_build/server/index.d.ts +22 -0
  26. package/_build/server/index.js +124 -0
  27. package/_build/server/middleware/errorHandling.d.ts +22 -0
  28. package/_build/server/middleware/errorHandling.js +48 -0
  29. package/_build/server/middleware/hotReload.d.ts +31 -0
  30. package/_build/server/middleware/hotReload.js +152 -0
  31. package/_build/server/middleware/logging.d.ts +13 -0
  32. package/_build/server/middleware/logging.js +23 -0
  33. package/_build/server/middleware/parsing.d.ts +9 -0
  34. package/_build/server/middleware/parsing.js +21 -0
  35. package/_build/server/middleware/performance.d.ts +13 -0
  36. package/_build/server/middleware/performance.js +26 -0
  37. package/_build/server/middleware/rateLimiting.d.ts +17 -0
  38. package/_build/server/middleware/rateLimiting.js +38 -0
  39. package/_build/server/middleware/runtime.d.ts +23 -0
  40. package/_build/server/middleware/runtime.js +186 -0
  41. package/_build/server/middleware/security.d.ts +24 -0
  42. package/_build/server/middleware/security.js +39 -0
  43. package/_build/server/middleware/static.d.ts +9 -0
  44. package/_build/server/middleware/static.js +63 -0
  45. package/_build/server/routes/api.d.ts +23 -0
  46. package/_build/server/routes/api.js +124 -0
  47. package/_build/server/scripts/revalidate.d.ts +2 -0
  48. package/{dist → _build/server}/scripts/revalidate.js +3 -3
  49. package/_build/server/utils/fileWatcher.d.ts +27 -0
  50. package/_build/server/utils/fileWatcher.js +194 -0
  51. package/_build/server/utils/startup.d.ts +18 -0
  52. package/_build/server/utils/startup.js +93 -0
  53. package/_build/server/utils/vite.d.ts +18 -0
  54. package/_build/server/utils/vite.js +61 -0
  55. package/_build/server/utils/websocket.d.ts +26 -0
  56. package/_build/server/utils/websocket.js +140 -0
  57. package/package.json +28 -16
  58. package/LICENSE +0 -190
  59. package/README.md +0 -158
  60. package/dist/config/vite.plugin.js +0 -80
  61. package/dist/helpers/cachePages.js +0 -26
  62. package/dist/helpers/createPage.js +0 -23
  63. package/dist/helpers/readPages.js +0 -19
  64. package/dist/scripts/build-html.js +0 -82
  65. package/dist/scripts/cli.js +0 -36
  66. package/dist/scripts/generate-test-multiapps.js +0 -51
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Security middleware configuration
3
+ * Handles helmet, CORS, and other security-related middleware
4
+ */
5
+ import helmet from "helmet";
6
+ import cors from "cors";
7
+ import { isDevelopment } from "../index.js";
8
+ /**
9
+ * Security headers middleware using helmet
10
+ * Configures appropriate security headers for the application
11
+ */
12
+ export const securityMiddleware = helmet({
13
+ contentSecurityPolicy: {
14
+ directives: {
15
+ defaultSrc: ["'self'"],
16
+ styleSrc: ["'self'", "'unsafe-inline'"],
17
+ scriptSrc: ["'self'"],
18
+ imgSrc: ["'self'", "data:", "https:"],
19
+ },
20
+ },
21
+ crossOriginEmbedderPolicy: false, // Allow embedding for development
22
+ });
23
+ /**
24
+ * CORS configuration for development
25
+ * Allows cross-origin requests in development mode
26
+ */
27
+ export const corsMiddleware = cors({
28
+ origin: true,
29
+ credentials: true,
30
+ });
31
+ /**
32
+ * Apply security middleware to Express app
33
+ */
34
+ export const applySecurity = (app) => {
35
+ app.use(securityMiddleware);
36
+ if (isDevelopment) {
37
+ app.use(corsMiddleware);
38
+ }
39
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Static file serving middleware configuration
3
+ * Handles static file serving with proper cache headers
4
+ */
5
+ import { Express } from "express";
6
+ /**
7
+ * Apply static file serving middleware to Express app
8
+ */
9
+ export declare const applyStatic: (app: Express) => void;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Static file serving middleware configuration
3
+ * Handles static file serving with proper cache headers
4
+ */
5
+ import express from "express";
6
+ import { extname } from "path";
7
+ import { CONFIG, isDevelopment } from "../config/index.js";
8
+ /**
9
+ * Apply static file serving middleware to Express app
10
+ */
11
+ export const applyStatic = (app) => {
12
+ /**
13
+ * Enhanced static file serving with proper cache headers
14
+ * In development mode, exclude JavaScript files so they can be handled by Vite
15
+ */
16
+ app.use((req, res, next) => {
17
+ // In development mode, skip JavaScript files - let Vite handle them
18
+ if (isDevelopment && req.path.endsWith('.js')) {
19
+ console.log(`[DIAGNOSTIC] Skipping static middleware for JS file in development: ${req.path}`);
20
+ return next();
21
+ }
22
+ // Use express.static for non-JS files or in production
23
+ express.static(CONFIG.BUILD_DIR, {
24
+ maxAge: CONFIG.CACHE_MAX_AGE * 1000, // Convert to milliseconds
25
+ etag: true,
26
+ lastModified: true,
27
+ setHeaders: (res, path) => {
28
+ // Set cache headers based on file type
29
+ const ext = extname(path).toLowerCase();
30
+ // DIAGNOSTIC: Log static file serving
31
+ if (ext === '.js') {
32
+ console.log(`[DIAGNOSTIC] Static middleware serving JS file: ${path}`);
33
+ console.log(`[DIAGNOSTIC] Cache max age: ${CONFIG.CACHE_MAX_AGE}`);
34
+ console.log(`[DIAGNOSTIC] Is development: ${isDevelopment}`);
35
+ }
36
+ if (['.js', '.css', '.woff', '.woff2', '.ttf', '.eot'].includes(ext)) {
37
+ if (ext === '.js' && isDevelopment) {
38
+ // Don't cache JavaScript files in development to prevent stale code
39
+ const cacheControl = 'no-cache, no-store, must-revalidate';
40
+ res.setHeader('Cache-Control', cacheControl);
41
+ res.setHeader('Pragma', 'no-cache');
42
+ res.setHeader('Expires', '0');
43
+ console.log(`[DIAGNOSTIC] Setting no-cache for JS in development: ${cacheControl}`);
44
+ }
45
+ else {
46
+ // Cache static assets for longer in production
47
+ const cacheControl = `public, max-age=${CONFIG.CACHE_MAX_AGE}`;
48
+ res.setHeader('Cache-Control', cacheControl);
49
+ if (ext === '.js') {
50
+ console.log(`[DIAGNOSTIC] Setting Cache-Control for JS: ${cacheControl}`);
51
+ }
52
+ }
53
+ }
54
+ else if (['.html', '.htm'].includes(ext)) {
55
+ // Don't cache HTML files in development
56
+ res.setHeader('Cache-Control', isDevelopment ? 'no-cache' : `public, max-age=${CONFIG.CACHE_MAX_AGE / 24}`);
57
+ }
58
+ // Security headers for static files
59
+ res.setHeader('X-Content-Type-Options', 'nosniff');
60
+ },
61
+ })(req, res, next);
62
+ });
63
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * API route handlers
3
+ * Handles health check, pages listing, and revalidate endpoints
4
+ */
5
+ import { Request, Response, Express } from "express";
6
+ /**
7
+ * Health check endpoint
8
+ * Returns server status and basic information
9
+ */
10
+ export declare const healthCheck: (req: Request, res: Response) => void;
11
+ /**
12
+ * API endpoint to list available pages
13
+ * In development mode, uses readPages helper; in production, scans static directory
14
+ */
15
+ export declare const listPages: (req: Request, res: Response) => Promise<void>;
16
+ /**
17
+ * Revalidate endpoint with enhanced error handling and rate limiting
18
+ */
19
+ export declare const revalidateEndpoint: (req: Request, res: Response) => Promise<void>;
20
+ /**
21
+ * Register API routes with Express app
22
+ */
23
+ export declare const registerApiRoutes: (app: Express) => void;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * API route handlers
3
+ * Handles health check, pages listing, and revalidate endpoints
4
+ */
5
+ import { revalidate } from "../scripts/revalidate.js";
6
+ import { getAvailablePagesRuntime } from "../../helpers/renderPageRuntime.js";
7
+ import { readdir } from "fs/promises";
8
+ import { extname, join } from "path";
9
+ import { CONFIG, isDevelopment } from "../config/index.js";
10
+ import { revalidateLimiter } from "../middleware/rateLimiting.js";
11
+ /**
12
+ * Health check endpoint
13
+ * Returns server status and basic information
14
+ */
15
+ export const healthCheck = (req, res) => {
16
+ const healthInfo = {
17
+ status: 'healthy',
18
+ timestamp: new Date().toISOString(),
19
+ uptime: process.uptime(),
20
+ environment: CONFIG.NODE_ENV,
21
+ version: process.version,
22
+ memory: process.memoryUsage(),
23
+ };
24
+ res.status(200).json(healthInfo);
25
+ };
26
+ /**
27
+ * API endpoint to list available pages
28
+ * In development mode, uses readPages helper; in production, scans static directory
29
+ */
30
+ export const listPages = async (req, res) => {
31
+ try {
32
+ let pages;
33
+ if (isDevelopment) {
34
+ // Use runtime pages discovery in development
35
+ const runtimePages = getAvailablePagesRuntime();
36
+ pages = Object.keys(runtimePages).map(pageName => {
37
+ const filePath = runtimePages[pageName];
38
+ const isIndexFile = filePath.endsWith('/index.tsx');
39
+ return {
40
+ name: isIndexFile ? `${pageName}/index.tsx` : `${pageName}.tsx`,
41
+ path: pageName === 'index' ? '/' : `/${pageName}`,
42
+ file: filePath,
43
+ };
44
+ });
45
+ }
46
+ else {
47
+ // Use static file scanning in production
48
+ pages = await getAvailablePages();
49
+ }
50
+ const response = {
51
+ success: true,
52
+ pages,
53
+ count: pages.length,
54
+ mode: isDevelopment ? 'development (runtime)' : 'production (static)',
55
+ };
56
+ res.status(200).json(response);
57
+ }
58
+ catch (error) {
59
+ console.error('Error listing pages:', error);
60
+ const errorResponse = {
61
+ success: false,
62
+ error: 'Failed to list available pages',
63
+ message: isDevelopment ? error.message : 'Internal server error',
64
+ };
65
+ res.status(500).json(errorResponse);
66
+ }
67
+ };
68
+ /**
69
+ * Revalidate endpoint with enhanced error handling and rate limiting
70
+ */
71
+ export const revalidateEndpoint = async (req, res) => {
72
+ try {
73
+ await revalidate(req, res);
74
+ }
75
+ catch (error) {
76
+ console.error('Revalidate error:', error);
77
+ const errorResponse = {
78
+ success: false,
79
+ error: 'Revalidation failed',
80
+ message: isDevelopment ? error.message : 'Internal server error',
81
+ };
82
+ res.status(500).json(errorResponse);
83
+ }
84
+ };
85
+ /**
86
+ * Scans the static directory for available HTML pages
87
+ * @returns {Promise<Array>} Array of available page paths
88
+ */
89
+ async function getAvailablePages() {
90
+ const pages = [];
91
+ try {
92
+ const scanDirectory = async (dir, basePath = '') => {
93
+ const items = await readdir(join(CONFIG.BUILD_DIR, dir), { withFileTypes: true });
94
+ for (const item of items) {
95
+ const itemPath = join(dir, item.name);
96
+ const urlPath = join(basePath, item.name);
97
+ if (item.isDirectory()) {
98
+ await scanDirectory(itemPath, urlPath);
99
+ }
100
+ else if (item.isFile() && extname(item.name).toLowerCase() === '.html') {
101
+ const pagePath = urlPath.replace(/\.html$/, '').replace(/\\/g, '/');
102
+ pages.push({
103
+ name: item.name,
104
+ path: pagePath === 'index' ? '/' : `/${pagePath}`,
105
+ file: itemPath.replace(/\\/g, '/'),
106
+ });
107
+ }
108
+ }
109
+ };
110
+ await scanDirectory('');
111
+ }
112
+ catch (error) {
113
+ console.warn('Could not scan pages directory:', error.message);
114
+ }
115
+ return pages.sort((a, b) => a.path.localeCompare(b.path));
116
+ }
117
+ /**
118
+ * Register API routes with Express app
119
+ */
120
+ export const registerApiRoutes = (app) => {
121
+ app.get('/health', healthCheck);
122
+ app.get('/api/pages', listPages);
123
+ app.post('/revalidate', revalidateLimiter, revalidateEndpoint);
124
+ };
@@ -0,0 +1,2 @@
1
+ import { Request, Response } from "express";
2
+ export declare const revalidate: (req: Request, res: Response) => void;
@@ -7,8 +7,8 @@ export const revalidate = (req, res) => {
7
7
  try {
8
8
  const paths = req?.body?.paths || [];
9
9
  const pathsArg = paths.length > 0 ? paths.join(" ") : "";
10
- const cachePages = path.resolve(__dirname, "../helpers/cachePages.js");
11
- const buildHtmlConfig = path.resolve(__dirname, "./build-html.js");
10
+ const cachePages = path.resolve(__dirname, "../../../helpers/cachePages.js");
11
+ const buildHtmlConfig = path.resolve(__dirname, "../../../scripts/build-html.js");
12
12
  const buildCommand = `NODE_TLS_REJECT_UNAUTHORIZED=0 node ${cachePages} ${pathsArg && `${pathsArg}`} && npx tsx ${buildHtmlConfig}`;
13
13
  exec(buildCommand, (error, stdout, stderr) => {
14
14
  if (error) {
@@ -20,7 +20,7 @@ export const revalidate = (req, res) => {
20
20
  console.error(`stderr: ${stderr}`);
21
21
  }
22
22
  });
23
- return res
23
+ res
24
24
  .status(200)
25
25
  .send(`Revalidation triggered, paths: ${paths.length > 0 ? paths.join(", ") : "all pages"} built!`);
26
26
  }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * File watcher system for hot reloading
3
+ * Watches source files and triggers WebSocket broadcasts on changes
4
+ */
5
+ import { FSWatcher } from 'chokidar';
6
+ type ReloadType = 'style' | 'page' | 'full' | 'asset';
7
+ /**
8
+ * Initialize file watcher
9
+ */
10
+ export declare const initializeFileWatcher: () => FSWatcher | null;
11
+ /**
12
+ * Get current file watcher instance
13
+ */
14
+ export declare const getFileWatcher: () => FSWatcher | null;
15
+ /**
16
+ * Get watched files count
17
+ */
18
+ export declare const getWatchedFilesCount: () => number;
19
+ /**
20
+ * Close file watcher and cleanup
21
+ */
22
+ export declare const closeFileWatcher: () => Promise<void>;
23
+ /**
24
+ * Manually trigger a reload (useful for testing)
25
+ */
26
+ export declare const triggerReload: (type?: ReloadType, data?: Record<string, any>) => void;
27
+ export {};
@@ -0,0 +1,194 @@
1
+ /**
2
+ * File watcher system for hot reloading
3
+ * Watches source files and triggers WebSocket broadcasts on changes
4
+ */
5
+ import chokidar from 'chokidar';
6
+ import path from 'path';
7
+ import { broadcastReload } from './websocket.js';
8
+ import { isDevelopment } from '../config/index.js';
9
+ import { invalidateRuntimeCache } from '../middleware/runtime.js';
10
+ let watcher = null;
11
+ let debounceTimer = null;
12
+ const DEBOUNCE_DELAY = 300; // 300ms debounce
13
+ /**
14
+ * Determine reload type based on file extension
15
+ */
16
+ const getReloadType = (filePath) => {
17
+ const ext = path.extname(filePath).toLowerCase();
18
+ switch (ext) {
19
+ case '.css':
20
+ case '.scss':
21
+ case '.sass':
22
+ case '.less':
23
+ return 'style';
24
+ case '.tsx':
25
+ case '.jsx':
26
+ case '.ts':
27
+ case '.js':
28
+ return 'page';
29
+ case '.json':
30
+ case '.config.js':
31
+ case '.config.ts':
32
+ return 'full';
33
+ case '.png':
34
+ case '.jpg':
35
+ case '.jpeg':
36
+ case '.gif':
37
+ case '.svg':
38
+ case '.ico':
39
+ case '.woff':
40
+ case '.woff2':
41
+ case '.ttf':
42
+ case '.eot':
43
+ return 'asset';
44
+ default:
45
+ return 'page';
46
+ }
47
+ };
48
+ /**
49
+ * Handle file change with debouncing
50
+ */
51
+ const handleFileChange = (eventType, filePath) => {
52
+ // Clear existing timer
53
+ if (debounceTimer) {
54
+ clearTimeout(debounceTimer);
55
+ }
56
+ // Set new timer
57
+ debounceTimer = setTimeout(async () => {
58
+ const reloadType = getReloadType(filePath);
59
+ const relativePath = path.relative(process.cwd(), filePath);
60
+ // Invalidate runtime cache when source files change
61
+ if (relativePath.includes('src/') || relativePath.includes('src\\')) {
62
+ await invalidateRuntimeCache();
63
+ }
64
+ // Broadcast reload message to WebSocket clients
65
+ broadcastReload(reloadType, {
66
+ file: relativePath,
67
+ event: eventType,
68
+ timestamp: Date.now()
69
+ });
70
+ debounceTimer = null;
71
+ }, DEBOUNCE_DELAY);
72
+ };
73
+ /**
74
+ * Initialize file watcher
75
+ */
76
+ export const initializeFileWatcher = () => {
77
+ if (!isDevelopment) {
78
+ // Skipping file watcher in production mode
79
+ return null;
80
+ }
81
+ try {
82
+ // Define paths to watch based on current working directory
83
+ const cwd = process.cwd();
84
+ const watchPaths = [
85
+ 'src', // Source directory
86
+ 'src/**/*', // All source files in current directory
87
+ 'package.json', // Dependency changes
88
+ 'tsconfig.json', // TypeScript configuration
89
+ 'eslint.config.js' // ESLint configuration
90
+ ];
91
+ // If we're in the main project directory, also watch template directories
92
+ if (cwd.includes('static.js') && !cwd.includes('templates/')) {
93
+ watchPaths.push('templates/**/src/**/*', // Template source files
94
+ 'templates/**/package.json', // Template dependencies
95
+ 'templates/**/tsconfig.json' // Template TypeScript configs
96
+ );
97
+ }
98
+ // Create watcher with options
99
+ watcher = chokidar.watch(watchPaths, {
100
+ ignored: [
101
+ '**/node_modules/**',
102
+ '**/_build/**',
103
+ '**/node_modules/**',
104
+ '**/.git/**',
105
+ '**/coverage/**',
106
+ '**/*.log',
107
+ '**/.*'
108
+ ],
109
+ ignoreInitial: true,
110
+ persistent: true,
111
+ followSymlinks: true,
112
+ depth: 10, // Allow deep directory watching
113
+ awaitWriteFinish: {
114
+ stabilityThreshold: 100,
115
+ pollInterval: 50
116
+ }
117
+ });
118
+ // Set up event handlers
119
+ watcher
120
+ .on('add', (filePath) => handleFileChange('add', filePath))
121
+ .on('change', (filePath) => handleFileChange('change', filePath))
122
+ .on('unlink', (filePath) => handleFileChange('unlink', filePath))
123
+ .on('addDir', (dirPath) => {
124
+ // Directory added (silent)
125
+ })
126
+ .on('unlinkDir', (dirPath) => {
127
+ // Directory removed, triggering full reload
128
+ // Trigger full reload when directories are removed
129
+ broadcastReload('full', {
130
+ directory: path.relative(process.cwd(), dirPath),
131
+ event: 'unlinkDir'
132
+ });
133
+ })
134
+ .on('error', (err) => {
135
+ console.error('[FileWatcher] Watcher error:', err);
136
+ })
137
+ .on('ready', () => {
138
+ const watched = watcher.getWatched();
139
+ const watchedPaths = Object.keys(watched);
140
+ // File watcher initialized
141
+ });
142
+ return watcher;
143
+ }
144
+ catch (error) {
145
+ console.error('[FileWatcher] Failed to initialize file watcher:', error);
146
+ return null;
147
+ }
148
+ };
149
+ /**
150
+ * Get current file watcher instance
151
+ */
152
+ export const getFileWatcher = () => watcher;
153
+ /**
154
+ * Get watched files count
155
+ */
156
+ export const getWatchedFilesCount = () => {
157
+ if (!watcher)
158
+ return 0;
159
+ const watched = watcher.getWatched();
160
+ return Object.values(watched).reduce((total, files) => total + files.length, 0);
161
+ };
162
+ /**
163
+ * Close file watcher and cleanup
164
+ */
165
+ export const closeFileWatcher = async () => {
166
+ if (!watcher) {
167
+ return;
168
+ }
169
+ return new Promise((resolve) => {
170
+ // Closing file watcher
171
+ // Clear debounce timer
172
+ if (debounceTimer) {
173
+ clearTimeout(debounceTimer);
174
+ debounceTimer = null;
175
+ }
176
+ // Close watcher
177
+ watcher.close().then(() => {
178
+ // File watcher closed
179
+ watcher = null;
180
+ resolve();
181
+ }).catch((error) => {
182
+ console.error('[FileWatcher] Error closing file watcher:', error);
183
+ watcher = null;
184
+ resolve();
185
+ });
186
+ });
187
+ };
188
+ /**
189
+ * Manually trigger a reload (useful for testing)
190
+ */
191
+ export const triggerReload = (type = 'page', data = {}) => {
192
+ // Manually triggering reload
193
+ broadcastReload(type, { ...data, manual: true });
194
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Server startup and shutdown utilities
3
+ * Handles server initialization and graceful shutdown
4
+ */
5
+ import { Server } from 'http';
6
+ import { Express } from 'express';
7
+ /**
8
+ * Start the server with proper error handling
9
+ */
10
+ export declare const startServer: (app: Express, initializeViteServer?: (app: Express) => Promise<any>) => Promise<Server>;
11
+ /**
12
+ * Graceful shutdown handling
13
+ */
14
+ export declare const gracefulShutdown: (server: Server, signal: string) => Promise<void>;
15
+ /**
16
+ * Setup process event handlers for graceful shutdown
17
+ */
18
+ export declare const setupProcessHandlers: (server: Server) => void;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Server startup and shutdown utilities
3
+ * Handles server initialization and graceful shutdown
4
+ */
5
+ import { CONFIG } from "../config/index.js";
6
+ import { closeViteServer, getViteServer } from "./vite.js";
7
+ import { closeWebSocketServer, getConnectedClientsCount } from "./websocket.js";
8
+ import { closeFileWatcher, getWatchedFilesCount } from "./fileWatcher.js";
9
+ /**
10
+ * Start the server with proper error handling
11
+ */
12
+ export const startServer = async (app, initializeViteServer) => {
13
+ // Initialize Vite server first (if provided)
14
+ if (initializeViteServer) {
15
+ await initializeViteServer(app);
16
+ }
17
+ return app.listen(CONFIG.PORT, () => {
18
+ const viteServer = getViteServer();
19
+ const wsClients = getConnectedClientsCount();
20
+ const watchedFiles = getWatchedFilesCount();
21
+ console.log(`
22
+ 🚀 StaticJS Server Started
23
+ =====================================
24
+ Environment: ${CONFIG.NODE_ENV}
25
+ Port: ${CONFIG.PORT}
26
+ URL: http://localhost:${CONFIG.PORT}
27
+ Health Check: http://localhost:${CONFIG.PORT}/health
28
+ Pages API: http://localhost:${CONFIG.PORT}/api/pages
29
+ ${viteServer ? 'Vite JS Compilation: ✅ Enabled' : 'Vite JS Compilation: ❌ Disabled'}
30
+ ${CONFIG.WEBSOCKET_ENABLED ? `WebSocket Hot Reload: ✅ Enabled (${wsClients} clients)` : 'WebSocket Hot Reload: ❌ Disabled'}
31
+ ${CONFIG.FILE_WATCHING_ENABLED ? `File Watching: ✅ Enabled (${watchedFiles} files)` : 'File Watching: ❌ Disabled'}
32
+ =====================================
33
+ `);
34
+ });
35
+ };
36
+ /**
37
+ * Graceful shutdown handling
38
+ */
39
+ export const gracefulShutdown = async (server, signal) => {
40
+ console.log(`\n📡 Received ${signal}. Starting graceful shutdown...`);
41
+ try {
42
+ // Close file watcher first
43
+ if (CONFIG.FILE_WATCHING_ENABLED) {
44
+ console.log('🔍 Closing file watcher...');
45
+ await closeFileWatcher();
46
+ }
47
+ // Close WebSocket server
48
+ if (CONFIG.WEBSOCKET_ENABLED) {
49
+ console.log('🔌 Closing WebSocket server...');
50
+ await closeWebSocketServer();
51
+ }
52
+ // Close Vite server if it exists
53
+ console.log('⚡ Closing Vite server...');
54
+ await closeViteServer();
55
+ // Close HTTP server
56
+ console.log('🌐 Closing HTTP server...');
57
+ server.close((err) => {
58
+ if (err) {
59
+ console.error('❌ Error during server shutdown:', err);
60
+ process.exit(1);
61
+ }
62
+ console.log('✅ Server closed successfully');
63
+ process.exit(0);
64
+ });
65
+ }
66
+ catch (error) {
67
+ console.error('❌ Error during graceful shutdown:', error);
68
+ process.exit(1);
69
+ }
70
+ // Force shutdown after 10 seconds
71
+ setTimeout(() => {
72
+ console.error('⚠️ Forced shutdown after timeout');
73
+ process.exit(1);
74
+ }, 10000);
75
+ };
76
+ /**
77
+ * Setup process event handlers for graceful shutdown
78
+ */
79
+ export const setupProcessHandlers = (server) => {
80
+ // Handle shutdown signals
81
+ process.on('SIGTERM', () => gracefulShutdown(server, 'SIGTERM'));
82
+ process.on('SIGINT', () => gracefulShutdown(server, 'SIGINT'));
83
+ // Handle uncaught exceptions
84
+ process.on('uncaughtException', (err) => {
85
+ console.error('💥 Uncaught Exception:', err);
86
+ gracefulShutdown(server, 'uncaughtException');
87
+ });
88
+ // Handle unhandled promise rejections
89
+ process.on('unhandledRejection', (reason, promise) => {
90
+ console.error('💥 Unhandled Rejection at:', promise, 'reason:', reason);
91
+ gracefulShutdown(server, 'unhandledRejection');
92
+ });
93
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Vite server utilities
3
+ * Handles Vite server initialization and JavaScript serving for development mode
4
+ */
5
+ import { ViteDevServer } from "vite";
6
+ import { Express } from "express";
7
+ /**
8
+ * Initialize Vite server for development mode
9
+ */
10
+ export declare const initializeViteServer: (app: Express) => Promise<ViteDevServer | null>;
11
+ /**
12
+ * Get the current Vite server instance
13
+ */
14
+ export declare const getViteServer: () => ViteDevServer | null;
15
+ /**
16
+ * Close the Vite server
17
+ */
18
+ export declare const closeViteServer: () => Promise<void>;