opticore-webapp 1.0.67 → 1.0.69
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/dist/index.cjs +889 -294
- package/dist/index.d.cts +486 -35
- package/dist/index.d.ts +486 -35
- package/dist/index.js +891 -299
- package/dist/utils/translations/message.translation.en.json +73 -10
- package/dist/utils/translations/message.translation.fr.json +56 -10
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/core/webServer.core.ts
|
|
9
2
|
import * as path2 from "path";
|
|
10
3
|
import process4 from "process";
|
|
11
4
|
import corsOrigin from "cors";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
5
|
+
import express from "express";
|
|
6
|
+
import chokidar from "chokidar";
|
|
7
|
+
import {
|
|
8
|
+
CEvent,
|
|
9
|
+
CEventNameError as eventName2,
|
|
10
|
+
ServerListenEventError as ServerListenEventError2
|
|
11
|
+
} from "opticore-catch-exception-error";
|
|
12
|
+
import { getEnvironmentValue } from "opticore-env-access";
|
|
13
|
+
import { requestCallsEvent } from "opticore-request-call-event";
|
|
14
|
+
import { SContainer as SContainer2 } from "opticore-dependency-inject";
|
|
15
|
+
import { HttpStatusCode as HttpStatusCode3 } from "opticore-http-response";
|
|
16
|
+
import { TranslationLoader as TranslationLoader2 } from "opticore-translator";
|
|
15
17
|
|
|
16
18
|
// src/core/handlers/eventProcess.handler.ts
|
|
17
19
|
import process from "process";
|
|
@@ -449,63 +451,149 @@ var SServerStartError = (err, environmentPath) => {
|
|
|
449
451
|
};
|
|
450
452
|
|
|
451
453
|
// src/core/webServer.core.ts
|
|
452
|
-
import { requestCallsEvent } from "opticore-request-call-event";
|
|
453
|
-
import { SContainer as SContainer2 } from "opticore-dependency-inject";
|
|
454
|
-
import { HttpStatusCode as HttpStatusCode3 } from "opticore-http-response";
|
|
455
|
-
import { TranslationLoader as TranslationLoader2 } from "opticore-translator";
|
|
456
454
|
var WebServerCore = class {
|
|
457
455
|
serverUtility;
|
|
458
|
-
expressApp
|
|
456
|
+
expressApp;
|
|
459
457
|
localLanguage;
|
|
460
458
|
loggerConfig;
|
|
461
|
-
routerExpressApp;
|
|
462
459
|
getEnvironment;
|
|
463
460
|
environmentPath;
|
|
464
461
|
serverListenEvent;
|
|
462
|
+
// Existing properties
|
|
465
463
|
currentRoutes = [];
|
|
466
464
|
currentDependencies = [];
|
|
467
465
|
server = void 0;
|
|
468
466
|
errorEmitter;
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
467
|
+
serverStatus = "STARTING";
|
|
468
|
+
serverStartTime = /* @__PURE__ */ new Date();
|
|
469
|
+
// HMR properties
|
|
470
|
+
fileWatcher = null;
|
|
471
|
+
hmrRestartPending = false;
|
|
472
|
+
hmrDebounceTimeout = null;
|
|
473
|
+
hmrRestartCount = 0;
|
|
474
|
+
lastHmrRestartTime = 0;
|
|
475
|
+
/**
|
|
476
|
+
* Creates a new WebServerCore instance.
|
|
477
|
+
*
|
|
478
|
+
* @constructor
|
|
479
|
+
* @param {WebServerConstructorInterface} paramsConstructor - Configuration parameters
|
|
480
|
+
*
|
|
481
|
+
* @param {express.Application} paramsConstructor.app - Express application instance
|
|
482
|
+
* @param {LoggerCore} paramsConstructor.loggerConfig - Logger configuration
|
|
483
|
+
* @param {string} paramsConstructor.environmentPath - Path to environment file
|
|
484
|
+
* @param {string} paramsConstructor.localLanguage - Default language for translations
|
|
485
|
+
* @param {CorsOptions} paramsConstructor.corsOriginOptions - CORS configuration
|
|
486
|
+
*
|
|
487
|
+
* @returns {WebServerCore} New WebServerCore instance
|
|
488
|
+
*
|
|
489
|
+
* @throws {Error} If environment file cannot be loaded
|
|
490
|
+
*
|
|
491
|
+
* @example
|
|
492
|
+
* ```typescript
|
|
493
|
+
* const server = new WebServerCore({
|
|
494
|
+
* app: express(),
|
|
495
|
+
* loggerConfig: new LoggerCore(config),
|
|
496
|
+
* environmentPath: ".env",
|
|
497
|
+
* localLanguage: "fr",
|
|
498
|
+
* corsOriginOptions: { origin: "http://localhost:3000" }
|
|
499
|
+
* });
|
|
500
|
+
* ```
|
|
501
|
+
*/
|
|
476
502
|
constructor(paramsConstructor) {
|
|
477
|
-
this.getEnvironment =
|
|
478
|
-
this.routerExpressApp = paramsConstructor.app;
|
|
503
|
+
this.getEnvironment = getEnvironmentValue(paramsConstructor.environmentPath);
|
|
479
504
|
this.loggerConfig = paramsConstructor.loggerConfig;
|
|
480
505
|
this.localLanguage = paramsConstructor.localLanguage;
|
|
481
506
|
this.environmentPath = paramsConstructor.environmentPath;
|
|
507
|
+
this.expressApp = express();
|
|
508
|
+
this.serverListenEvent = new ServerListenEventError2(paramsConstructor.localLanguage);
|
|
482
509
|
this.expressApp.use(express.json());
|
|
483
510
|
this.expressApp.use(express.raw());
|
|
484
511
|
this.expressApp.use(express.text());
|
|
485
512
|
this.expressApp.use(express.urlencoded({ extended: true }));
|
|
486
513
|
this.expressApp.use(corsOrigin(paramsConstructor.corsOriginOptions));
|
|
487
|
-
this.serverListenEvent = new ServerListenEventError2(paramsConstructor.localLanguage);
|
|
488
514
|
this.serverUtility = new CoreService(paramsConstructor.localLanguage, paramsConstructor.environmentPath);
|
|
489
|
-
this.
|
|
490
|
-
this.
|
|
515
|
+
this.serverStatus = "STARTING";
|
|
516
|
+
this.serverStartTime = /* @__PURE__ */ new Date();
|
|
517
|
+
this.setupProcessEventListeners();
|
|
491
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* Starts the HTTP server and initializes all components including HMR if enabled.
|
|
521
|
+
*
|
|
522
|
+
* @method onStartServer
|
|
523
|
+
* @public
|
|
524
|
+
*
|
|
525
|
+
* @param {TFeatureRoutes[]} routers - Array of feature routes to register
|
|
526
|
+
* @param {(env: IEnvVariables) => void} [databaseCallback] - Optional database connection callback
|
|
527
|
+
* @param {TDependency[]} [dependenciesProvider] - Optional dependency injection providers
|
|
528
|
+
*
|
|
529
|
+
* @returns {serverWebApp | undefined} HTTP server instance or undefined if startup fails
|
|
530
|
+
*
|
|
531
|
+
* @throws {ServerListenEventError} If port/host configuration is invalid
|
|
532
|
+
* @throws {Error} If server initialization fails
|
|
533
|
+
*
|
|
534
|
+
* @fires WebServerCore#startHttpServer - When server successfully starts
|
|
535
|
+
* @fires WebServerCore#startHMR - When HMR is started (if enabled)
|
|
536
|
+
* @fires WebServerCore#serverError - When server fails to start
|
|
537
|
+
*
|
|
538
|
+
* @example
|
|
539
|
+
* ```typescript
|
|
540
|
+
* const server = app.onStartServer(
|
|
541
|
+
* routes,
|
|
542
|
+
* (env) => connectToDatabase(env),
|
|
543
|
+
* dependencies
|
|
544
|
+
* );
|
|
545
|
+
*
|
|
546
|
+
* if (server) {
|
|
547
|
+
* console.log("Server started successfully");
|
|
548
|
+
* }
|
|
549
|
+
* ```
|
|
550
|
+
*/
|
|
492
551
|
onStartServer(routers, databaseCallback, dependenciesProvider) {
|
|
552
|
+
try {
|
|
553
|
+
loaderTranslationFile(this.localLanguage);
|
|
554
|
+
this.currentRoutes = routers;
|
|
555
|
+
this.currentDependencies = dependenciesProvider || [];
|
|
556
|
+
this.serverStartTime = /* @__PURE__ */ new Date();
|
|
557
|
+
this.serverStatus = "STARTING";
|
|
558
|
+
if (!this.validateServerParameters()) {
|
|
559
|
+
return void 0;
|
|
560
|
+
}
|
|
561
|
+
const server = this.startHttpServer(databaseCallback);
|
|
562
|
+
if (this.getEnvironment.hmrEnabled) {
|
|
563
|
+
this.startHMR();
|
|
564
|
+
}
|
|
565
|
+
return server;
|
|
566
|
+
} catch (error) {
|
|
567
|
+
this.handleStartupError(error);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Validates server configuration parameters.
|
|
572
|
+
*
|
|
573
|
+
* @method validateServerParameters
|
|
574
|
+
* @private
|
|
575
|
+
*
|
|
576
|
+
* @returns {boolean} True if all parameters are valid, false otherwise
|
|
577
|
+
*
|
|
578
|
+
* @remarks
|
|
579
|
+
* Validates:
|
|
580
|
+
* - Port number is valid and positive
|
|
581
|
+
* - Host is not empty
|
|
582
|
+
* - Local language is specified
|
|
583
|
+
* - HMR configuration (if enabled)
|
|
584
|
+
*/
|
|
585
|
+
validateServerParameters() {
|
|
493
586
|
loaderTranslationFile(this.localLanguage);
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
return void 0;
|
|
587
|
+
const port = Number(this.getEnvironment.appPort);
|
|
588
|
+
if (isNaN(port) || port <= 0) {
|
|
589
|
+
this.serverListenEvent.portUndefined();
|
|
590
|
+
return false;
|
|
499
591
|
}
|
|
500
|
-
if (this.getEnvironment.appHost === "") {
|
|
592
|
+
if (!this.getEnvironment.appHost || this.getEnvironment.appHost.trim() === "") {
|
|
501
593
|
this.serverListenEvent.hostUndefined(this.getEnvironment.appHost);
|
|
502
|
-
return
|
|
594
|
+
return false;
|
|
503
595
|
}
|
|
504
|
-
if (
|
|
505
|
-
this.serverListenEvent.portUndefined();
|
|
506
|
-
return void 0;
|
|
507
|
-
}
|
|
508
|
-
if (this.localLanguage === "") {
|
|
596
|
+
if (!this.localLanguage || this.localLanguage.trim() === "") {
|
|
509
597
|
SLogger(this.localLanguage).logger.error({
|
|
510
598
|
message: TranslationLoader2.t("noDefaultLocalLang", this.localLanguage),
|
|
511
599
|
title: TranslationLoader2.t("noLocalLang", this.localLanguage),
|
|
@@ -513,75 +601,225 @@ var WebServerCore = class {
|
|
|
513
601
|
stackTrace: void 0,
|
|
514
602
|
httpCodeValue: HttpStatusCode3.NOT_FOUND
|
|
515
603
|
});
|
|
516
|
-
return
|
|
604
|
+
return false;
|
|
517
605
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
606
|
+
if (this.getEnvironment.hmrEnabled) {
|
|
607
|
+
if (!this.getEnvironment.hmrWatchPatterns || this.getEnvironment.hmrWatchPatterns.length === 0) {
|
|
608
|
+
SLogger(this.localLanguage).logger.warn({
|
|
609
|
+
title: TranslationLoader2.t("HMR_CONFIG_WARNING", this.localLanguage),
|
|
610
|
+
message: "HMR enabled but no watch patterns defined"
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return true;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Starts the HTTP server and sets up event listeners.
|
|
618
|
+
*
|
|
619
|
+
* @method startHttpServer
|
|
620
|
+
* @private
|
|
621
|
+
*
|
|
622
|
+
* @param {(env: IEnvVariables) => void} [databaseCallback] - Database connection callback
|
|
623
|
+
*
|
|
624
|
+
* @returns {serverWebApp | undefined} HTTP server instance or undefined if startup fails
|
|
625
|
+
*
|
|
626
|
+
* @throws {Error} If server fails to start
|
|
627
|
+
*/
|
|
628
|
+
startHttpServer(databaseCallback) {
|
|
629
|
+
try {
|
|
630
|
+
loaderTranslationFile(this.localLanguage);
|
|
631
|
+
const port = Number(this.getEnvironment.appPort);
|
|
632
|
+
const host = this.getEnvironment.appHost;
|
|
633
|
+
this.server = this.expressApp.listen(port, host, () => {
|
|
522
634
|
try {
|
|
635
|
+
this.configureServerComponents(databaseCallback);
|
|
636
|
+
this.serverStatus = "READY";
|
|
523
637
|
SLogger(this.localLanguage).logger.info({
|
|
524
|
-
|
|
525
|
-
|
|
638
|
+
title: TranslationLoader2.t("SERVER_RUNNING_TITLE", this.localLanguage),
|
|
639
|
+
message: TranslationLoader2.t("SERVER_RUNNING_AT", this.localLanguage, {
|
|
640
|
+
server: `${host}:${port}`,
|
|
641
|
+
hmrEnabled: this.getEnvironment.hmrEnabled ? "with HMR" : "without HMR"
|
|
642
|
+
})
|
|
526
643
|
});
|
|
527
|
-
if (databaseCallback) {
|
|
528
|
-
databaseCallback(this.getEnvironment);
|
|
529
|
-
}
|
|
530
|
-
new SContainer2(this.localLanguage, this.currentDependencies);
|
|
531
|
-
this.expressApp.use(express.static(path2.join(process4.cwd(), "public/template")));
|
|
532
|
-
this.registerRoutes(this.currentRoutes);
|
|
533
|
-
this.setupErrorHandling();
|
|
534
|
-
this.setupServerEvents();
|
|
535
|
-
this.serverState = "READY";
|
|
536
|
-
this.infoWebApp();
|
|
537
|
-
this.notifyServerReady();
|
|
538
644
|
} catch (err) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
645
|
+
this.handleServerConfigurationError(err);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
this.setupServerEventListeners();
|
|
649
|
+
return this.server;
|
|
650
|
+
} catch (error) {
|
|
651
|
+
this.serverListenEvent.onEventError(
|
|
652
|
+
new Error(TranslationLoader2.t(
|
|
653
|
+
"HTTP_SERVER_FAILED",
|
|
654
|
+
this.localLanguage,
|
|
655
|
+
{ errorMessage: error.message }
|
|
656
|
+
))
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Configures server components (database, dependencies, routes, etc.).
|
|
662
|
+
*
|
|
663
|
+
* @method configureServerComponents
|
|
664
|
+
* @private
|
|
665
|
+
*
|
|
666
|
+
* @param {(env: IEnvVariables) => void} [databaseCallback] - Database connection callback
|
|
667
|
+
*
|
|
668
|
+
* @returns {void}
|
|
669
|
+
*
|
|
670
|
+
* @throws {Error} If component configuration fails
|
|
671
|
+
*/
|
|
672
|
+
configureServerComponents(databaseCallback) {
|
|
673
|
+
try {
|
|
674
|
+
loaderTranslationFile(this.localLanguage);
|
|
675
|
+
if (databaseCallback && typeof databaseCallback === "function") {
|
|
676
|
+
try {
|
|
677
|
+
databaseCallback(this.getEnvironment);
|
|
678
|
+
} catch (dbError) {
|
|
679
|
+
this.serverListenEvent.listenerError(
|
|
680
|
+
new Error(TranslationLoader2.t("DB_CON_FAILED", this.localLanguage, { dbErrorMessage: dbError.message }))
|
|
681
|
+
);
|
|
547
682
|
}
|
|
548
683
|
}
|
|
549
|
-
|
|
550
|
-
|
|
684
|
+
try {
|
|
685
|
+
new SContainer2(this.localLanguage, this.currentDependencies);
|
|
686
|
+
} catch (depError) {
|
|
687
|
+
this.serverListenEvent.listenerError(
|
|
688
|
+
new Error(TranslationLoader2.t("DEPENDENCY_INJECTION_FAILED", this.localLanguage, { depErrorMessage: depError.message }))
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
this.expressApp.use(express.static(path2.join(process4.cwd(), "public/template")));
|
|
692
|
+
this.registerRoutes(this.currentRoutes);
|
|
693
|
+
this.setupErrorHandling();
|
|
694
|
+
this.infoWebApp();
|
|
695
|
+
} catch (error) {
|
|
696
|
+
this.serverListenEvent.listenerError(
|
|
697
|
+
new Error(TranslationLoader2.t("SERVER_COMPONENT_CONFIG_FAILED", this.localLanguage, { errorMessage: error.message }))
|
|
698
|
+
);
|
|
699
|
+
throw error;
|
|
700
|
+
}
|
|
551
701
|
}
|
|
552
702
|
/**
|
|
553
|
-
*
|
|
703
|
+
* Handles server configuration errors.
|
|
704
|
+
*
|
|
705
|
+
* @method handleServerConfigurationError
|
|
706
|
+
* @private
|
|
707
|
+
*
|
|
708
|
+
* @param {any} err - The error that occurred
|
|
709
|
+
*
|
|
710
|
+
* @returns {void}
|
|
554
711
|
*/
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
this.
|
|
563
|
-
SLogger(this.localLanguage).logger.info({
|
|
564
|
-
title: TranslationLoader2.t("", this.localLanguage),
|
|
565
|
-
message: TranslationLoader2.t("", this.localLanguage)
|
|
566
|
-
});
|
|
567
|
-
this.lastError = error;
|
|
568
|
-
this.serverState = "BLOCKED";
|
|
569
|
-
this.notifyWatcherBlockedByError(error);
|
|
570
|
-
});
|
|
571
|
-
this.errorEmitter.on("error", (error) => {
|
|
572
|
-
SLogger(this.localLanguage).logger.info({
|
|
573
|
-
title: TranslationLoader2.t("ERROR_EMITTED", this.localLanguage),
|
|
574
|
-
message: `Error emitted: ${error.message}`
|
|
575
|
-
});
|
|
576
|
-
});
|
|
712
|
+
handleServerConfigurationError(err) {
|
|
713
|
+
loaderTranslationFile(this.localLanguage);
|
|
714
|
+
this.serverStatus = "ERROR";
|
|
715
|
+
this.serverListenEvent.onEventError(err);
|
|
716
|
+
try {
|
|
717
|
+
SServerStartError(err, this.environmentPath);
|
|
718
|
+
} catch (startError) {
|
|
719
|
+
this.serverListenEvent.listenerError(startError);
|
|
577
720
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Handles general startup errors.
|
|
724
|
+
*
|
|
725
|
+
* @method handleStartupError
|
|
726
|
+
* @private
|
|
727
|
+
*
|
|
728
|
+
* @param {any} error - The startup error
|
|
729
|
+
*
|
|
730
|
+
* @returns {void}
|
|
731
|
+
*/
|
|
732
|
+
handleStartupError(error) {
|
|
733
|
+
loaderTranslationFile(this.localLanguage);
|
|
734
|
+
this.serverStatus = "ERROR";
|
|
735
|
+
this.serverListenEvent.listenerError(error);
|
|
736
|
+
SLogger(this.localLanguage).logger.error({
|
|
737
|
+
title: TranslationLoader2.t("GLOBAL_STARTUP_ERROR", this.localLanguage),
|
|
738
|
+
message: error.message,
|
|
739
|
+
errorType: error.name || "StartupError",
|
|
740
|
+
stackTrace: error.stack,
|
|
741
|
+
httpCodeValue: HttpStatusCode3.INTERNAL_SERVER_ERROR
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Sets up Node.js process event listeners.
|
|
746
|
+
*
|
|
747
|
+
* @method setupProcessEventListeners
|
|
748
|
+
* @private
|
|
749
|
+
*
|
|
750
|
+
* @returns {void}
|
|
751
|
+
*
|
|
752
|
+
* @remarks
|
|
753
|
+
* Listens for:
|
|
754
|
+
* - Process exit events
|
|
755
|
+
* - Uncaught exceptions
|
|
756
|
+
* - Unhandled rejections
|
|
757
|
+
* - System signals (SIGINT, SIGTERM)
|
|
758
|
+
*/
|
|
759
|
+
setupProcessEventListeners() {
|
|
760
|
+
loaderTranslationFile(this.localLanguage);
|
|
761
|
+
process4.on(CEvent.beforeExit, (code) => {
|
|
762
|
+
this.serverListenEvent.processBeforeExit(code);
|
|
763
|
+
});
|
|
764
|
+
process4.on(CEvent.disconnect, () => {
|
|
765
|
+
this.serverListenEvent.processDisconnected();
|
|
766
|
+
});
|
|
767
|
+
process4.on(CEvent.exit, (code) => {
|
|
768
|
+
this.serverListenEvent.exited(code);
|
|
769
|
+
});
|
|
770
|
+
process4.on(CEvent.message, (message) => {
|
|
771
|
+
this.serverListenEvent.message(message);
|
|
772
|
+
});
|
|
773
|
+
process4.on(CEvent.multipleResolves, (type, promise, reason) => {
|
|
774
|
+
this.serverListenEvent.multipleResolves(type, promise, reason);
|
|
775
|
+
});
|
|
776
|
+
process4.on(CEvent.rejectionHandled, (promise) => {
|
|
777
|
+
this.serverListenEvent.promiseRejectionHandled(promise);
|
|
778
|
+
});
|
|
779
|
+
process4.on(CEvent.uncaughtException, (error) => {
|
|
780
|
+
this.serverListenEvent.uncaughtException(error);
|
|
781
|
+
});
|
|
782
|
+
process4.on(CEvent.uncaughtExceptionMonitor, (error) => {
|
|
783
|
+
this.serverListenEvent.uncaughtExceptionMonitor(error);
|
|
784
|
+
});
|
|
785
|
+
process4.on(CEvent.unhandledRejection, (reason, promise) => {
|
|
786
|
+
this.serverListenEvent.unhandledRejection(reason, promise);
|
|
787
|
+
});
|
|
788
|
+
process4.on(CEvent.warning, (warning) => {
|
|
789
|
+
this.serverListenEvent.warning(warning);
|
|
790
|
+
});
|
|
791
|
+
process4.on(CEvent.sigint, () => {
|
|
792
|
+
this.serverListenEvent.processInterrupted();
|
|
793
|
+
});
|
|
794
|
+
process4.on(CEvent.sigterm, (signal) => {
|
|
795
|
+
this.serverListenEvent.sigtermSignalReceived(signal);
|
|
581
796
|
});
|
|
582
797
|
}
|
|
583
|
-
|
|
584
|
-
|
|
798
|
+
/**
|
|
799
|
+
* Sets up HTTP server event listeners.
|
|
800
|
+
*
|
|
801
|
+
* @method setupServerEventListeners
|
|
802
|
+
* @private
|
|
803
|
+
*
|
|
804
|
+
* @returns {void}
|
|
805
|
+
*
|
|
806
|
+
* @remarks
|
|
807
|
+
* Configures listeners for:
|
|
808
|
+
* - Server errors
|
|
809
|
+
* - Connection closing
|
|
810
|
+
* - Connection dropping
|
|
811
|
+
* - HTTP requests (for logging)
|
|
812
|
+
*
|
|
813
|
+
* @listens Server#error - Server error events
|
|
814
|
+
* @listens Server#close - Server closing events
|
|
815
|
+
* @listens Server#drop - Connection drop events
|
|
816
|
+
* @listens Server#request - HTTP request events
|
|
817
|
+
*/
|
|
818
|
+
setupServerEventListeners() {
|
|
819
|
+
loaderTranslationFile(this.localLanguage);
|
|
820
|
+
if (!this.server) {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
585
823
|
this.server.on(eventName2.error, (err) => {
|
|
586
824
|
this.serverListenEvent.onEventError(err);
|
|
587
825
|
});
|
|
@@ -603,270 +841,624 @@ var WebServerCore = class {
|
|
|
603
841
|
);
|
|
604
842
|
});
|
|
605
843
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
844
|
+
/**
|
|
845
|
+
* Sets up error handling middleware and event emitters.
|
|
846
|
+
*
|
|
847
|
+
* @method setupErrorHandling
|
|
848
|
+
* @private
|
|
849
|
+
*
|
|
850
|
+
* @returns {void}
|
|
851
|
+
*/
|
|
852
|
+
setupErrorHandling() {
|
|
853
|
+
loaderTranslationFile(this.localLanguage);
|
|
854
|
+
SLogger(this.localLanguage).logger.info({
|
|
855
|
+
title: TranslationLoader2.t("SETTING_UP", this.localLanguage),
|
|
856
|
+
message: TranslationLoader2.t("SETTING_UP_ERROR", this.localLanguage)
|
|
857
|
+
});
|
|
858
|
+
this.errorEmitter = eventProcessHandler(this.localLanguage, this.expressApp);
|
|
859
|
+
if (this.errorEmitter) {
|
|
860
|
+
this.expressApp.use((err, req, res, next) => {
|
|
861
|
+
this.serverListenEvent.expressErrorHandlingMiddleware(
|
|
862
|
+
this.errorEmitter,
|
|
863
|
+
err,
|
|
864
|
+
req,
|
|
865
|
+
res,
|
|
866
|
+
next
|
|
867
|
+
);
|
|
868
|
+
});
|
|
869
|
+
this.errorEmitter.on("transformError", (error) => {
|
|
870
|
+
this.serverListenEvent.listenerError(error);
|
|
871
|
+
});
|
|
872
|
+
this.errorEmitter.on("error", (error) => {
|
|
873
|
+
this.serverListenEvent.listenerError(error);
|
|
874
|
+
});
|
|
875
|
+
this.errorEmitter.on("hotReload", (data) => {
|
|
876
|
+
SLogger(this.localLanguage).logger.info({
|
|
877
|
+
title: TranslationLoader2.t("HOT_RELOAD_EVENT", this.localLanguage),
|
|
878
|
+
message: `Hot reload triggered for ${data.file}`
|
|
879
|
+
});
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
SLogger(this.localLanguage).logger.info({
|
|
883
|
+
title: TranslationLoader2.t("ERROR_HANDLING", this.localLanguage),
|
|
884
|
+
message: TranslationLoader2.t("ERROR_HANDLING_CONFIGURED", this.localLanguage)
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
/**
|
|
888
|
+
* Registers routes with the Express application.
|
|
889
|
+
*
|
|
890
|
+
* @method registerRoutes
|
|
891
|
+
* @private
|
|
892
|
+
*
|
|
893
|
+
* @param {any[]} allFeatureRoutes - Array of feature routes to register
|
|
894
|
+
*
|
|
895
|
+
* @returns {void}
|
|
896
|
+
*/
|
|
897
|
+
registerRoutes(allFeatureRoutes) {
|
|
898
|
+
allFeatureRoutes.forEach((router) => {
|
|
899
|
+
if (router.routes) {
|
|
900
|
+
router.routes.forEach((route) => {
|
|
901
|
+
this.expressApp.use(route.path, route.handler);
|
|
615
902
|
});
|
|
616
903
|
}
|
|
617
|
-
}
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* Displays server information.
|
|
908
|
+
*
|
|
909
|
+
* @method infoWebApp
|
|
910
|
+
* @private
|
|
911
|
+
*
|
|
912
|
+
* @returns {void}
|
|
913
|
+
*/
|
|
914
|
+
infoWebApp() {
|
|
915
|
+
loaderTranslationFile(this.localLanguage);
|
|
916
|
+
this.serverUtility.infoServer(
|
|
917
|
+
this.getEnvironment.appHost,
|
|
918
|
+
Number(this.getEnvironment.appPort)
|
|
919
|
+
);
|
|
618
920
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
921
|
+
/**
|
|
922
|
+
* Starts the Hot Module Replacement (HMR) file watching system.
|
|
923
|
+
*
|
|
924
|
+
* @method startHMR
|
|
925
|
+
* @private
|
|
926
|
+
*
|
|
927
|
+
* @returns {void}
|
|
928
|
+
*
|
|
929
|
+
* @remarks
|
|
930
|
+
* Configures file watcher based on environment variables:
|
|
931
|
+
* - HMR_ENABLED: Enable/disable HMR
|
|
932
|
+
* - HMR_WATCH_PATTERNS: Files to watch
|
|
933
|
+
* - HMR_IGNORE_PATTERNS: Files to ignore
|
|
934
|
+
*
|
|
935
|
+
* @throws {Error} If HMR configuration is invalid
|
|
936
|
+
*/
|
|
937
|
+
startHMR() {
|
|
938
|
+
try {
|
|
939
|
+
loaderTranslationFile(this.localLanguage);
|
|
940
|
+
const hmrConfig = this.getEnvironment;
|
|
941
|
+
if (!hmrConfig.hmrEnabled) {
|
|
622
942
|
SLogger(this.localLanguage).logger.info({
|
|
623
|
-
title: TranslationLoader2.t("
|
|
624
|
-
message:
|
|
943
|
+
title: TranslationLoader2.t("HMR_DISABLED", this.localLanguage),
|
|
944
|
+
message: "HMR is disabled in configuration"
|
|
625
945
|
});
|
|
626
|
-
|
|
946
|
+
return;
|
|
627
947
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
948
|
+
if (!hmrConfig.hmrWatchPatterns || hmrConfig.hmrWatchPatterns.length === 0) {
|
|
949
|
+
SLogger(this.localLanguage).logger.error({
|
|
950
|
+
title: TranslationLoader2.t("HMR_WATCH_PATTERNS_MISSING", this.localLanguage),
|
|
951
|
+
message: "No watch patterns defined for HMR",
|
|
952
|
+
errorType: "HMR Configuration Error",
|
|
953
|
+
httpCodeValue: HttpStatusCode3.BAD_REQUEST
|
|
954
|
+
});
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
const watchPatterns = hmrConfig.hmrWatchPatterns;
|
|
958
|
+
const ignorePatterns = hmrConfig.hmrIgnorePatterns || [
|
|
959
|
+
"node_modules/**",
|
|
960
|
+
"dist/**",
|
|
961
|
+
"build/**",
|
|
962
|
+
"*.log",
|
|
963
|
+
".git/**"
|
|
964
|
+
];
|
|
965
|
+
const allPatterns = [
|
|
966
|
+
...watchPatterns,
|
|
967
|
+
...ignorePatterns.map((pattern) => `!${pattern}`)
|
|
968
|
+
];
|
|
969
|
+
this.fileWatcher = chokidar.watch(allPatterns, {
|
|
970
|
+
ignored: /(^|[/\\])\../,
|
|
971
|
+
persistent: true,
|
|
972
|
+
ignoreInitial: true,
|
|
973
|
+
awaitWriteFinish: {
|
|
974
|
+
stabilityThreshold: 300,
|
|
975
|
+
pollInterval: 100
|
|
976
|
+
},
|
|
977
|
+
cwd: process4.cwd(),
|
|
978
|
+
depth: 10
|
|
633
979
|
});
|
|
634
|
-
this.
|
|
635
|
-
|
|
636
|
-
|
|
980
|
+
this.fileWatcher.on("ready", () => this.onHMRWatcherReady(watchPatterns, ignorePatterns)).on("change", (filePath) => this.handleFileChange(filePath)).on("add", (filePath) => this.handleFileChange(filePath, "added")).on("unlink", (filePath) => this.handleFileChange(filePath, "deleted")).on("error", (error) => this.onHMRWatcherError(error));
|
|
981
|
+
this.hmrRestartCount = 0;
|
|
982
|
+
this.lastHmrRestartTime = Date.now();
|
|
637
983
|
SLogger(this.localLanguage).logger.info({
|
|
638
|
-
title: TranslationLoader2.t("
|
|
639
|
-
message: TranslationLoader2.t("
|
|
984
|
+
title: TranslationLoader2.t("HMR_STARTED", this.localLanguage),
|
|
985
|
+
message: TranslationLoader2.t("HMR_MONITORING_STARTED", this.localLanguage)
|
|
640
986
|
});
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
title: TranslationLoader2.t("IPC", this.localLanguage),
|
|
648
|
-
message: TranslationLoader2.t("IPC_NOT_AVAILABLE", this.localLanguage)
|
|
987
|
+
} catch (error) {
|
|
988
|
+
SLogger(this.localLanguage).logger.error({
|
|
989
|
+
title: TranslationLoader2.t("HMR_START_FAILED", this.localLanguage),
|
|
990
|
+
message: error.message,
|
|
991
|
+
errorType: "HMR Error",
|
|
992
|
+
stackTrace: error.stack
|
|
649
993
|
});
|
|
650
|
-
return;
|
|
651
994
|
}
|
|
652
|
-
SLogger(this.localLanguage).logger.info({
|
|
653
|
-
title: TranslationLoader2.t("IPC_READY", this.localLanguage),
|
|
654
|
-
message: TranslationLoader2.t("IPC_READY_HOT_RELOAD", this.localLanguage)
|
|
655
|
-
});
|
|
656
|
-
process4.on("message", async (message) => {
|
|
657
|
-
if (message.type === "HOT_RELOAD_REQUEST") {
|
|
658
|
-
if (this.serverState === "BLOCKED") {
|
|
659
|
-
SLogger(this.localLanguage).logger.info({
|
|
660
|
-
title: TranslationLoader2.t("BLOCKED", this.localLanguage),
|
|
661
|
-
message: TranslationLoader2.t("BLOCKED_STATE", this.localLanguage)
|
|
662
|
-
});
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
if (this.serverState === "READY") {
|
|
666
|
-
await this.performTrueHotReload();
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
});
|
|
670
995
|
}
|
|
671
996
|
/**
|
|
672
|
-
*
|
|
997
|
+
* Handles file change events with debouncing.
|
|
998
|
+
*
|
|
999
|
+
* @method handleFileChange
|
|
1000
|
+
* @private
|
|
1001
|
+
*
|
|
1002
|
+
* @param {string} filePath - Path of the changed file
|
|
1003
|
+
* @param {string} [action="modified"] - Type of file change (modified/added/deleted)
|
|
1004
|
+
*
|
|
1005
|
+
* @returns {void}
|
|
1006
|
+
*
|
|
1007
|
+
* @remarks
|
|
1008
|
+
* Uses debouncing to prevent multiple rapid reloads.
|
|
1009
|
+
* Debounce time configurable via HMR_DEBOUNCE_MS environment variable.
|
|
673
1010
|
*/
|
|
674
|
-
|
|
675
|
-
this.
|
|
676
|
-
this.
|
|
677
|
-
|
|
1011
|
+
handleFileChange(filePath, action = "modified") {
|
|
1012
|
+
const debounceMs = this.getEnvironment.hmrDebounceMs || 500;
|
|
1013
|
+
if (this.hmrDebounceTimeout) {
|
|
1014
|
+
clearTimeout(this.hmrDebounceTimeout);
|
|
1015
|
+
}
|
|
1016
|
+
this.hmrDebounceTimeout = setTimeout(async () => {
|
|
1017
|
+
await this.triggerHotReload(filePath, action);
|
|
1018
|
+
}, debounceMs);
|
|
678
1019
|
SLogger(this.localLanguage).logger.info({
|
|
679
|
-
title: TranslationLoader2.t("
|
|
680
|
-
message:
|
|
1020
|
+
title: TranslationLoader2.t("FILE_CHANGE_DETECTED", this.localLanguage),
|
|
1021
|
+
message: `File ${action}: ${filePath}`
|
|
681
1022
|
});
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Triggers a hot reload operation.
|
|
1026
|
+
*
|
|
1027
|
+
* @method triggerHotReload
|
|
1028
|
+
* @private
|
|
1029
|
+
*
|
|
1030
|
+
* @param {string} filePath - Path of the changed file
|
|
1031
|
+
* @param {string} action - Type of file change
|
|
1032
|
+
*
|
|
1033
|
+
* @returns {Promise<void>}
|
|
1034
|
+
*
|
|
1035
|
+
* @remarks
|
|
1036
|
+
* - Checks if reload is already in progress
|
|
1037
|
+
* - Validates restart limits
|
|
1038
|
+
* - Performs appropriate reload actions based on file type
|
|
1039
|
+
* - Emits hotReload event
|
|
1040
|
+
*/
|
|
1041
|
+
async triggerHotReload(filePath, action) {
|
|
1042
|
+
if (this.hmrRestartPending) {
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
if (!this.canProceedWithHMRRestart()) {
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
this.hmrRestartPending = true;
|
|
1049
|
+
this.hmrRestartCount++;
|
|
1050
|
+
this.lastHmrRestartTime = Date.now();
|
|
682
1051
|
try {
|
|
683
|
-
|
|
684
|
-
SLogger(this.localLanguage).logger.info({
|
|
685
|
-
title: TranslationLoader2.t("APP_MODULES_CLEARED", this.localLanguage),
|
|
686
|
-
message: TranslationLoader2.t("CLEARED_MODULES", this.localLanguage, { clearedModules })
|
|
687
|
-
});
|
|
688
|
-
this.reloadConfigurations();
|
|
689
|
-
SLogger(this.localLanguage).logger.info({
|
|
690
|
-
title: TranslationLoader2.t("APP_MODULES_CLEARED", this.localLanguage),
|
|
691
|
-
message: TranslationLoader2.t("CLEARED_MODULES", this.localLanguage, { hotReloadAttempts: this.hotReloadAttempts })
|
|
692
|
-
});
|
|
693
|
-
this.reloadDependencies();
|
|
1052
|
+
loaderTranslationFile(this.localLanguage);
|
|
694
1053
|
SLogger(this.localLanguage).logger.info({
|
|
695
|
-
title: TranslationLoader2.t("
|
|
696
|
-
message: TranslationLoader2.t("
|
|
1054
|
+
title: TranslationLoader2.t("HOT_RELOAD_STARTING", this.localLanguage),
|
|
1055
|
+
message: TranslationLoader2.t("RELOADING_APPLICATION", this.localLanguage, {
|
|
1056
|
+
file: filePath,
|
|
1057
|
+
action
|
|
1058
|
+
})
|
|
697
1059
|
});
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
1060
|
+
if (this.errorEmitter) {
|
|
1061
|
+
this.errorEmitter.emit("hotReload", {
|
|
1062
|
+
file: filePath,
|
|
1063
|
+
action,
|
|
1064
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1065
|
+
restartCount: this.hmrRestartCount
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
await this.performHotReloadActions(filePath);
|
|
1069
|
+
SLogger(this.localLanguage).logger.success({
|
|
1070
|
+
title: TranslationLoader2.t("HOT_RELOAD_COMPLETE", this.localLanguage),
|
|
1071
|
+
message: TranslationLoader2.t("APPLICATION_RELOADED", this.localLanguage, {
|
|
1072
|
+
file: filePath,
|
|
1073
|
+
restartCount: this.hmrRestartCount,
|
|
1074
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1075
|
+
})
|
|
705
1076
|
});
|
|
706
|
-
this.notifyWatcherReloadSuccess(duration);
|
|
707
1077
|
} catch (error) {
|
|
708
|
-
this.
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
1078
|
+
SLogger(this.localLanguage).logger.error({
|
|
1079
|
+
title: TranslationLoader2.t("HOT_RELOAD_FAILED", this.localLanguage),
|
|
1080
|
+
message: error.message,
|
|
1081
|
+
errorType: "HotReloadError",
|
|
1082
|
+
stackTrace: error.stack,
|
|
1083
|
+
httpCodeValue: HttpStatusCode3.INTERNAL_SERVER_ERROR
|
|
713
1084
|
});
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
this.lastError = error;
|
|
717
|
-
this.serverState = "BLOCKED";
|
|
718
|
-
this.notifyWatcherBlockedByError(error);
|
|
719
|
-
} else {
|
|
720
|
-
this.serverState = "BLOCKED";
|
|
721
|
-
this.notifyWatcherReloadError(error);
|
|
722
|
-
}
|
|
1085
|
+
} finally {
|
|
1086
|
+
this.hmrRestartPending = false;
|
|
723
1087
|
}
|
|
724
1088
|
}
|
|
725
1089
|
/**
|
|
726
|
-
*
|
|
1090
|
+
* Performs appropriate hot reload actions based on file type.
|
|
1091
|
+
*
|
|
1092
|
+
* @method performHotReloadActions
|
|
1093
|
+
* @private
|
|
1094
|
+
*
|
|
1095
|
+
* @param {string} filePath - Path of the changed file
|
|
1096
|
+
*
|
|
1097
|
+
* @returns {Promise<void>}
|
|
1098
|
+
*
|
|
1099
|
+
* @remarks
|
|
1100
|
+
* Different actions for different file types:
|
|
1101
|
+
* - .json/.env: Reload translations
|
|
1102
|
+
* - routes/controller files: Reload routes
|
|
1103
|
+
* - config/.env files: Reload dependencies
|
|
727
1104
|
*/
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
const isJsonFile = key.endsWith(".json");
|
|
738
|
-
if (isApplicationModule && !isNodeModule && !isJsonFile) {
|
|
739
|
-
delete __require.cache[key];
|
|
740
|
-
clearedCount++;
|
|
741
|
-
}
|
|
1105
|
+
async performHotReloadActions(filePath) {
|
|
1106
|
+
if (filePath.includes(".json") || filePath.endsWith(".env")) {
|
|
1107
|
+
loaderTranslationFile(this.localLanguage);
|
|
1108
|
+
}
|
|
1109
|
+
if (filePath.includes("routes") || filePath.includes("controller")) {
|
|
1110
|
+
await this.reloadRoutes();
|
|
1111
|
+
}
|
|
1112
|
+
if (filePath.includes("config") || filePath.includes(".env")) {
|
|
1113
|
+
await this.reloadDependencies();
|
|
742
1114
|
}
|
|
743
|
-
return clearedCount;
|
|
744
1115
|
}
|
|
745
|
-
|
|
1116
|
+
/**
|
|
1117
|
+
* Reloads routes dynamically.
|
|
1118
|
+
*
|
|
1119
|
+
* @method reloadRoutes
|
|
1120
|
+
* @private
|
|
1121
|
+
*
|
|
1122
|
+
* @returns {Promise<void>}
|
|
1123
|
+
*
|
|
1124
|
+
* @throws {Error} If route reloading fails
|
|
1125
|
+
*
|
|
1126
|
+
* @remarks
|
|
1127
|
+
* This method should be implemented based on your architecture.
|
|
1128
|
+
* It should reload route modules from the filesystem.
|
|
1129
|
+
*/
|
|
1130
|
+
async reloadRoutes() {
|
|
746
1131
|
try {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
1132
|
+
SLogger(this.localLanguage).logger.info({
|
|
1133
|
+
title: TranslationLoader2.t("ROUTES_RELOADED", this.localLanguage),
|
|
1134
|
+
message: "Routes dynamically reloaded"
|
|
750
1135
|
});
|
|
751
|
-
loaderTranslationFile(this.localLanguage);
|
|
752
1136
|
} catch (error) {
|
|
753
|
-
throw new Error(`
|
|
1137
|
+
throw new Error(`Route reload failed: ${error.message}`);
|
|
754
1138
|
}
|
|
755
1139
|
}
|
|
756
|
-
|
|
1140
|
+
/**
|
|
1141
|
+
* Reloads dependencies dynamically.
|
|
1142
|
+
*
|
|
1143
|
+
* @method reloadDependencies
|
|
1144
|
+
* @private
|
|
1145
|
+
*
|
|
1146
|
+
* @returns {Promise<void>}
|
|
1147
|
+
*
|
|
1148
|
+
* @throws {Error} If dependency reloading fails
|
|
1149
|
+
*/
|
|
1150
|
+
async reloadDependencies() {
|
|
757
1151
|
try {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
1152
|
+
if (this.currentDependencies.length > 0) {
|
|
1153
|
+
new SContainer2(this.localLanguage, this.currentDependencies);
|
|
1154
|
+
SLogger(this.localLanguage).logger.info({
|
|
1155
|
+
title: TranslationLoader2.t("DEPENDENCIES_RELOADED", this.localLanguage),
|
|
1156
|
+
message: "Dependencies reloaded"
|
|
1157
|
+
});
|
|
762
1158
|
}
|
|
763
1159
|
} catch (error) {
|
|
764
1160
|
throw new Error(`Dependency reload failed: ${error.message}`);
|
|
765
1161
|
}
|
|
766
1162
|
}
|
|
767
1163
|
/**
|
|
768
|
-
*
|
|
1164
|
+
* Checks if HMR restart can proceed based on configuration limits.
|
|
1165
|
+
*
|
|
1166
|
+
* @method canProceedWithHMRRestart
|
|
1167
|
+
* @private
|
|
1168
|
+
*
|
|
1169
|
+
* @returns {boolean} True if restart can proceed, false otherwise
|
|
1170
|
+
*
|
|
1171
|
+
* @remarks
|
|
1172
|
+
* Checks:
|
|
1173
|
+
* - Auto-restart enabled/disabled
|
|
1174
|
+
* - Maximum restarts per minute limit
|
|
769
1175
|
*/
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
serverState: this.serverState,
|
|
777
|
-
message: "Code reloaded successfully"
|
|
1176
|
+
canProceedWithHMRRestart() {
|
|
1177
|
+
const hmrConfig = this.getEnvironment;
|
|
1178
|
+
if (!hmrConfig.hmrAutoRestarts) {
|
|
1179
|
+
SLogger(this.localLanguage).logger.warn({
|
|
1180
|
+
title: TranslationLoader2.t("HMR_AUTO_RESTART_DISABLED", this.localLanguage),
|
|
1181
|
+
message: "HMR auto-restart disabled"
|
|
778
1182
|
});
|
|
1183
|
+
return false;
|
|
779
1184
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
file: errorDetails.file,
|
|
795
|
-
line: errorDetails.line,
|
|
796
|
-
column: errorDetails.column,
|
|
797
|
-
detail: errorDetails.errorDetail,
|
|
798
|
-
tool: errorDetails.tool
|
|
799
|
-
}
|
|
1185
|
+
const maxRestarts = hmrConfig.hmrMaxRestarts || 5;
|
|
1186
|
+
const now = Date.now();
|
|
1187
|
+
const oneMinuteAgo = now - 6e4;
|
|
1188
|
+
if (this.lastHmrRestartTime < oneMinuteAgo) {
|
|
1189
|
+
this.hmrRestartCount = 0;
|
|
1190
|
+
}
|
|
1191
|
+
if (this.hmrRestartCount >= maxRestarts) {
|
|
1192
|
+
const nextReset = Math.ceil((this.lastHmrRestartTime + 6e4 - now) / 1e3);
|
|
1193
|
+
SLogger(this.localLanguage).logger.error({
|
|
1194
|
+
title: TranslationLoader2.t("HMR_RESTART_LIMIT_EXCEEDED", this.localLanguage),
|
|
1195
|
+
message: TranslationLoader2.t("HMR_RESTART_LIMIT_MESSAGE", this.localLanguage, {
|
|
1196
|
+
max: maxRestarts,
|
|
1197
|
+
nextReset
|
|
1198
|
+
})
|
|
800
1199
|
});
|
|
1200
|
+
return false;
|
|
801
1201
|
}
|
|
1202
|
+
return true;
|
|
802
1203
|
}
|
|
803
1204
|
/**
|
|
804
|
-
*
|
|
1205
|
+
* Called when the HMR watcher is ready.
|
|
1206
|
+
*
|
|
1207
|
+
* @method onHMRWatcherReady
|
|
1208
|
+
* @private
|
|
1209
|
+
*
|
|
1210
|
+
* @param {string[]} watchPatterns - Patterns being watched
|
|
1211
|
+
* @param {string[]} ignorePatterns - Patterns being ignored
|
|
1212
|
+
*
|
|
1213
|
+
* @returns {void}
|
|
805
1214
|
*/
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
});
|
|
815
|
-
}
|
|
1215
|
+
onHMRWatcherReady(watchPatterns, ignorePatterns) {
|
|
1216
|
+
SLogger(this.localLanguage).logger.info({
|
|
1217
|
+
title: TranslationLoader2.t("HMR_WATCHER_READY", this.localLanguage),
|
|
1218
|
+
message: TranslationLoader2.t("HMR_WATCHING_DETAILS", this.localLanguage, {
|
|
1219
|
+
watchCount: watchPatterns,
|
|
1220
|
+
ignoreCount: ignorePatterns
|
|
1221
|
+
})
|
|
1222
|
+
});
|
|
816
1223
|
}
|
|
817
1224
|
/**
|
|
818
|
-
*
|
|
1225
|
+
* Handles HMR watcher errors.
|
|
1226
|
+
*
|
|
1227
|
+
* @method onHMRWatcherError
|
|
1228
|
+
* @private
|
|
1229
|
+
*
|
|
1230
|
+
* @param {Error} error - The watcher error
|
|
1231
|
+
*
|
|
1232
|
+
* @returns {void}
|
|
819
1233
|
*/
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
const file = fileMatch ? fileMatch[1] : null;
|
|
827
|
-
const line = fileMatch ? parseInt(fileMatch[2], 10) : null;
|
|
828
|
-
const column = fileMatch ? parseInt(fileMatch[3], 10) : null;
|
|
829
|
-
const errorDetailMatch = errorMessage.match(/ERROR:\s*(.+?)(?:\n|$)/);
|
|
830
|
-
const errorDetail = errorDetailMatch ? errorDetailMatch[1].trim() : null;
|
|
831
|
-
return {
|
|
832
|
-
message: errorMessage,
|
|
833
|
-
file,
|
|
834
|
-
line,
|
|
835
|
-
column,
|
|
836
|
-
errorDetail,
|
|
837
|
-
tool
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
registerRoutes(allFeatureRoutes) {
|
|
841
|
-
allFeatureRoutes.forEach((router) => {
|
|
842
|
-
if (router.routes) {
|
|
843
|
-
router.routes.forEach((route) => {
|
|
844
|
-
this.expressApp.use(route.path, route.handler);
|
|
845
|
-
});
|
|
846
|
-
}
|
|
1234
|
+
onHMRWatcherError(error) {
|
|
1235
|
+
SLogger(this.localLanguage).logger.error({
|
|
1236
|
+
title: TranslationLoader2.t("HMR_WATCHER_ERROR", this.localLanguage),
|
|
1237
|
+
message: error.message,
|
|
1238
|
+
errorType: "FileWatcherError",
|
|
1239
|
+
stackTrace: error.stack
|
|
847
1240
|
});
|
|
848
1241
|
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
1242
|
+
/**
|
|
1243
|
+
* Stops the HMR system.
|
|
1244
|
+
*
|
|
1245
|
+
* @method stopHMR
|
|
1246
|
+
* @private
|
|
1247
|
+
*
|
|
1248
|
+
* @returns {void}
|
|
1249
|
+
*/
|
|
1250
|
+
stopHMR() {
|
|
1251
|
+
if (this.fileWatcher) {
|
|
1252
|
+
this.fileWatcher.close();
|
|
1253
|
+
this.fileWatcher = null;
|
|
1254
|
+
}
|
|
1255
|
+
if (this.hmrDebounceTimeout) {
|
|
1256
|
+
clearTimeout(this.hmrDebounceTimeout);
|
|
1257
|
+
this.hmrDebounceTimeout = null;
|
|
1258
|
+
}
|
|
1259
|
+
SLogger(this.localLanguage).logger.info({
|
|
1260
|
+
title: TranslationLoader2.t("HMR_STOPPED", this.localLanguage),
|
|
1261
|
+
message: TranslationLoader2.t("HMR_MONITORING_STOPPED", this.localLanguage)
|
|
1262
|
+
});
|
|
854
1263
|
}
|
|
855
|
-
|
|
1264
|
+
/**
|
|
1265
|
+
* Stops the server and HMR system cleanly.
|
|
1266
|
+
*
|
|
1267
|
+
* @method onStopServer
|
|
1268
|
+
* @public
|
|
1269
|
+
*
|
|
1270
|
+
* @returns {void}
|
|
1271
|
+
*/
|
|
1272
|
+
onStopServer() {
|
|
1273
|
+
this.stopHMR();
|
|
856
1274
|
if (this.server) {
|
|
857
1275
|
this.server.close(() => {
|
|
858
1276
|
SLogger(this.localLanguage).logger.info({
|
|
859
|
-
title: TranslationLoader2.t("
|
|
860
|
-
message:
|
|
1277
|
+
title: TranslationLoader2.t("SERVER_STOPPED", this.localLanguage),
|
|
1278
|
+
message: "Server stopped cleanly"
|
|
861
1279
|
});
|
|
862
|
-
process4.exit(0);
|
|
863
1280
|
});
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1281
|
+
this.serverStatus = "STOPPED";
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Gets the current server state information.
|
|
1286
|
+
*
|
|
1287
|
+
* @method getServerState
|
|
1288
|
+
* @public
|
|
1289
|
+
*
|
|
1290
|
+
* @returns {IServerStateInfo} Server state information object
|
|
1291
|
+
*
|
|
1292
|
+
* @remarks
|
|
1293
|
+
* Includes:
|
|
1294
|
+
* - Status, host, port
|
|
1295
|
+
* - Route and dependency counts
|
|
1296
|
+
* - Uptime and memory usage
|
|
1297
|
+
* - HMR configuration and statistics
|
|
1298
|
+
*/
|
|
1299
|
+
getServerState() {
|
|
1300
|
+
const now = /* @__PURE__ */ new Date();
|
|
1301
|
+
const uptime = this.serverStartTime ? now.getTime() - this.serverStartTime.getTime() : 0;
|
|
1302
|
+
return {
|
|
1303
|
+
status: this.serverStatus,
|
|
1304
|
+
isRunning: this.server !== void 0 && this.serverStatus === "READY",
|
|
1305
|
+
host: this.getEnvironment.appHost,
|
|
1306
|
+
port: Number(this.getEnvironment.appPort),
|
|
1307
|
+
language: this.localLanguage,
|
|
1308
|
+
routesCount: this.currentRoutes.length,
|
|
1309
|
+
dependenciesCount: this.currentDependencies.length,
|
|
1310
|
+
startTime: this.serverStartTime,
|
|
1311
|
+
currentTime: now,
|
|
1312
|
+
uptime,
|
|
1313
|
+
uptimeFormatted: this.formatUptime(uptime),
|
|
1314
|
+
memoryUsage: process4.memoryUsage(),
|
|
1315
|
+
pid: process4.pid,
|
|
1316
|
+
platform: process4.platform,
|
|
1317
|
+
nodeVersion: process4.version,
|
|
1318
|
+
cwd: process4.cwd(),
|
|
1319
|
+
hmrEnabled: this.getEnvironment.hmrEnabled,
|
|
1320
|
+
hmrWatchingFiles: this.getEnvironment.hmrWatchPatterns?.length || 0,
|
|
1321
|
+
hmrRestartCount: this.hmrRestartCount
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Gets the current server status.
|
|
1326
|
+
*
|
|
1327
|
+
* @method getServerStatus
|
|
1328
|
+
* @public
|
|
1329
|
+
*
|
|
1330
|
+
* @returns {TServerStatus} Current server status
|
|
1331
|
+
*/
|
|
1332
|
+
getServerStatus() {
|
|
1333
|
+
return this.serverStatus;
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Gets detailed server statistics.
|
|
1337
|
+
*
|
|
1338
|
+
* @method getServerStats
|
|
1339
|
+
* @public
|
|
1340
|
+
*
|
|
1341
|
+
* @returns {IServerStats} Server statistics object
|
|
1342
|
+
*
|
|
1343
|
+
* @remarks
|
|
1344
|
+
* Includes:
|
|
1345
|
+
* - Performance metrics (CPU, memory)
|
|
1346
|
+
* - Uptime information
|
|
1347
|
+
* - HMR statistics
|
|
1348
|
+
*/
|
|
1349
|
+
getServerStats() {
|
|
1350
|
+
const uptime = this.serverStartTime ? Date.now() - this.serverStartTime.getTime() : 0;
|
|
1351
|
+
const memory = process4.memoryUsage();
|
|
1352
|
+
return {
|
|
1353
|
+
status: this.serverStatus,
|
|
1354
|
+
uptime: this.formatUptime(uptime),
|
|
1355
|
+
memory: {
|
|
1356
|
+
rss: this.formatBytes(memory.rss),
|
|
1357
|
+
heapTotal: this.formatBytes(memory.heapTotal),
|
|
1358
|
+
heapUsed: this.formatBytes(memory.heapUsed),
|
|
1359
|
+
external: this.formatBytes(memory.external)
|
|
1360
|
+
},
|
|
1361
|
+
performance: {
|
|
1362
|
+
cpuUsage: process4.cpuUsage(),
|
|
1363
|
+
resourceUsage: process4.resourceUsage?.()
|
|
1364
|
+
},
|
|
1365
|
+
hmrStats: {
|
|
1366
|
+
enabled: this.getEnvironment.hmrEnabled,
|
|
1367
|
+
restartCount: this.hmrRestartCount,
|
|
1368
|
+
lastRestartTime: this.lastHmrRestartTime,
|
|
1369
|
+
watchingFiles: this.getEnvironment.hmrWatchPatterns?.length || 0
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Formats milliseconds into a human-readable uptime string.
|
|
1375
|
+
*
|
|
1376
|
+
* @method formatUptime
|
|
1377
|
+
* @private
|
|
1378
|
+
*
|
|
1379
|
+
* @param {number} ms - Milliseconds to format
|
|
1380
|
+
*
|
|
1381
|
+
* @returns {string} Formatted uptime string
|
|
1382
|
+
*
|
|
1383
|
+
* @example
|
|
1384
|
+
* formatUptime(3661000) // returns "1h 1m 1s"
|
|
1385
|
+
*/
|
|
1386
|
+
formatUptime(ms) {
|
|
1387
|
+
const seconds = Math.floor(ms / 1e3);
|
|
1388
|
+
const minutes = Math.floor(seconds / 60);
|
|
1389
|
+
const hours = Math.floor(minutes / 60);
|
|
1390
|
+
const days = Math.floor(hours / 24);
|
|
1391
|
+
if (days > 0) {
|
|
1392
|
+
return `${days}d ${hours % 24}h ${minutes % 60}m`;
|
|
1393
|
+
} else if (hours > 0) {
|
|
1394
|
+
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
1395
|
+
} else if (minutes > 0) {
|
|
1396
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
867
1397
|
} else {
|
|
868
|
-
|
|
1398
|
+
return `${seconds}s`;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Formats bytes into a human-readable size string.
|
|
1403
|
+
*
|
|
1404
|
+
* @method formatBytes
|
|
1405
|
+
* @private
|
|
1406
|
+
*
|
|
1407
|
+
* @param {number} bytes - Bytes to format
|
|
1408
|
+
*
|
|
1409
|
+
* @returns {string} Formatted size string
|
|
1410
|
+
*
|
|
1411
|
+
* @example
|
|
1412
|
+
* formatBytes(1048576) // returns "1.00 MB"
|
|
1413
|
+
*/
|
|
1414
|
+
formatBytes(bytes) {
|
|
1415
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
1416
|
+
let value = bytes;
|
|
1417
|
+
let unitIndex = 0;
|
|
1418
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
1419
|
+
value /= 1024;
|
|
1420
|
+
unitIndex++;
|
|
869
1421
|
}
|
|
1422
|
+
return `${value.toFixed(2)} ${units[unitIndex]}`;
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Checks if HMR is currently active.
|
|
1426
|
+
*
|
|
1427
|
+
* @method isHMRActive
|
|
1428
|
+
* @public
|
|
1429
|
+
*
|
|
1430
|
+
* @returns {boolean} True if HMR is enabled and watching files, false otherwise
|
|
1431
|
+
*/
|
|
1432
|
+
isHMRActive() {
|
|
1433
|
+
return this.getEnvironment.hmrEnabled && this.fileWatcher !== null;
|
|
1434
|
+
}
|
|
1435
|
+
/**
|
|
1436
|
+
* Gets detailed HMR information and status.
|
|
1437
|
+
*
|
|
1438
|
+
* @method getHMRInfo
|
|
1439
|
+
* @public
|
|
1440
|
+
*
|
|
1441
|
+
* @returns {any} HMR information object
|
|
1442
|
+
*
|
|
1443
|
+
* @remarks
|
|
1444
|
+
* Includes:
|
|
1445
|
+
* - Configuration settings from .env
|
|
1446
|
+
* - Current restart count
|
|
1447
|
+
* - Watcher status
|
|
1448
|
+
*/
|
|
1449
|
+
getHMRInfo() {
|
|
1450
|
+
return {
|
|
1451
|
+
enabled: this.getEnvironment.hmrEnabled,
|
|
1452
|
+
watchPatterns: this.getEnvironment.hmrWatchPatterns,
|
|
1453
|
+
ignorePatterns: this.getEnvironment.hmrIgnorePatterns,
|
|
1454
|
+
debounceMs: this.getEnvironment.hmrDebounceMs,
|
|
1455
|
+
maxRestarts: this.getEnvironment.hmrMaxRestarts,
|
|
1456
|
+
autoRestart: this.getEnvironment.hmrAutoRestarts,
|
|
1457
|
+
currentRestartCount: this.hmrRestartCount,
|
|
1458
|
+
lastRestartTime: this.lastHmrRestartTime,
|
|
1459
|
+
isWatching: this.fileWatcher !== null,
|
|
1460
|
+
isRestartPending: this.hmrRestartPending
|
|
1461
|
+
};
|
|
870
1462
|
}
|
|
871
1463
|
};
|
|
872
1464
|
export {
|