UNPKG

fortify2-js

Version:

MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.

494 lines (490 loc) 19.7 kB
'use strict'; var FileWatcher = require('../../service/Reload/FileWatcher.js'); var HotReloader = require('../../service/Reload/HotReloader.js'); var Logger = require('../../utils/Logger.js'); var TypeScriptChecker = require('./typescript/TypeScriptChecker.js'); /*************************************************************************** * FortifyJS - Secure Array Types * * This file contains type definitions for the SecureArray architecture * * @author Nehonix * @license MIT * * Copyright (c) 2025 Nehonix. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. ***************************************************************************** */ /** * FileWatcherManager - Handles all file watching and hot reload operations for FastApi.ts * Manages file monitoring, hot reload functionality, and server restart coordination */ class FileWatcherManager { constructor(options, dependencies) { this.isMainProcess = true; this.options = options; this.dependencies = dependencies; if (this.options.fileWatcher?.enabled) { this.initializeFileWatcher(); } // Initialize TypeScript checker if enabled if (this.options.fileWatcher?.typeCheck?.enabled) { this.initializeTypeScriptChecker(); } } /** * Initialize file watcher and hot reloader */ initializeFileWatcher() { if (!this.options.fileWatcher?.enabled) return; Logger.logger.debug("fileWatcher", "Initializing file watcher..."); // Check if we're in the main process or a child process this.isMainProcess = !process.env.FORTIFY_CHILD_PROCESS; if (this.isMainProcess) { console.log("reloading..."); // Main process: Initialize hot reloader for true process restart this.hotReloader = new HotReloader.HotReloader({ enabled: true, script: process.argv[1] || "index.js", args: process.argv.slice(2), env: process.env, // Don't set FORTIFY_CHILD_PROCESS here - it will be set when spawning child processes cwd: process.cwd(), restartDelay: this.options.fileWatcher.restartDelay || 500, maxRestarts: this.options.fileWatcher.maxRestarts || 10, gracefulShutdownTimeout: 5000, verbose: this.options.fileWatcher.verbose || false, typescript: this.options.fileWatcher.typescript || { enabled: true, runner: "auto", runnerArgs: [], fallbackToNode: true, autoDetectRunner: true, }, }, this.options); // Initialize file watcher for the main process this.fileWatcher = new FileWatcher.UltraFastFileWatcher(this.options.fileWatcher); } else { // Child process: Don't initialize file watcher to avoid conflicts Logger.logger.debug("fileWatcher", "Running in child process mode (hot reload enabled)"); } Logger.logger.debug("fileWatcher", "FW initialized"); } /** * Initialize TypeScript checker */ initializeTypeScriptChecker() { if (!this.options.fileWatcher?.typeCheck?.enabled) return; Logger.logger.debug("typescript", "Initializing TypeScript checker..."); const typeCheckConfig = { enabled: true, configFile: this.options.fileWatcher.typeCheck.configFile, watchMode: false, checkOnSave: this.options.fileWatcher.typeCheck.checkOnSave ?? true, showWarnings: this.options.fileWatcher.typeCheck.showWarnings ?? true, showInfos: this.options.fileWatcher.typeCheck.showInfos ?? false, maxErrors: this.options.fileWatcher.typeCheck.maxErrors ?? 50, excludePatterns: [ "node_modules", "dist", "build", ".git", ...(this.options.fileWatcher.typeCheck.excludePatterns || []), ], includePatterns: this.options.fileWatcher.typeCheck .includePatterns || ["**/*.ts", "**/*.tsx"], verbose: this.options.fileWatcher.typeCheck.verbose ?? false, }; this.typeScriptChecker = new TypeScriptChecker.TypeScriptChecker(typeCheckConfig); Logger.logger.debug("typescript", "TypeScript checker initialized"); } /** * Get file watcher instance */ getFileWatcher() { return this.fileWatcher; } /** * Get hot reloader instance */ getHotReloader() { return this.hotReloader; } /** * Check if file watcher is enabled */ isFileWatcherEnabled() { return this.options.fileWatcher?.enabled === true; } /** * Check if running in main process */ isInMainProcess() { return this.isMainProcess; } /** * Set HTTP server reference for restart operations */ setHttpServer(server) { this.httpServer = server; } /** * Start file watcher for auto-reload (main process only) */ async startFileWatcher() { if (!this.fileWatcher) return; try { Logger.logger.debug("fileWatcher", " Starting file watcher for auto-reload..."); // Setup file watcher event handlers this.setupFileWatcherEventHandlers(); // Start watching with restart callback await this.fileWatcher.startWatching(async () => { await this.restartServer(); }); Logger.logger.debug("fileWatcher", "File watcher started successfully"); } catch (error) { Logger.logger.error("fileWatcher", "Failed to start file watcher:", error.message); } } /** * Start file watcher with hot reload (main process) */ async startFileWatcherWithHotReload() { if (!this.fileWatcher || !this.hotReloader) return; try { Logger.logger.debug("fileWatcher", "Starting file watcher with hot reload..."); // Setup file watcher event handlers for hot reload this.setupHotReloadEventHandlers(); // Start watching with hot reload callback await this.fileWatcher.startWatching(async () => { await this.triggerHotReload(); }); Logger.logger.debug("fileWatcher", "File watcher with hot reload started successfully"); } catch (error) { Logger.logger.error("fileWatcher", "Failed to start file watcher with hot reload:", error.message); } } /** * Setup hot reload event handlers */ setupHotReloadEventHandlers() { if (!this.fileWatcher || !this.hotReloader) return; this.fileWatcher.on("file:changed", async (event) => { if (this.options.fileWatcher?.verbose) { Logger.logger.debug("fileWatcher", `File changed: ${event.filename}`); } // Automatically check TypeScript if enabled and file is a TypeScript file await this.handleTypeScriptCheck(event); }); this.fileWatcher.on("restart:starting", (event) => { Logger.logger.debug("fileWatcher", `Hot reloading due to: ${event.filename}`); }); this.hotReloader.on("restart:completed", (data) => { Logger.logger.debug("fileWatcher", `Hot reload completed in ${data.duration}ms`); }); this.hotReloader.on("restart:failed", (data) => { Logger.logger.error("fileWatcher", `Hot reload failed: ${data.error}`); }); } /** * Setup file watcher event handlers */ setupFileWatcherEventHandlers() { if (!this.fileWatcher) return; this.fileWatcher.on("file:changed", async (event) => { if (this.options.fileWatcher?.verbose) { Logger.logger.debug("fileWatcher", `File changed: ${event.filename}`); } // Automatically check TypeScript if enabled and file is a TypeScript file await this.handleTypeScriptCheck(event); }); this.fileWatcher.on("restart:starting", (event) => { Logger.logger.debug("fileWatcher", `Restarting due to: ${event.filename}`); }); this.fileWatcher.on("restart:completed", (data) => { Logger.logger.debug("fileWatcher", `Restart completed in ${data.duration}ms`); }); this.fileWatcher.on("restart:failed", (data) => { Logger.logger.error("fileWatcher", `Restart failed: ${data.error}`); }); } /** * Trigger hot reload (true process restart) */ async triggerHotReload() { if (!this.hotReloader) { Logger.logger.warn("fileWatcher", "Hot reloader not available, falling back to regular restart"); await this.restartServer(); return; } try { // Check TypeScript types before restarting if enabled if (this.typeScriptChecker && this.options.fileWatcher?.typeCheck?.checkBeforeRestart) { Logger.logger.debug("typescript", "Checking TypeScript types before restart..."); const typeCheckResult = await this.typeScriptChecker.checkFiles(); if (!typeCheckResult.success && this.options.fileWatcher?.typeCheck?.failOnError) { Logger.logger.error("typescript", "❌ TypeScript errors found, skipping restart"); Logger.logger.error("typescript", `Found ${typeCheckResult.errors.length} errors`); return; } if (typeCheckResult.errors.length > 0) { Logger.logger.warn("typescript", `TypeScript errors found but continuing restart (${typeCheckResult.errors.length} errors)`); } } Logger.logger.debug("fileWatcher", "Triggering hot reload (process restart)..."); await this.hotReloader.restart(); } catch (error) { Logger.logger.error("fileWatcher", "Hot reload failed:", error.message); // Fallback to regular restart Logger.logger.debug("fileWatcher", "Falling back to regular restart..."); await this.restartServer(); } } /** * Restart server (for file watcher) with hot reload * Uses HotReloader for true process restart with TypeScript support */ async restartServer() { try { Logger.logger.info("fileWatcher", "🔄 Hot reloading server..."); // Use hot reloader for true process restart (supports TypeScript) if (this.hotReloader) { await this.hotReloader.restart(); return; } // This should not happen if FileWatcherManager is properly initialized Logger.logger.error("fileWatcher", "❌ HotReloader not available - this indicates a configuration issue"); throw new Error("HotReloader not initialized. Cannot perform hot reload."); } catch (error) { Logger.logger.error("fileWatcher", "Server hot reload failed:", error.message); throw error; } } /** * Stop file watcher */ async stopFileWatcher() { if (this.fileWatcher) { await this.fileWatcher.stopWatching(); Logger.logger.debug("fileWatcher", "File watcher stopped"); } } /** * Get file watcher status */ getFileWatcherStatus() { return this.fileWatcher?.getStatus() || null; } /** * Get file watcher restart stats */ getFileWatcherStats() { return this.fileWatcher?.getRestartStats() || null; } /** * Check TypeScript files for errors */ async checkTypeScript(files) { if (!this.typeScriptChecker) { return { success: false, errors: [ { file: "system", line: 0, column: 0, message: "TypeScript checker not initialized", code: 0, severity: "error", category: "system", source: "FileWatcherManager", }, ], warnings: [], totalFiles: 0, checkedFiles: [], duration: 0, timestamp: new Date(), }; } return await this.typeScriptChecker.checkFiles(files); } /** * Get TypeScript checker status */ getTypeScriptStatus() { return this.typeScriptChecker?.getStatus() || null; } /** * Enable TypeScript checking */ enableTypeScriptChecking() { if (this.typeScriptChecker) { this.typeScriptChecker.setEnabled(true); } else { Logger.logger.warn("typescript", "TypeScript checker not initialized"); } } /** * Disable TypeScript checking */ disableTypeScriptChecking() { if (this.typeScriptChecker) { this.typeScriptChecker.setEnabled(false); } else { Logger.logger.warn("typescript", "TypeScript checker not initialized"); } } /** * Handle automatic TypeScript checking when files change */ async handleTypeScriptCheck(event) { if (!this.typeScriptChecker || !this.options.fileWatcher?.typeCheck?.checkOnSave) { return; } // Only check TypeScript files const filename = event.filename || event.path || ""; if (!filename.endsWith(".ts") && !filename.endsWith(".tsx")) { return; } try { Logger.logger.debug("typescript", `Checking TypeScript for: ${filename}`); const result = await this.typeScriptChecker.checkFiles([filename]); if (result.errors.length > 0) { Logger.logger.error("typescript", `❌ TypeScript errors in ${filename}:`); result.errors.slice(0, 3).forEach((error) => { Logger.logger.error("typescript", ` Line ${error.line}: ${error.message} (TS${error.code})`); }); if (result.errors.length > 3) { Logger.logger.error("typescript", ` ... and ${result.errors.length - 3} more errors`); } } else { if (this.options.fileWatcher?.typeCheck?.verbose) { Logger.logger.info("typescript", `✔ No TypeScript errors in ${filename}`); } } if (result.warnings.length > 0 && this.options.fileWatcher?.typeCheck?.showWarnings) { Logger.logger.warn("typescript", `TypeScript warnings in ${filename}:`); result.warnings.slice(0, 2).forEach((warning) => { Logger.logger.warn("typescript", ` Line ${warning.line}: ${warning.message} (TS${warning.code})`); }); if (result.warnings.length > 2) { Logger.logger.warn("typescript", ` ... and ${result.warnings.length - 2} more warnings`); } } } catch (error) { Logger.logger.warn("typescript", `Failed to check TypeScript for ${filename}: ${error.message}`); } } /** * Add file watcher monitoring endpoints */ addFileWatcherMonitoringEndpoints(basePoint) { if (!this.fileWatcher || !this.options.fileWatcher?.enabled) return; // File watcher status endpoint this.dependencies.app.get(basePoint + "/health/filewatcher", async (req, res) => { try { const status = this.getFileWatcherStatus(); const stats = this.getFileWatcherStats(); res.json({ timestamp: new Date().toISOString(), fileWatcher: { status, stats, }, }); } catch (error) { res.status(500).json({ error: "Failed to get file watcher status", message: error.message, }); } }); // File watcher control endpoint this.dependencies.app.post(basePoint + "/filewatcher/control", async (req, res) => { try { const { action } = req.body; if (action === "stop") { await this.stopFileWatcher(); res.json({ success: true, message: "File watcher stopped", }); } else if (action === "start") { await this.startFileWatcher(); res.json({ success: true, message: "File watcher started", }); } else if (action === "restart") { await this.stopFileWatcher(); await this.startFileWatcher(); res.json({ success: true, message: "File watcher restarted", }); } else if (action === "reset-stats") { if (this.fileWatcher) { this.fileWatcher.resetStats(); } res.json({ success: true, message: "File watcher stats reset", }); } else { res.status(400).json({ error: "Invalid action. Use 'start', 'stop', 'restart', or 'reset-stats'", }); } } catch (error) { res.status(500).json({ error: "Failed to control file watcher", message: error.message, }); } }); } } exports.FileWatcherManager = FileWatcherManager; //# sourceMappingURL=FileWatcherManager.js.map