opticore-webapp 1.0.69 → 1.0.71
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 +575 -1042
- package/dist/index.d.cts +136 -438
- package/dist/index.d.ts +136 -438
- package/dist/index.js +608 -1084
- package/dist/utils/translations/message.translation.en.json +8 -15
- package/dist/utils/translations/message.translation.fr.json +8 -4
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,178 +1,82 @@
|
|
|
1
1
|
// src/core/webServer.core.ts
|
|
2
|
-
import * as
|
|
3
|
-
import
|
|
2
|
+
import * as path3 from "path";
|
|
3
|
+
import process6 from "process";
|
|
4
4
|
import corsOrigin from "cors";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
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";
|
|
5
|
+
import { CEventNameError as eventName2, ServerListenEventError as ServerListenEventError2 } from "opticore-catch-exception-error";
|
|
6
|
+
import { express as express2 } from "opticore-express";
|
|
7
|
+
import { getEnvironmentValue as getEnvironmentValue2 } from "opticore-env-access";
|
|
15
8
|
import { HttpStatusCode as HttpStatusCode3 } from "opticore-http-response";
|
|
16
|
-
import { TranslationLoader as
|
|
9
|
+
import { TranslationLoader as TranslationLoader3 } from "opticore-translator";
|
|
10
|
+
import { requestCallsEvent } from "opticore-request-call-event";
|
|
17
11
|
|
|
18
12
|
// src/core/handlers/eventProcess.handler.ts
|
|
19
|
-
import
|
|
13
|
+
import process2 from "process";
|
|
20
14
|
import EventEmitter from "events";
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
CEvent as event
|
|
25
|
-
} from "opticore-catch-exception-error";
|
|
26
|
-
|
|
27
|
-
// src/utils/isTransformError.utils.ts
|
|
28
|
-
var isTransformErrorUtils = (error) => {
|
|
29
|
-
return error?.name === "TransformError" || error?.message?.includes("Transform failed") || error?.message?.includes("esbuild");
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// src/core/handlers/eventProcess.handler.ts
|
|
33
|
-
function eventProcessHandler(localeLanguage, expressApp) {
|
|
15
|
+
import { express } from "opticore-express";
|
|
16
|
+
import { ServerListenEventError, CEventNameError as eventName, CEvent as event } from "opticore-catch-exception-error";
|
|
17
|
+
function eventProcessHandler(localeLanguage) {
|
|
34
18
|
const errorEmitter = new EventEmitter();
|
|
19
|
+
const app = express();
|
|
35
20
|
const serverListenEvent = new ServerListenEventError(localeLanguage);
|
|
36
|
-
console.log("[Server] Setting up error event handlers...");
|
|
37
21
|
errorEmitter.on(eventName.error, (error) => {
|
|
38
|
-
console.error("[Server] EventEmitter error caught:", error.message);
|
|
39
|
-
if (isTransformErrorUtils(error)) {
|
|
40
|
-
handleTransformError(error);
|
|
41
|
-
}
|
|
42
22
|
serverListenEvent.listenerError(error);
|
|
43
23
|
});
|
|
44
|
-
|
|
45
|
-
console.error("[Server] ============================================");
|
|
46
|
-
console.error("[Server] TRANSFORM ERROR DETECTED");
|
|
47
|
-
console.error("[Server] ============================================");
|
|
48
|
-
console.error("[Server] Message:", error.message);
|
|
49
|
-
console.error("[Server] Stack:", error.stack);
|
|
50
|
-
if (error.errors && Array.isArray(error.errors)) {
|
|
51
|
-
error.errors.forEach((err, index) => {
|
|
52
|
-
console.error(`[Server] Error ${index + 1}:`, err.text);
|
|
53
|
-
if (err.location) {
|
|
54
|
-
console.error(`[Server] File: ${err.location.file}`);
|
|
55
|
-
console.error(`[Server] Line: ${err.location.line}, Column: ${err.location.column}`);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
const fileMatch = error.message.match(/([^:]+\.ts):(\d+):(\d+):/);
|
|
60
|
-
if (fileMatch) {
|
|
61
|
-
console.error("[Server] Problem file:", fileMatch[1]);
|
|
62
|
-
console.error("[Server] Line:", fileMatch[2], "Column:", fileMatch[3]);
|
|
63
|
-
}
|
|
64
|
-
const errorDetailMatch = error.message.match(/ERROR: (.+)$/m);
|
|
65
|
-
if (errorDetailMatch) {
|
|
66
|
-
console.error("[Server] Error detail:", errorDetailMatch[1]);
|
|
67
|
-
}
|
|
68
|
-
console.error("[Server] ============================================");
|
|
69
|
-
if (process.send) {
|
|
70
|
-
process.send({
|
|
71
|
-
type: "TRANSFORM_ERROR",
|
|
72
|
-
error: error.message,
|
|
73
|
-
stack: error.stack,
|
|
74
|
-
errorName: error.name,
|
|
75
|
-
details: error.errors,
|
|
76
|
-
timestamp: Date.now()
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
process.on(event.uncaughtException, (error) => {
|
|
81
|
-
console.error("[Server] UNCAUGHT EXCEPTION:", error.message);
|
|
82
|
-
console.error("[Server] Stack:", error.stack);
|
|
83
|
-
if (isTransformErrorUtils(error)) {
|
|
84
|
-
errorEmitter.emit("transformError", error);
|
|
85
|
-
}
|
|
86
|
-
serverListenEvent.uncaughtException(error);
|
|
87
|
-
if (process.send) {
|
|
88
|
-
const messageType = isTransformErrorUtils(error) ? "TRANSFORM_ERROR" : "HOT_RELOAD_ERROR";
|
|
89
|
-
process.send({
|
|
90
|
-
type: messageType,
|
|
91
|
-
error: error.message,
|
|
92
|
-
stack: error.stack,
|
|
93
|
-
errorName: error.name,
|
|
94
|
-
timestamp: Date.now()
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
process.on(event.unhandledRejection, (reason, promise) => {
|
|
99
|
-
console.error("[Server] UNHANDLED REJECTION:", reason);
|
|
100
|
-
if (isTransformErrorUtils(reason)) {
|
|
101
|
-
errorEmitter.emit("transformError", reason);
|
|
102
|
-
}
|
|
103
|
-
serverListenEvent.unhandledRejection(reason, promise);
|
|
104
|
-
if (process.send) {
|
|
105
|
-
const messageType = isTransformErrorUtils(reason) ? "TRANSFORM_ERROR" : "HOT_RELOAD_ERROR";
|
|
106
|
-
process.send({
|
|
107
|
-
type: messageType,
|
|
108
|
-
error: reason?.message || String(reason),
|
|
109
|
-
errorName: reason?.name,
|
|
110
|
-
timestamp: Date.now()
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
process.on(event.beforeExit, (code) => {
|
|
24
|
+
process2.on(event.beforeExit, (code) => {
|
|
115
25
|
setTimeout(() => {
|
|
116
26
|
serverListenEvent.processBeforeExit(code);
|
|
117
27
|
}, 100);
|
|
118
28
|
});
|
|
119
|
-
|
|
29
|
+
process2.on(event.disconnect, () => {
|
|
120
30
|
serverListenEvent.processDisconnected();
|
|
121
31
|
});
|
|
122
|
-
|
|
32
|
+
process2.on(event.exit, (code) => {
|
|
123
33
|
serverListenEvent.exited(code);
|
|
124
34
|
});
|
|
125
|
-
|
|
35
|
+
process2.on(event.rejectionHandled, (promise) => {
|
|
126
36
|
serverListenEvent.promiseRejectionHandled(promise);
|
|
127
37
|
});
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
38
|
+
process2.on(event.uncaughtException, (error) => {
|
|
39
|
+
serverListenEvent.uncaughtException(error);
|
|
40
|
+
});
|
|
41
|
+
process2.on(event.uncaughtExceptionMonitor, (error) => {
|
|
133
42
|
serverListenEvent.uncaughtExceptionMonitor(error);
|
|
134
43
|
});
|
|
135
|
-
|
|
44
|
+
process2.on(event.unhandledRejection, (reason, promise) => {
|
|
45
|
+
serverListenEvent.unhandledRejection(reason, promise);
|
|
46
|
+
});
|
|
47
|
+
process2.on(event.warning, (warning) => {
|
|
136
48
|
serverListenEvent.warning(warning);
|
|
137
49
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
serverListenEvent.message(message);
|
|
141
|
-
}
|
|
50
|
+
process2.on(event.message, (message) => {
|
|
51
|
+
serverListenEvent.message(message);
|
|
142
52
|
});
|
|
143
|
-
|
|
53
|
+
process2.on(event.multipleResolves, (type, promise, reason) => {
|
|
144
54
|
serverListenEvent.multipleResolves(type, promise, reason);
|
|
145
55
|
});
|
|
146
|
-
|
|
56
|
+
process2.on(event.sigint, () => {
|
|
147
57
|
serverListenEvent.processInterrupted();
|
|
148
58
|
});
|
|
149
|
-
|
|
59
|
+
process2.on(event.sigterm, (signal) => {
|
|
150
60
|
serverListenEvent.sigtermSignalReceived(signal);
|
|
151
61
|
});
|
|
152
|
-
|
|
153
|
-
console.error("[Server] Express error middleware triggered:", err.message);
|
|
154
|
-
console.error("[Server] Request URL:", req.url);
|
|
155
|
-
console.error("[Server] Request method:", req.method);
|
|
156
|
-
errorEmitter.emit(eventName.error, err);
|
|
62
|
+
app.use((err, req, res, next) => {
|
|
157
63
|
serverListenEvent.expressErrorHandlingMiddleware(errorEmitter, err, req, res, next);
|
|
158
64
|
});
|
|
159
|
-
console.log("[Server] Error event handlers configured successfully");
|
|
160
|
-
return errorEmitter;
|
|
161
65
|
}
|
|
162
66
|
|
|
163
67
|
// src/application/service/core.service.ts
|
|
164
|
-
import
|
|
68
|
+
import process4 from "process";
|
|
165
69
|
import chalk from "chalk";
|
|
166
70
|
import * as path from "path";
|
|
167
71
|
import * as fs from "fs";
|
|
168
72
|
import colors from "ansi-colors";
|
|
169
73
|
import { HttpStatusCode, HttpStatusCode as status } from "opticore-http-response";
|
|
170
74
|
import { TranslationLoader } from "opticore-translator";
|
|
171
|
-
import {
|
|
75
|
+
import { getEnvironmentValue } from "opticore-env-access";
|
|
172
76
|
|
|
173
77
|
// src/utils/envPath.utils.ts
|
|
174
|
-
import
|
|
175
|
-
var envPath =
|
|
78
|
+
import process3 from "process";
|
|
79
|
+
var envPath = process3.cwd() + "/config/env/.env";
|
|
176
80
|
|
|
177
81
|
// src/application/service/loaderTranslationFile.service.ts
|
|
178
82
|
import { translationLoaderConfig } from "opticore-loader-translation";
|
|
@@ -209,18 +113,20 @@ var dependenciesContainerProvider = (localLang) => {
|
|
|
209
113
|
};
|
|
210
114
|
|
|
211
115
|
// src/application/service/core.service.ts
|
|
212
|
-
import {
|
|
116
|
+
import { SContainer as SContainer2 } from "opticore-dependency-inject";
|
|
213
117
|
|
|
214
|
-
// src/
|
|
215
|
-
var
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
118
|
+
// src/utils/parsingDateTime.utils.ts
|
|
119
|
+
var parsingDateTimeUtils = (localFormatTime) => {
|
|
120
|
+
const now = /* @__PURE__ */ new Date();
|
|
121
|
+
const formatter = new Intl.DateTimeFormat(localFormatTime, {
|
|
122
|
+
day: "2-digit",
|
|
123
|
+
month: "2-digit",
|
|
124
|
+
year: "numeric",
|
|
125
|
+
hour: "2-digit",
|
|
126
|
+
minute: "2-digit",
|
|
127
|
+
hour12: false
|
|
128
|
+
});
|
|
129
|
+
return formatter.format(now);
|
|
224
130
|
};
|
|
225
131
|
|
|
226
132
|
// src/application/service/core.service.ts
|
|
@@ -229,10 +135,12 @@ var CoreService = class {
|
|
|
229
135
|
environmentPath;
|
|
230
136
|
serverLog;
|
|
231
137
|
logger;
|
|
138
|
+
container;
|
|
232
139
|
constructor(localLang, environmentPath) {
|
|
140
|
+
this.loadTranslationFiles();
|
|
233
141
|
this.environmentPath = environmentPath;
|
|
234
142
|
this.localLanguage = localLang;
|
|
235
|
-
|
|
143
|
+
this.container = new SContainer2(localLang);
|
|
236
144
|
this.serverLog = dependenciesContainerProvider(localLang).resolve("OpticoreLogger");
|
|
237
145
|
this.logger = dependenciesContainerProvider(localLang).resolve("LoggerCore");
|
|
238
146
|
}
|
|
@@ -246,11 +154,46 @@ var CoreService = class {
|
|
|
246
154
|
formatMemoryUsage(data) {
|
|
247
155
|
return `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;
|
|
248
156
|
}
|
|
157
|
+
loadTranslationFiles() {
|
|
158
|
+
loaderTranslationFile(this.localLanguage);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
*
|
|
162
|
+
* @param filePath
|
|
163
|
+
* @private
|
|
164
|
+
*
|
|
165
|
+
* Return void
|
|
166
|
+
*/
|
|
167
|
+
getEnvFileLoading(filePath) {
|
|
168
|
+
this.loadTranslationFiles();
|
|
169
|
+
try {
|
|
170
|
+
const fullPath = path.resolve(process4.cwd(), filePath);
|
|
171
|
+
if (fs.existsSync(fullPath)) {
|
|
172
|
+
const env = fs.readFileSync(fullPath, "utf-8");
|
|
173
|
+
const lines = env.split("\n");
|
|
174
|
+
lines.forEach((line) => {
|
|
175
|
+
const match = line.match(/^([^#=]+)=([^#]+)$/);
|
|
176
|
+
if (match) {
|
|
177
|
+
const key = match[1].trim();
|
|
178
|
+
process4.env[key] = match[2].trim();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
} catch (err) {
|
|
183
|
+
this.logger.error({
|
|
184
|
+
message: err.message,
|
|
185
|
+
title: TranslationLoader.t("EnvFileLoading", this.localLanguage),
|
|
186
|
+
errorType: err.code,
|
|
187
|
+
stackTrace: err.stack,
|
|
188
|
+
httpCodeValue: status.INTERNAL_SERVER_ERROR
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
249
192
|
/**
|
|
250
193
|
* Returns an Object containing a node version, openssl, and v0
|
|
251
194
|
*/
|
|
252
195
|
getVersions() {
|
|
253
|
-
const { node, openssl, v8 } =
|
|
196
|
+
const { node, openssl, v8 } = process4.versions;
|
|
254
197
|
const data = {
|
|
255
198
|
"node version": node,
|
|
256
199
|
"openssl": openssl,
|
|
@@ -270,15 +213,15 @@ var CoreService = class {
|
|
|
270
213
|
* and Memory usage system
|
|
271
214
|
*/
|
|
272
215
|
getUsageMemory() {
|
|
273
|
-
|
|
274
|
-
const memoryData =
|
|
216
|
+
this.loadTranslationFiles();
|
|
217
|
+
const memoryData = process4.memoryUsage();
|
|
275
218
|
const data = {
|
|
276
219
|
[TranslationLoader.t("totalMemoryAllocated", this.localLanguage)]: this.formatMemoryUsage(memoryData.rss),
|
|
277
220
|
[TranslationLoader.t("sizeAllocatedHeap", this.localLanguage)]: this.formatMemoryUsage(memoryData.heapTotal),
|
|
278
221
|
[TranslationLoader.t("memoryUsedExecution", this.localLanguage)]: this.formatMemoryUsage(memoryData.heapUsed),
|
|
279
222
|
[TranslationLoader.t("externalMemory", this.localLanguage)]: this.formatMemoryUsage(memoryData.external),
|
|
280
|
-
[TranslationLoader.t("memoryUsageUser", this.localLanguage)]: this.formatMemoryUsage(
|
|
281
|
-
[TranslationLoader.t("memoryUsageSystem", this.localLanguage)]: this.formatMemoryUsage(
|
|
223
|
+
[TranslationLoader.t("memoryUsageUser", this.localLanguage)]: this.formatMemoryUsage(process4.cpuUsage().user),
|
|
224
|
+
[TranslationLoader.t("memoryUsageSystem", this.localLanguage)]: this.formatMemoryUsage(process4.cpuUsage().system)
|
|
282
225
|
};
|
|
283
226
|
return {
|
|
284
227
|
"rss": data[TranslationLoader.t("totalMemoryAllocated", this.localLanguage)],
|
|
@@ -287,52 +230,21 @@ var CoreService = class {
|
|
|
287
230
|
"external": data[TranslationLoader.t("externalMemory", this.localLanguage)],
|
|
288
231
|
"user": data[TranslationLoader.t("memoryUsageUser", this.localLanguage)],
|
|
289
232
|
"system": data[TranslationLoader.t("memoryUsageSystem", this.localLanguage)],
|
|
290
|
-
"pid":
|
|
233
|
+
"pid": process4.pid
|
|
291
234
|
};
|
|
292
235
|
}
|
|
293
236
|
/**
|
|
294
237
|
* Return an Object containing a project path and running server time
|
|
295
238
|
*/
|
|
296
239
|
getProjectInfo() {
|
|
297
|
-
const startTime =
|
|
298
|
-
const endTime =
|
|
240
|
+
const startTime = process4.hrtime();
|
|
241
|
+
const endTime = process4.hrtime(startTime);
|
|
299
242
|
const executionTime = (endTime[0] * 1e9 + endTime[1]) / 1e6;
|
|
300
243
|
return {
|
|
301
|
-
"projectPath": path.join(
|
|
244
|
+
"projectPath": path.join(process4.cwd()),
|
|
302
245
|
"startingTime": `${executionTime.toFixed(5)} ms`
|
|
303
246
|
};
|
|
304
247
|
}
|
|
305
|
-
/**
|
|
306
|
-
*
|
|
307
|
-
* @param filePath
|
|
308
|
-
* @private
|
|
309
|
-
*
|
|
310
|
-
* Return void
|
|
311
|
-
*/
|
|
312
|
-
getEnvFileLoading(filePath) {
|
|
313
|
-
try {
|
|
314
|
-
const fullPath = path.resolve(process3.cwd(), filePath);
|
|
315
|
-
if (fs.existsSync(fullPath)) {
|
|
316
|
-
const env = fs.readFileSync(fullPath, "utf-8");
|
|
317
|
-
const lines = env.split("\n");
|
|
318
|
-
lines.forEach((line) => {
|
|
319
|
-
const match = line.match(/^([^#=]+)=([^#]+)$/);
|
|
320
|
-
if (match) {
|
|
321
|
-
const key = match[1].trim();
|
|
322
|
-
process3.env[key] = match[2].trim();
|
|
323
|
-
}
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
} catch (err) {
|
|
327
|
-
this.logger.error({
|
|
328
|
-
message: err.message,
|
|
329
|
-
title: TranslationLoader.t("EnvFileLoading", this.localLanguage),
|
|
330
|
-
errorType: err.code,
|
|
331
|
-
stackTrace: err.stack,
|
|
332
|
-
httpCodeValue: status.INTERNAL_SERVER_ERROR
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
248
|
/**
|
|
337
249
|
*
|
|
338
250
|
* @param development
|
|
@@ -341,14 +253,15 @@ var CoreService = class {
|
|
|
341
253
|
* Return string
|
|
342
254
|
*/
|
|
343
255
|
getServerRunningMode(development, production) {
|
|
256
|
+
this.loadTranslationFiles();
|
|
344
257
|
try {
|
|
345
|
-
loaderTranslationFile(this.localLanguage);
|
|
346
258
|
this.getEnvFileLoading(".env");
|
|
347
|
-
const env =
|
|
348
|
-
const isDevelopment = env.devEnv === development
|
|
259
|
+
const env = getEnvironmentValue(path.join(envPath));
|
|
260
|
+
const isDevelopment = env.devEnv === development;
|
|
261
|
+
const isProd = env.prodEnv === production;
|
|
349
262
|
if (isDevelopment) {
|
|
350
263
|
return `${TranslationLoader.t("serverRunning", this.localLanguage)} ${colors.bgBlue(`${colors.bold(`${development}`)}`)} mode`;
|
|
351
|
-
} else if (
|
|
264
|
+
} else if (isProd) {
|
|
352
265
|
return `${TranslationLoader.t("serverRunning", this.localLanguage)} ${colors.bgBlue(`${colors.bold(`${production}`)}`)} mode`;
|
|
353
266
|
} else {
|
|
354
267
|
return `${TranslationLoader.t("serverRunning", this.localLanguage)} ${colors.bgBlue(`${colors.bold(`${development}`)}`)} mode`;
|
|
@@ -364,10 +277,10 @@ var CoreService = class {
|
|
|
364
277
|
}
|
|
365
278
|
}
|
|
366
279
|
infoServer(host, port) {
|
|
280
|
+
this.loadTranslationFiles();
|
|
367
281
|
try {
|
|
368
|
-
|
|
369
|
-
const
|
|
370
|
-
const msg5 = getEnvironment.protocolTransfert === "" ? colors.underline(`http://${host}:${port}`) : colors.underline(`${getEnvironment.protocolTransfert}://${host}:${port}`);
|
|
282
|
+
const getEnvironment = getEnvironmentValue(this.environmentPath);
|
|
283
|
+
const msg5 = getEnvironment.protocolTransfer === "" ? colors.underline(`http://${host}:${port}`) : colors.underline(`${getEnvironment.protocolTransfer}://${host}:${port}`);
|
|
371
284
|
const messages = [
|
|
372
285
|
TranslationLoader.t("webServerListening", this.localLanguage),
|
|
373
286
|
TranslationLoader.t("webServerUsingNodeVersion", this.localLanguage, { nodeVersion: this.getVersions().nodeVersion }),
|
|
@@ -377,7 +290,7 @@ var CoreService = class {
|
|
|
377
290
|
const maxLength = Math.max(...messages.map((m) => {
|
|
378
291
|
return m.replace(/\u001b\[[0-9]{1,2}m/g, "").length;
|
|
379
292
|
})) + 4;
|
|
380
|
-
console.log(chalk.blackBright(`${TranslationLoader.t("tailingServerLog", this.localLanguage)} (${path.join(path.basename(
|
|
293
|
+
console.log(chalk.blackBright(`${TranslationLoader.t("tailingServerLog", this.localLanguage)} (${path.join(path.basename(process4.cwd()), "logs", "app.log")})`));
|
|
381
294
|
const border = chalk.bgGreen.white(" ".repeat(maxLength));
|
|
382
295
|
console.log(border);
|
|
383
296
|
messages.forEach((msg) => {
|
|
@@ -387,20 +300,33 @@ var CoreService = class {
|
|
|
387
300
|
});
|
|
388
301
|
console.log(border);
|
|
389
302
|
console.log("\n");
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
title: TranslationLoader.t("serverRunningTitle", this.localLanguage),
|
|
393
|
-
message: TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
|
|
394
|
-
});
|
|
395
|
-
SLogger(this.localLanguage).serverLog.serverLog({
|
|
396
|
-
timestamp: (/* @__PURE__ */ new Date()).toString(),
|
|
303
|
+
this.serverLog.serverLog({
|
|
304
|
+
timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
|
|
397
305
|
level: "SERVER",
|
|
398
306
|
title: TranslationLoader.t("serverRunningTitle", this.localLanguage),
|
|
399
307
|
typeName: TranslationLoader.t("opticoreServerTypeName", this.localLanguage),
|
|
400
308
|
message: TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
|
|
401
309
|
});
|
|
310
|
+
const dependenciesList = this.container.listDependencies();
|
|
311
|
+
if (dependenciesList.length > 0) {
|
|
312
|
+
this.serverLog.opticoreLog({
|
|
313
|
+
level: "OPTICORE",
|
|
314
|
+
message: TranslationLoader.t("DEPENDENCIES_STORED", this.localLanguage, { dependenciesList: dependenciesList.join(", ") }),
|
|
315
|
+
timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
|
|
316
|
+
title: TranslationLoader.t("DEPENDENCIES_CONTAINER", this.localLanguage),
|
|
317
|
+
typeName: "DEPENDENCIES_CONTAINER"
|
|
318
|
+
});
|
|
319
|
+
} else {
|
|
320
|
+
this.serverLog.opticoreLog({
|
|
321
|
+
level: "OPTICORE",
|
|
322
|
+
message: TranslationLoader.t("NO_DEPENDENCIES_STORED", this.localLanguage),
|
|
323
|
+
timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
|
|
324
|
+
title: TranslationLoader.t("DEPENDENCIES_CONTAINER", this.localLanguage),
|
|
325
|
+
typeName: "DEPENDENCIES_CONTAINER"
|
|
326
|
+
});
|
|
327
|
+
}
|
|
402
328
|
} catch (err) {
|
|
403
|
-
|
|
329
|
+
this.logger.error({
|
|
404
330
|
message: err.message,
|
|
405
331
|
title: TranslationLoader.t("server", this.localLanguage),
|
|
406
332
|
errorType: err.code,
|
|
@@ -419,6 +345,20 @@ import {
|
|
|
419
345
|
CCodeError,
|
|
420
346
|
CErrorName
|
|
421
347
|
} from "opticore-catch-exception-error";
|
|
348
|
+
|
|
349
|
+
// src/application/service/logger.service.ts
|
|
350
|
+
var SLogger = (localLang) => {
|
|
351
|
+
return {
|
|
352
|
+
get serverLog() {
|
|
353
|
+
return dependenciesContainerProvider(localLang).resolve("OpticoreLogger");
|
|
354
|
+
},
|
|
355
|
+
get logger() {
|
|
356
|
+
return dependenciesContainerProvider(localLang).resolve("LoggerCore");
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// src/application/service/serverStartError.service.ts
|
|
422
362
|
import { HttpStatusCode as HttpStatusCode2 } from "opticore-http-response";
|
|
423
363
|
var SServerStartError = (err, environmentPath) => {
|
|
424
364
|
if (err.name) {
|
|
@@ -450,391 +390,165 @@ var SServerStartError = (err, environmentPath) => {
|
|
|
450
390
|
}
|
|
451
391
|
};
|
|
452
392
|
|
|
393
|
+
// src/core/webServer.core.ts
|
|
394
|
+
import { SContainer as SContainer3 } from "opticore-dependency-inject";
|
|
395
|
+
|
|
396
|
+
// src/application/service/getEnvFileLoading.service.ts
|
|
397
|
+
import path2 from "path";
|
|
398
|
+
import process5 from "process";
|
|
399
|
+
import fs2 from "fs";
|
|
400
|
+
import { TranslationLoader as TranslationLoader2 } from "opticore-translator";
|
|
401
|
+
import { HttpStatusCode as status2 } from "opticore-http-response";
|
|
402
|
+
var getEnvFileLoadingService = (filePath, localLanguage) => {
|
|
403
|
+
loaderTranslationFile(localLanguage);
|
|
404
|
+
const logger = dependenciesContainerProvider(localLanguage).resolve("LoggerCore");
|
|
405
|
+
try {
|
|
406
|
+
const fullPath = path2.resolve(process5.cwd(), filePath);
|
|
407
|
+
if (fs2.existsSync(fullPath)) {
|
|
408
|
+
const env = fs2.readFileSync(fullPath, "utf-8");
|
|
409
|
+
const lines = env.split("\n");
|
|
410
|
+
lines.forEach((line) => {
|
|
411
|
+
const match = line.match(/^([^#=]+)=([^#]+)$/);
|
|
412
|
+
if (match) {
|
|
413
|
+
const key = match[1].trim();
|
|
414
|
+
process5.env[key] = match[2].trim();
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
} catch (err) {
|
|
419
|
+
logger.error({
|
|
420
|
+
message: err.message,
|
|
421
|
+
title: TranslationLoader2.t("EnvFileLoading", localLanguage),
|
|
422
|
+
errorType: err.code,
|
|
423
|
+
stackTrace: err.stack,
|
|
424
|
+
httpCodeValue: status2.INTERNAL_SERVER_ERROR
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
453
429
|
// src/core/webServer.core.ts
|
|
454
430
|
var WebServerCore = class {
|
|
455
431
|
serverUtility;
|
|
456
|
-
expressApp;
|
|
432
|
+
expressApp = express2();
|
|
433
|
+
container;
|
|
457
434
|
localLanguage;
|
|
458
435
|
loggerConfig;
|
|
459
|
-
|
|
436
|
+
routerExpressApp;
|
|
437
|
+
getEnvironmentValue;
|
|
460
438
|
environmentPath;
|
|
461
439
|
serverListenEvent;
|
|
462
|
-
|
|
463
|
-
currentRoutes = [];
|
|
464
|
-
currentDependencies = [];
|
|
465
|
-
server = void 0;
|
|
466
|
-
errorEmitter;
|
|
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
|
-
*/
|
|
440
|
+
dependenciesRegistered = false;
|
|
502
441
|
constructor(paramsConstructor) {
|
|
503
|
-
this.
|
|
442
|
+
this.loadTranslationFiles();
|
|
443
|
+
this.stackTraceErrorHandling();
|
|
444
|
+
this.getEnvironmentValue = getEnvironmentValue2(paramsConstructor.environmentPath);
|
|
445
|
+
this.routerExpressApp = paramsConstructor.app;
|
|
504
446
|
this.loggerConfig = paramsConstructor.loggerConfig;
|
|
505
447
|
this.localLanguage = paramsConstructor.localLanguage;
|
|
506
448
|
this.environmentPath = paramsConstructor.environmentPath;
|
|
507
|
-
this.
|
|
508
|
-
this.
|
|
509
|
-
this.expressApp.use(
|
|
510
|
-
this.expressApp.use(
|
|
511
|
-
this.expressApp.use(
|
|
512
|
-
this.expressApp.use(express.urlencoded({ extended: true }));
|
|
449
|
+
this.container = new SContainer3(paramsConstructor.localLanguage);
|
|
450
|
+
this.expressApp.use(express2.json());
|
|
451
|
+
this.expressApp.use(express2.raw());
|
|
452
|
+
this.expressApp.use(express2.text());
|
|
453
|
+
this.expressApp.use(express2.urlencoded({ extended: true }));
|
|
513
454
|
this.expressApp.use(corsOrigin(paramsConstructor.corsOriginOptions));
|
|
455
|
+
this.serverListenEvent = new ServerListenEventError2(paramsConstructor.localLanguage);
|
|
514
456
|
this.serverUtility = new CoreService(paramsConstructor.localLanguage, paramsConstructor.environmentPath);
|
|
515
|
-
this.serverStatus = "STARTING";
|
|
516
|
-
this.serverStartTime = /* @__PURE__ */ new Date();
|
|
517
|
-
this.setupProcessEventListeners();
|
|
518
457
|
}
|
|
519
458
|
/**
|
|
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
459
|
*
|
|
529
|
-
* @
|
|
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
|
-
* ```
|
|
460
|
+
* @param dependencies
|
|
550
461
|
*/
|
|
551
|
-
|
|
462
|
+
registerDependencies(dependencies2) {
|
|
463
|
+
this.loadTranslationFiles();
|
|
552
464
|
try {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
if (!this.validateServerParameters()) {
|
|
559
|
-
return void 0;
|
|
560
|
-
}
|
|
561
|
-
const server = this.startHttpServer(databaseCallback);
|
|
562
|
-
if (this.getEnvironment.hmrEnabled) {
|
|
563
|
-
this.startHMR();
|
|
465
|
+
getEnvFileLoadingService(".env", this.localLanguage);
|
|
466
|
+
if (dependencies2 && dependencies2.length > 0) {
|
|
467
|
+
dependencies2.forEach((dependency) => {
|
|
468
|
+
this.container.register(dependency.key, dependency.factory, dependency.scope);
|
|
469
|
+
});
|
|
564
470
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
471
|
+
} catch (err) {
|
|
472
|
+
SLogger(this.environmentPath).logger.error({
|
|
473
|
+
message: err.message,
|
|
474
|
+
title: "Register Dependencies",
|
|
475
|
+
errorType: err.code,
|
|
476
|
+
stackTrace: err.stack,
|
|
477
|
+
httpCodeValue: HttpStatusCode3.INTERNAL_SERVER_ERROR
|
|
478
|
+
});
|
|
568
479
|
}
|
|
569
480
|
}
|
|
570
481
|
/**
|
|
571
|
-
* Validates server configuration parameters.
|
|
572
482
|
*
|
|
573
|
-
* @
|
|
574
|
-
* @
|
|
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)
|
|
483
|
+
* @param routers
|
|
484
|
+
* @param databaseCallback
|
|
485
|
+
* @param dependenciesProvider
|
|
584
486
|
*/
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
if (
|
|
487
|
+
onStartServer(routers, databaseCallback, dependenciesProvider) {
|
|
488
|
+
this.loadTranslationFiles();
|
|
489
|
+
getEnvFileLoadingService(".env", this.localLanguage);
|
|
490
|
+
if (this.getEnvironmentValue.appPort === "" && Number(this.getEnvironmentValue.appPort) === 0) {
|
|
491
|
+
this.serverListenEvent.hostPortUndefined(Number(this.getEnvironmentValue.appPort));
|
|
492
|
+
} else if (this.getEnvironmentValue.appHost === "") {
|
|
493
|
+
this.serverListenEvent.hostUndefined(this.getEnvironmentValue.appHost);
|
|
494
|
+
} else if (Number(this.getEnvironmentValue.appPort) === 0) {
|
|
589
495
|
this.serverListenEvent.portUndefined();
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
}
|
|
596
|
-
if (!this.localLanguage || this.localLanguage.trim() === "") {
|
|
597
|
-
SLogger(this.localLanguage).logger.error({
|
|
598
|
-
message: TranslationLoader2.t("noDefaultLocalLang", this.localLanguage),
|
|
599
|
-
title: TranslationLoader2.t("noLocalLang", this.localLanguage),
|
|
600
|
-
errorType: TranslationLoader2.t("localLangMissing", this.localLanguage),
|
|
496
|
+
} else if (this.localLanguage === "") {
|
|
497
|
+
SLogger(this.environmentPath).logger.error({
|
|
498
|
+
message: TranslationLoader3.t("noDefaultLocalLang", this.localLanguage),
|
|
499
|
+
title: TranslationLoader3.t("noLocalLang", this.localLanguage),
|
|
500
|
+
errorType: TranslationLoader3.t("localLangMissing", this.localLanguage),
|
|
601
501
|
stackTrace: void 0,
|
|
602
502
|
httpCodeValue: HttpStatusCode3.NOT_FOUND
|
|
603
503
|
});
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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, () => {
|
|
634
|
-
try {
|
|
635
|
-
this.configureServerComponents(databaseCallback);
|
|
636
|
-
this.serverStatus = "READY";
|
|
637
|
-
SLogger(this.localLanguage).logger.info({
|
|
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
|
-
})
|
|
643
|
-
});
|
|
644
|
-
} catch (err) {
|
|
645
|
-
this.handleServerConfigurationError(err);
|
|
504
|
+
} else {
|
|
505
|
+
return this.expressApp.listen(
|
|
506
|
+
Number(this.getEnvironmentValue.appPort),
|
|
507
|
+
() => {
|
|
508
|
+
this.loadTranslationFiles();
|
|
509
|
+
try {
|
|
510
|
+
databaseCallback(this.getEnvironmentValue);
|
|
511
|
+
if (dependenciesProvider) {
|
|
512
|
+
this.registerDependencies(dependenciesProvider);
|
|
513
|
+
}
|
|
514
|
+
this.container.loadServices();
|
|
515
|
+
this.expressApp.use(express2.static(path3.join(process6.cwd(), "public/template")));
|
|
516
|
+
this.registerRoutes(routers);
|
|
517
|
+
} catch (err) {
|
|
518
|
+
SServerStartError(err, this.environmentPath);
|
|
519
|
+
}
|
|
646
520
|
}
|
|
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
521
|
);
|
|
658
522
|
}
|
|
659
523
|
}
|
|
660
524
|
/**
|
|
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
525
|
*
|
|
670
|
-
* @
|
|
526
|
+
* @param serverWeb
|
|
671
527
|
*/
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
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();
|
|
528
|
+
onListeningOnServerEvent(serverWeb) {
|
|
529
|
+
this.loadTranslationFiles();
|
|
530
|
+
serverWeb.on(eventName2.error, (err) => {
|
|
531
|
+
this.serverListenEvent.onEventError(err);
|
|
532
|
+
}).on(eventName2.close, () => {
|
|
533
|
+
this.serverListenEvent.serverClosing();
|
|
534
|
+
}).on(eventName2.drop, () => {
|
|
535
|
+
this.serverListenEvent.dropNewConnection();
|
|
536
|
+
}).on(eventName2.listening, () => {
|
|
694
537
|
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
|
-
}
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
|
-
* Handles server configuration errors.
|
|
704
|
-
*
|
|
705
|
-
* @method handleServerConfigurationError
|
|
706
|
-
* @private
|
|
707
|
-
*
|
|
708
|
-
* @param {any} err - The error that occurred
|
|
709
|
-
*
|
|
710
|
-
* @returns {void}
|
|
711
|
-
*/
|
|
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);
|
|
720
|
-
}
|
|
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);
|
|
796
538
|
});
|
|
797
539
|
}
|
|
798
540
|
/**
|
|
799
|
-
* Sets up HTTP server event listeners.
|
|
800
|
-
*
|
|
801
|
-
* @method setupServerEventListeners
|
|
802
|
-
* @private
|
|
803
|
-
*
|
|
804
|
-
* @returns {void}
|
|
805
541
|
*
|
|
806
|
-
* @
|
|
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
|
|
542
|
+
* @param serverWeb
|
|
817
543
|
*/
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
this.server.on(eventName2.error, (err) => {
|
|
824
|
-
this.serverListenEvent.onEventError(err);
|
|
825
|
-
});
|
|
826
|
-
this.server.on(eventName2.close, () => {
|
|
827
|
-
this.serverListenEvent.serverClosing();
|
|
828
|
-
});
|
|
829
|
-
this.server.on(eventName2.drop, () => {
|
|
830
|
-
this.serverListenEvent.dropNewConnection();
|
|
831
|
-
});
|
|
832
|
-
this.server.on(eventName2.request, (req, res) => {
|
|
544
|
+
onRequestOnServerEvent(serverWeb) {
|
|
545
|
+
this.loadTranslationFiles();
|
|
546
|
+
serverWeb.on(eventName2.request, (req, res) => {
|
|
833
547
|
requestCallsEvent(
|
|
834
548
|
req,
|
|
835
549
|
res,
|
|
836
|
-
this.
|
|
837
|
-
Number(this.
|
|
550
|
+
this.getEnvironmentValue.appHost,
|
|
551
|
+
Number(this.getEnvironmentValue.appPort),
|
|
838
552
|
dateTimeFormattedUtils,
|
|
839
553
|
this.environmentPath,
|
|
840
554
|
this.localLanguage
|
|
@@ -842,626 +556,436 @@ var WebServerCore = class {
|
|
|
842
556
|
});
|
|
843
557
|
}
|
|
844
558
|
/**
|
|
845
|
-
* Sets up error handling middleware and event emitters.
|
|
846
559
|
*
|
|
847
|
-
* @method setupErrorHandling
|
|
848
560
|
* @private
|
|
849
|
-
*
|
|
850
|
-
* @returns {void}
|
|
851
561
|
*/
|
|
852
|
-
|
|
562
|
+
loadTranslationFiles() {
|
|
853
563
|
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
564
|
}
|
|
887
565
|
/**
|
|
888
|
-
* Registers routes with the Express application.
|
|
889
566
|
*
|
|
890
|
-
* @
|
|
567
|
+
* @param allFeatureRoutes
|
|
891
568
|
* @private
|
|
892
|
-
*
|
|
893
|
-
* @param {any[]} allFeatureRoutes - Array of feature routes to register
|
|
894
|
-
*
|
|
895
|
-
* @returns {void}
|
|
896
569
|
*/
|
|
897
570
|
registerRoutes(allFeatureRoutes) {
|
|
898
|
-
allFeatureRoutes.
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
});
|
|
903
|
-
}
|
|
571
|
+
allFeatureRoutes.map((router) => {
|
|
572
|
+
router.routes.map((route) => {
|
|
573
|
+
this.expressApp.use(route.path, route.handler);
|
|
574
|
+
});
|
|
904
575
|
});
|
|
905
576
|
}
|
|
906
577
|
/**
|
|
907
|
-
* Displays server information.
|
|
908
578
|
*
|
|
909
|
-
* @method infoWebApp
|
|
910
579
|
* @private
|
|
911
|
-
*
|
|
912
|
-
* @returns {void}
|
|
913
580
|
*/
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
this.serverUtility.infoServer(
|
|
917
|
-
this.getEnvironment.appHost,
|
|
918
|
-
Number(this.getEnvironment.appPort)
|
|
919
|
-
);
|
|
581
|
+
stackTraceErrorHandling() {
|
|
582
|
+
return eventProcessHandler(this.localLanguage);
|
|
920
583
|
}
|
|
921
584
|
/**
|
|
922
|
-
* Starts the Hot Module Replacement (HMR) file watching system.
|
|
923
585
|
*
|
|
924
|
-
* @method startHMR
|
|
925
586
|
* @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
587
|
*/
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
title: TranslationLoader2.t("HMR_DISABLED", this.localLanguage),
|
|
944
|
-
message: "HMR is disabled in configuration"
|
|
945
|
-
});
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
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
|
|
979
|
-
});
|
|
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();
|
|
983
|
-
SLogger(this.localLanguage).logger.info({
|
|
984
|
-
title: TranslationLoader2.t("HMR_STARTED", this.localLanguage),
|
|
985
|
-
message: TranslationLoader2.t("HMR_MONITORING_STARTED", this.localLanguage)
|
|
986
|
-
});
|
|
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
|
|
993
|
-
});
|
|
994
|
-
}
|
|
588
|
+
infoWebApp() {
|
|
589
|
+
this.loadTranslationFiles();
|
|
590
|
+
this.serverUtility.infoServer(
|
|
591
|
+
this.getEnvironmentValue.appHost,
|
|
592
|
+
Number(this.getEnvironmentValue.appPort)
|
|
593
|
+
);
|
|
995
594
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// src/hotReload/hotReload.watcher.ts
|
|
598
|
+
import { fork, spawn } from "child_process";
|
|
599
|
+
import { watch as fsWatch } from "fs";
|
|
600
|
+
import { readdir, access } from "fs/promises";
|
|
601
|
+
import { join as join3, resolve as resolve2, extname, relative, basename as basename2 } from "path";
|
|
602
|
+
import { config as dotenvConfig } from "dotenv";
|
|
603
|
+
import chalk2 from "chalk";
|
|
604
|
+
import colors2 from "ansi-colors";
|
|
605
|
+
import gradient from "gradient-string";
|
|
606
|
+
var DEFAULT_WATCH_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
|
607
|
+
var DEFAULT_HOT_RELOAD_EXTENSIONS = [".json"];
|
|
608
|
+
var DEFAULT_IGNORE = [
|
|
609
|
+
"node_modules",
|
|
610
|
+
"dist",
|
|
611
|
+
".git",
|
|
612
|
+
"package.json",
|
|
613
|
+
"package-lock.json",
|
|
614
|
+
".idea",
|
|
615
|
+
".vscode",
|
|
616
|
+
"coverage",
|
|
617
|
+
"logs"
|
|
618
|
+
];
|
|
619
|
+
var DEFAULT_DEBOUNCE_MS = 300;
|
|
620
|
+
var DEFAULT_MAX_CRASH_RESTARTS = 5;
|
|
621
|
+
var HotReloadWatcher = class {
|
|
622
|
+
cfg;
|
|
623
|
+
child = null;
|
|
624
|
+
watchers = [];
|
|
625
|
+
debounceTimer = null;
|
|
626
|
+
isRestarting = false;
|
|
627
|
+
crashRestartCount = 0;
|
|
628
|
+
started = false;
|
|
629
|
+
restartStart = 0;
|
|
630
|
+
constructor(config) {
|
|
631
|
+
this.cfg = {
|
|
632
|
+
entry: resolve2(config.entry),
|
|
633
|
+
runtime: config.runtime ?? "node",
|
|
634
|
+
runtimeArgs: config.runtimeArgs ?? [],
|
|
635
|
+
rootDir: resolve2(config.rootDir ?? process.cwd()),
|
|
636
|
+
watchDirs: config.watchDirs ?? [],
|
|
637
|
+
watchExtensions: config.watchExtensions ?? DEFAULT_WATCH_EXTENSIONS,
|
|
638
|
+
ignore: [...DEFAULT_IGNORE, ...config.ignore ?? []],
|
|
639
|
+
envFile: config.envFile ?? ".env",
|
|
640
|
+
hotReloadExtensions: config.hotReloadExtensions ?? DEFAULT_HOT_RELOAD_EXTENSIONS,
|
|
641
|
+
debounceMs: config.debounceMs ?? DEFAULT_DEBOUNCE_MS,
|
|
642
|
+
restartOnCrash: config.restartOnCrash ?? true,
|
|
643
|
+
maxCrashRestarts: config.maxCrashRestarts ?? DEFAULT_MAX_CRASH_RESTARTS
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
// ─── Public API ──────────────────────────────────────────────────────────
|
|
647
|
+
async start() {
|
|
648
|
+
if (this.started) return;
|
|
649
|
+
this.started = true;
|
|
650
|
+
this.printBanner();
|
|
651
|
+
this.spawnChild();
|
|
652
|
+
await this.setupWatchers();
|
|
653
|
+
this.setupProcessSignals();
|
|
654
|
+
}
|
|
655
|
+
async stop() {
|
|
656
|
+
this.watchers.forEach((w) => {
|
|
657
|
+
w.close();
|
|
1022
658
|
});
|
|
659
|
+
this.watchers = [];
|
|
660
|
+
await this.killChild(true);
|
|
661
|
+
this.printStopped();
|
|
1023
662
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
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();
|
|
663
|
+
// ─── Child process management ────────────────────────────────────────────
|
|
664
|
+
spawnChild() {
|
|
665
|
+
const { entry, runtime, runtimeArgs } = this.cfg;
|
|
1051
666
|
try {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
667
|
+
this.child = runtime === "node" ? fork(entry, [], {
|
|
668
|
+
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
|
669
|
+
env: process.env
|
|
670
|
+
}) : spawn(runtime, [...runtimeArgs, entry], {
|
|
671
|
+
stdio: "inherit",
|
|
672
|
+
env: process.env,
|
|
673
|
+
shell: false
|
|
1059
674
|
});
|
|
1060
|
-
|
|
1061
|
-
this.
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
})
|
|
675
|
+
this.child.on("exit", (code) => {
|
|
676
|
+
if (this.isRestarting) return;
|
|
677
|
+
if (code !== 0 && code !== null) {
|
|
678
|
+
this.printCrash(code);
|
|
679
|
+
if (this.cfg.restartOnCrash && this.crashRestartCount < this.cfg.maxCrashRestarts) {
|
|
680
|
+
this.crashRestartCount++;
|
|
681
|
+
this.printCrashRetry(this.crashRestartCount, this.cfg.maxCrashRestarts);
|
|
682
|
+
setTimeout(() => {
|
|
683
|
+
this.spawnChild();
|
|
684
|
+
}, 1e3);
|
|
685
|
+
} else if (this.crashRestartCount >= this.cfg.maxCrashRestarts) {
|
|
686
|
+
this.printCrashLimit(this.cfg.maxCrashRestarts);
|
|
687
|
+
process.exit(1);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
1076
690
|
});
|
|
1077
|
-
|
|
1078
|
-
|
|
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
|
|
691
|
+
this.child.on("error", (err) => {
|
|
692
|
+
this.printError(`Failed to start child process: ${err.message}`);
|
|
1084
693
|
});
|
|
1085
|
-
}
|
|
1086
|
-
this.
|
|
1087
|
-
|
|
694
|
+
} catch (err) {
|
|
695
|
+
this.printError(`Cannot spawn '${runtime} ${entry}': ${err.message}`);
|
|
696
|
+
process.exit(1);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
async killChild(silent = false) {
|
|
700
|
+
if (!this.child) return;
|
|
701
|
+
const child = this.child;
|
|
702
|
+
this.child = null;
|
|
703
|
+
await new Promise((done) => {
|
|
704
|
+
const forceKill = setTimeout(() => {
|
|
705
|
+
child.kill("SIGKILL");
|
|
706
|
+
done();
|
|
707
|
+
}, 3e3);
|
|
708
|
+
child.once("exit", () => {
|
|
709
|
+
clearTimeout(forceKill);
|
|
710
|
+
done();
|
|
711
|
+
});
|
|
712
|
+
child.kill("SIGTERM");
|
|
713
|
+
});
|
|
1088
714
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
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
|
|
1104
|
-
*/
|
|
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();
|
|
715
|
+
// ─── File watching ────────────────────────────────────────────────────────
|
|
716
|
+
async setupWatchers() {
|
|
717
|
+
const dirs = [
|
|
718
|
+
this.cfg.rootDir,
|
|
719
|
+
...this.cfg.watchDirs.map((d) => resolve2(d))
|
|
720
|
+
];
|
|
721
|
+
for (const dir of dirs) {
|
|
722
|
+
await this.watchDirectoryRecursive(dir);
|
|
1114
723
|
}
|
|
1115
724
|
}
|
|
1116
|
-
|
|
1117
|
-
|
|
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() {
|
|
725
|
+
async watchDirectoryRecursive(dir) {
|
|
726
|
+
if (this.shouldIgnoreDir(dir)) return;
|
|
1131
727
|
try {
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
});
|
|
1136
|
-
} catch (error) {
|
|
1137
|
-
throw new Error(`Route reload failed: ${error.message}`);
|
|
728
|
+
await access(dir);
|
|
729
|
+
} catch {
|
|
730
|
+
return;
|
|
1138
731
|
}
|
|
1139
|
-
}
|
|
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() {
|
|
1151
732
|
try {
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
733
|
+
const watcher = fsWatch(dir, (event2, filename) => {
|
|
734
|
+
if (!filename) return;
|
|
735
|
+
this.onFileChange(join3(dir, filename));
|
|
736
|
+
});
|
|
737
|
+
watcher.on("error", () => {
|
|
738
|
+
});
|
|
739
|
+
this.watchers.push(watcher);
|
|
740
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
741
|
+
for (const entry of entries) {
|
|
742
|
+
if (entry.isDirectory()) {
|
|
743
|
+
await this.watchDirectoryRecursive(join3(dir, entry.name));
|
|
744
|
+
}
|
|
1158
745
|
}
|
|
1159
|
-
} catch
|
|
1160
|
-
throw new Error(`Dependency reload failed: ${error.message}`);
|
|
746
|
+
} catch {
|
|
1161
747
|
}
|
|
1162
748
|
}
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
* Checks:
|
|
1173
|
-
* - Auto-restart enabled/disabled
|
|
1174
|
-
* - Maximum restarts per minute limit
|
|
1175
|
-
*/
|
|
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"
|
|
1182
|
-
});
|
|
1183
|
-
return false;
|
|
1184
|
-
}
|
|
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
|
-
})
|
|
1199
|
-
});
|
|
1200
|
-
return false;
|
|
749
|
+
// ─── Change handling ─────────────────────────────────────────────────────
|
|
750
|
+
onFileChange(filePath) {
|
|
751
|
+
if (this.shouldIgnoreFile(filePath)) return;
|
|
752
|
+
if (!this.isWatchedFile(filePath)) return;
|
|
753
|
+
const rel = relative(this.cfg.rootDir, filePath);
|
|
754
|
+
if (this.isHotReloadable(filePath)) {
|
|
755
|
+
this.doHotReload(filePath, rel);
|
|
756
|
+
} else {
|
|
757
|
+
this.scheduleRestart(rel);
|
|
1201
758
|
}
|
|
1202
|
-
return true;
|
|
1203
759
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
760
|
+
doHotReload(filePath, relativePath) {
|
|
761
|
+
if (this.isEnvFile(filePath)) {
|
|
762
|
+
dotenvConfig({ path: filePath, override: true });
|
|
763
|
+
this.printHot(relativePath, "env variables reloaded");
|
|
764
|
+
this.sendIpc({ type: "hot-reload", file: relativePath, kind: "env" });
|
|
765
|
+
} else {
|
|
766
|
+
this.printHot(relativePath, "json config reloaded");
|
|
767
|
+
this.sendIpc({ type: "hot-reload", file: relativePath, kind: "json" });
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
scheduleRestart(relativePath) {
|
|
771
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
772
|
+
this.debounceTimer = setTimeout(async () => {
|
|
773
|
+
this.printReloading(relativePath);
|
|
774
|
+
this.restartStart = Date.now();
|
|
775
|
+
this.isRestarting = true;
|
|
776
|
+
this.crashRestartCount = 0;
|
|
777
|
+
await this.killChild(true);
|
|
778
|
+
this.isRestarting = false;
|
|
779
|
+
this.spawnChild();
|
|
780
|
+
this.printReady(Date.now() - this.restartStart);
|
|
781
|
+
}, this.cfg.debounceMs);
|
|
782
|
+
}
|
|
783
|
+
// ─── IPC ─────────────────────────────────────────────────────────────────
|
|
784
|
+
sendIpc(message) {
|
|
785
|
+
if (this.child && typeof this.child.send === "function") {
|
|
786
|
+
try {
|
|
787
|
+
this.child.send(message);
|
|
788
|
+
} catch {
|
|
789
|
+
}
|
|
790
|
+
}
|
|
1223
791
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
792
|
+
// ─── Ignore / watch filters ───────────────────────────────────────────────
|
|
793
|
+
shouldIgnoreDir(dirPath) {
|
|
794
|
+
return this.cfg.ignore.some((p) => dirPath.includes(`/${p}`) || dirPath.endsWith(p));
|
|
795
|
+
}
|
|
796
|
+
shouldIgnoreFile(filePath) {
|
|
797
|
+
const name = basename2(filePath);
|
|
798
|
+
const rel = relative(this.cfg.rootDir, filePath);
|
|
799
|
+
return this.cfg.ignore.some((p) => name === p || rel.includes(p) || filePath.includes(`/${p}/`));
|
|
800
|
+
}
|
|
801
|
+
isWatchedFile(filePath) {
|
|
802
|
+
if (this.isEnvFile(filePath)) return true;
|
|
803
|
+
return this.cfg.watchExtensions.includes(extname(filePath));
|
|
804
|
+
}
|
|
805
|
+
isHotReloadable(filePath) {
|
|
806
|
+
if (this.isEnvFile(filePath)) return true;
|
|
807
|
+
return this.cfg.hotReloadExtensions.includes(extname(filePath));
|
|
808
|
+
}
|
|
809
|
+
isEnvFile(filePath) {
|
|
810
|
+
const name = basename2(filePath);
|
|
811
|
+
return name === this.cfg.envFile || name.startsWith(".env");
|
|
812
|
+
}
|
|
813
|
+
// ─── ANSI strip helper (for width calculation) ────────────────────────────
|
|
814
|
+
strip(str) {
|
|
815
|
+
return str.replace(/\[[0-9;]*m/g, "").replace(/\][^]*/g, "");
|
|
816
|
+
}
|
|
817
|
+
// ─── Timestamp ────────────────────────────────────────────────────────────
|
|
818
|
+
ts() {
|
|
819
|
+
const n = /* @__PURE__ */ new Date();
|
|
820
|
+
const h = String(n.getHours()).padStart(2, "0");
|
|
821
|
+
const m = String(n.getMinutes()).padStart(2, "0");
|
|
822
|
+
const s = String(n.getSeconds()).padStart(2, "0");
|
|
823
|
+
return `${h}:${m}:${s}`;
|
|
824
|
+
}
|
|
825
|
+
// ─── Console output ───────────────────────────────────────────────────────
|
|
826
|
+
/**
|
|
827
|
+
* Startup banner — mirrors the infoServer() box style from CoreService.
|
|
828
|
+
*
|
|
829
|
+
* ╔══════════════════════════════════════════╗
|
|
830
|
+
* gradient title
|
|
831
|
+
* ╔══ bgGreen box ════════════════════════╗
|
|
832
|
+
* entry dist/index.js
|
|
833
|
+
* runtime node (IPC enabled)
|
|
834
|
+
* root ./src
|
|
835
|
+
* watching .ts .js .json .env
|
|
836
|
+
* debounce 300ms
|
|
837
|
+
* ╚══════════════════════════════════════════╝
|
|
838
|
+
*/
|
|
839
|
+
printBanner() {
|
|
840
|
+
const entryRel = relative(process.cwd(), this.cfg.entry) || this.cfg.entry;
|
|
841
|
+
const rootRel = relative(process.cwd(), this.cfg.rootDir) || ".";
|
|
842
|
+
const runtimeStr = this.cfg.runtime === "node" ? `node ${chalk2.blackBright("(IPC enabled)")}` : this.cfg.runtime;
|
|
843
|
+
const extStr = [...this.cfg.watchExtensions, ".env"].join(" ");
|
|
844
|
+
const ignoreStr = this.cfg.ignore.slice(0, 4).join(" ") + (this.cfg.ignore.length > 4 ? ` ${chalk2.blackBright(`+${this.cfg.ignore.length - 4} more`)}` : "");
|
|
845
|
+
const TITLE = "OPTICORE HOT RELOAD";
|
|
846
|
+
const rows = [
|
|
847
|
+
` entry ${chalk2.white.bold(entryRel)}`,
|
|
848
|
+
` runtime ${runtimeStr}`,
|
|
849
|
+
` root ${chalk2.white(rootRel)}`,
|
|
850
|
+
` watching ${chalk2.cyan(extStr)}`,
|
|
851
|
+
` ignoring ${chalk2.blackBright(ignoreStr)}`,
|
|
852
|
+
` debounce ${chalk2.white(`${this.cfg.debounceMs}ms`)}`
|
|
853
|
+
];
|
|
854
|
+
const maxLen = Math.max(
|
|
855
|
+
TITLE.length + 4,
|
|
856
|
+
...rows.map((r) => this.strip(r).length)
|
|
857
|
+
) + 4;
|
|
858
|
+
const border = chalk2.bgGreen.white(" ".repeat(maxLen));
|
|
859
|
+
const titlePad = " ".repeat(Math.max(0, Math.floor((maxLen - TITLE.length) / 2)));
|
|
860
|
+
console.log("\n" + titlePad + gradient(["#43e97b", "#38f9d7", "#00c6fb"])(TITLE));
|
|
861
|
+
console.log(border);
|
|
862
|
+
rows.forEach((row) => {
|
|
863
|
+
const cleanLen = this.strip(row).length;
|
|
864
|
+
const padding = Math.max(0, maxLen - cleanLen - 2);
|
|
865
|
+
console.log(chalk2.bgGreen.white(` ${row}${" ".repeat(padding)} `));
|
|
1240
866
|
});
|
|
867
|
+
console.log(border);
|
|
868
|
+
console.log(chalk2.blackBright(` watching for changes...
|
|
869
|
+
`));
|
|
1241
870
|
}
|
|
1242
871
|
/**
|
|
1243
|
-
*
|
|
872
|
+
* HOT event — in-process reload, no server restart.
|
|
1244
873
|
*
|
|
1245
|
-
*
|
|
1246
|
-
* @private
|
|
1247
|
-
*
|
|
1248
|
-
* @returns {void}
|
|
874
|
+
* ✔ [ HOT ] 14:23:45 | .env → env variables reloaded
|
|
1249
875
|
*/
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
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
|
-
});
|
|
876
|
+
printHot(file, action) {
|
|
877
|
+
const badge = colors2.bgCyan(colors2.white.bold(" HOT "));
|
|
878
|
+
const ts = chalk2.blackBright(this.ts());
|
|
879
|
+
const sep = chalk2.blackBright("|");
|
|
880
|
+
const filePart = chalk2.cyan.bold(file.padEnd(32));
|
|
881
|
+
const arrow = chalk2.green("\u2192");
|
|
882
|
+
const msg = chalk2.green(action);
|
|
883
|
+
console.log(` ${chalk2.green("\u2714")} ${badge} ${ts} ${sep} ${filePart} ${arrow} ${msg}`);
|
|
1263
884
|
}
|
|
1264
885
|
/**
|
|
1265
|
-
*
|
|
886
|
+
* RELOAD event — server restart triggered.
|
|
1266
887
|
*
|
|
1267
|
-
*
|
|
1268
|
-
* @public
|
|
1269
|
-
*
|
|
1270
|
-
* @returns {void}
|
|
888
|
+
* ⚡ [ RELOAD ] 14:24:03 | src/routes/user.ts → restarting server...
|
|
1271
889
|
*/
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
this.serverStatus = "STOPPED";
|
|
1282
|
-
}
|
|
890
|
+
printReloading(file) {
|
|
891
|
+
const badge = colors2.bgYellow(colors2.white.bold(" RELOAD "));
|
|
892
|
+
const ts = chalk2.blackBright(this.ts());
|
|
893
|
+
const sep = chalk2.blackBright("|");
|
|
894
|
+
const filePart = chalk2.yellow.bold(file.padEnd(32));
|
|
895
|
+
const arrow = chalk2.yellow("\u2192");
|
|
896
|
+
const msg = chalk2.yellow("restarting server...");
|
|
897
|
+
console.log(`
|
|
898
|
+
${chalk2.yellow("\u26A1")} ${badge} ${ts} ${sep} ${filePart} ${arrow} ${msg}`);
|
|
1283
899
|
}
|
|
1284
900
|
/**
|
|
1285
|
-
*
|
|
1286
|
-
*
|
|
1287
|
-
* @method getServerState
|
|
1288
|
-
* @public
|
|
1289
|
-
*
|
|
1290
|
-
* @returns {IServerStateInfo} Server state information object
|
|
901
|
+
* READY event — server successfully restarted.
|
|
902
|
+
* Uses the same full-width bgGreen border as infoServer().
|
|
1291
903
|
*
|
|
1292
|
-
*
|
|
1293
|
-
*
|
|
1294
|
-
*
|
|
1295
|
-
* - Route and dependency counts
|
|
1296
|
-
* - Uptime and memory usage
|
|
1297
|
-
* - HMR configuration and statistics
|
|
904
|
+
* ════════════════════════════════════════════
|
|
905
|
+
* READY server restarted in 245ms
|
|
906
|
+
* ════════════════════════════════════════════
|
|
1298
907
|
*/
|
|
1299
|
-
|
|
1300
|
-
const
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
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
|
-
};
|
|
908
|
+
printReady(elapsedMs) {
|
|
909
|
+
const msg = ` server restarted in ${chalk2.white.bold(`${elapsedMs}ms`)}`;
|
|
910
|
+
const cleanLen = this.strip(msg).length;
|
|
911
|
+
const width = Math.max(52, cleanLen + 6);
|
|
912
|
+
const border = chalk2.bgGreen.white(" ".repeat(width));
|
|
913
|
+
const padding = Math.max(0, width - cleanLen - 2);
|
|
914
|
+
const line = chalk2.bgGreen.white(` ${chalk2.bgGreen.white.bold(" READY ")} ${msg}${" ".repeat(padding)} `);
|
|
915
|
+
console.log(` ${border}`);
|
|
916
|
+
console.log(` ${line}`);
|
|
917
|
+
console.log(` ${border}
|
|
918
|
+
`);
|
|
1323
919
|
}
|
|
1324
920
|
/**
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1327
|
-
* @method getServerStatus
|
|
1328
|
-
* @public
|
|
921
|
+
* CRASH event — unexpected child process exit.
|
|
1329
922
|
*
|
|
1330
|
-
*
|
|
923
|
+
* ✘ [ CRASH ] 14:24:10 | server exited with code 1
|
|
1331
924
|
*/
|
|
1332
|
-
|
|
1333
|
-
|
|
925
|
+
printCrash(code) {
|
|
926
|
+
const badge = colors2.bgRed(colors2.white.bold(" CRASH "));
|
|
927
|
+
const ts = chalk2.blackBright(this.ts());
|
|
928
|
+
const sep = chalk2.blackBright("|");
|
|
929
|
+
const msg = chalk2.red(`server exited with code ${chalk2.bold(String(code))}`);
|
|
930
|
+
console.log(`
|
|
931
|
+
${chalk2.red("\u2718")} ${badge} ${ts} ${sep} ${msg}`);
|
|
1334
932
|
}
|
|
1335
933
|
/**
|
|
1336
|
-
*
|
|
934
|
+
* RETRY event — auto-restart after crash.
|
|
1337
935
|
*
|
|
1338
|
-
*
|
|
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
|
|
936
|
+
* ↺ [ RETRY ] 14:24:11 | attempt 1 / 5
|
|
1348
937
|
*/
|
|
1349
|
-
|
|
1350
|
-
const
|
|
1351
|
-
const
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
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
|
-
};
|
|
938
|
+
printCrashRetry(attempt, max) {
|
|
939
|
+
const badge = colors2.bgMagenta(colors2.white.bold(" RETRY "));
|
|
940
|
+
const ts = chalk2.blackBright(this.ts());
|
|
941
|
+
const sep = chalk2.blackBright("|");
|
|
942
|
+
const msg = chalk2.magenta(`auto-restart attempt ${chalk2.bold(`${attempt} / ${max}`)}`);
|
|
943
|
+
console.log(` ${chalk2.magenta("\u21BA")} ${badge} ${ts} ${sep} ${msg}`);
|
|
1372
944
|
}
|
|
1373
945
|
/**
|
|
1374
|
-
*
|
|
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"
|
|
946
|
+
* LIMIT reached — give up restarting.
|
|
1385
947
|
*/
|
|
1386
|
-
|
|
1387
|
-
const
|
|
1388
|
-
const
|
|
1389
|
-
const
|
|
1390
|
-
const
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
1395
|
-
} else if (minutes > 0) {
|
|
1396
|
-
return `${minutes}m ${seconds % 60}s`;
|
|
1397
|
-
} else {
|
|
1398
|
-
return `${seconds}s`;
|
|
1399
|
-
}
|
|
948
|
+
printCrashLimit(max) {
|
|
949
|
+
const badge = colors2.bgRed(colors2.white.bold(" ERROR "));
|
|
950
|
+
const ts = chalk2.blackBright(this.ts());
|
|
951
|
+
const sep = chalk2.blackBright("|");
|
|
952
|
+
const msg = chalk2.red(`max crash restarts reached (${chalk2.bold(String(max))}), giving up`);
|
|
953
|
+
console.log(`
|
|
954
|
+
${chalk2.red("\u2718")} ${badge} ${ts} ${sep} ${msg}
|
|
955
|
+
`);
|
|
1400
956
|
}
|
|
1401
957
|
/**
|
|
1402
|
-
*
|
|
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"
|
|
958
|
+
* Error — miscellaneous internal error.
|
|
1413
959
|
*/
|
|
1414
|
-
|
|
1415
|
-
const
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
value /= 1024;
|
|
1420
|
-
unitIndex++;
|
|
1421
|
-
}
|
|
1422
|
-
return `${value.toFixed(2)} ${units[unitIndex]}`;
|
|
960
|
+
printError(message) {
|
|
961
|
+
const badge = colors2.bgRed(colors2.white.bold(" ERROR "));
|
|
962
|
+
const ts = chalk2.blackBright(this.ts());
|
|
963
|
+
const sep = chalk2.blackBright("|");
|
|
964
|
+
console.error(` ${chalk2.red("\u2718")} ${badge} ${ts} ${sep} ${chalk2.red(message)}`);
|
|
1423
965
|
}
|
|
1424
966
|
/**
|
|
1425
|
-
*
|
|
1426
|
-
*
|
|
1427
|
-
* @method isHMRActive
|
|
1428
|
-
* @public
|
|
1429
|
-
*
|
|
1430
|
-
* @returns {boolean} True if HMR is enabled and watching files, false otherwise
|
|
967
|
+
* Stopped — watcher shut down.
|
|
1431
968
|
*/
|
|
1432
|
-
|
|
1433
|
-
|
|
969
|
+
printStopped() {
|
|
970
|
+
const badge = colors2.bgBlackBright(colors2.white.bold(" STOPPED"));
|
|
971
|
+
const ts = chalk2.blackBright(this.ts());
|
|
972
|
+
const sep = chalk2.blackBright("|");
|
|
973
|
+
console.log(` ${chalk2.gray("\u25A0")} ${badge} ${ts} ${sep} ${chalk2.gray("watcher stopped")}
|
|
974
|
+
`);
|
|
1434
975
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
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
|
|
976
|
+
// ─── Graceful shutdown ────────────────────────────────────────────────────
|
|
977
|
+
setupProcessSignals() {
|
|
978
|
+
const shutdown = async () => {
|
|
979
|
+
console.log("");
|
|
980
|
+
await this.stop();
|
|
981
|
+
process.exit(0);
|
|
1461
982
|
};
|
|
983
|
+
process.once("SIGINT", shutdown);
|
|
984
|
+
process.once("SIGTERM", shutdown);
|
|
1462
985
|
}
|
|
1463
986
|
};
|
|
1464
987
|
export {
|
|
988
|
+
HotReloadWatcher,
|
|
1465
989
|
WebServerCore as WebServer,
|
|
1466
990
|
envPath
|
|
1467
991
|
};
|