opticore-webapp 1.0.70 → 1.0.72
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 +418 -25
- package/dist/index.d.cts +175 -1
- package/dist/index.d.ts +175 -1
- package/dist/index.js +441 -49
- package/dist/utils/translations/message.translation.en.json +4 -13
- package/dist/utils/translations/message.translation.fr.json +6 -2
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
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
5
|
import { CEventNameError as eventName2, ServerListenEventError as ServerListenEventError2 } from "opticore-catch-exception-error";
|
|
6
6
|
import { express as express2 } from "opticore-express";
|
|
7
7
|
import { getEnvironmentValue as getEnvironmentValue2 } from "opticore-env-access";
|
|
8
8
|
import { HttpStatusCode as HttpStatusCode3 } from "opticore-http-response";
|
|
9
|
-
import { TranslationLoader as
|
|
9
|
+
import { TranslationLoader as TranslationLoader3 } from "opticore-translator";
|
|
10
10
|
import { requestCallsEvent } from "opticore-request-call-event";
|
|
11
11
|
|
|
12
12
|
// src/core/handlers/eventProcess.handler.ts
|
|
13
|
-
import
|
|
13
|
+
import process2 from "process";
|
|
14
14
|
import EventEmitter from "events";
|
|
15
15
|
import { express } from "opticore-express";
|
|
16
16
|
import { ServerListenEventError, CEventNameError as eventName, CEvent as event } from "opticore-catch-exception-error";
|
|
@@ -21,42 +21,42 @@ function eventProcessHandler(localeLanguage) {
|
|
|
21
21
|
errorEmitter.on(eventName.error, (error) => {
|
|
22
22
|
serverListenEvent.listenerError(error);
|
|
23
23
|
});
|
|
24
|
-
|
|
24
|
+
process2.on(event.beforeExit, (code) => {
|
|
25
25
|
setTimeout(() => {
|
|
26
26
|
serverListenEvent.processBeforeExit(code);
|
|
27
27
|
}, 100);
|
|
28
28
|
});
|
|
29
|
-
|
|
29
|
+
process2.on(event.disconnect, () => {
|
|
30
30
|
serverListenEvent.processDisconnected();
|
|
31
31
|
});
|
|
32
|
-
|
|
32
|
+
process2.on(event.exit, (code) => {
|
|
33
33
|
serverListenEvent.exited(code);
|
|
34
34
|
});
|
|
35
|
-
|
|
35
|
+
process2.on(event.rejectionHandled, (promise) => {
|
|
36
36
|
serverListenEvent.promiseRejectionHandled(promise);
|
|
37
37
|
});
|
|
38
|
-
|
|
38
|
+
process2.on(event.uncaughtException, (error) => {
|
|
39
39
|
serverListenEvent.uncaughtException(error);
|
|
40
40
|
});
|
|
41
|
-
|
|
41
|
+
process2.on(event.uncaughtExceptionMonitor, (error) => {
|
|
42
42
|
serverListenEvent.uncaughtExceptionMonitor(error);
|
|
43
43
|
});
|
|
44
|
-
|
|
44
|
+
process2.on(event.unhandledRejection, (reason, promise) => {
|
|
45
45
|
serverListenEvent.unhandledRejection(reason, promise);
|
|
46
46
|
});
|
|
47
|
-
|
|
47
|
+
process2.on(event.warning, (warning) => {
|
|
48
48
|
serverListenEvent.warning(warning);
|
|
49
49
|
});
|
|
50
|
-
|
|
50
|
+
process2.on(event.message, (message) => {
|
|
51
51
|
serverListenEvent.message(message);
|
|
52
52
|
});
|
|
53
|
-
|
|
53
|
+
process2.on(event.multipleResolves, (type, promise, reason) => {
|
|
54
54
|
serverListenEvent.multipleResolves(type, promise, reason);
|
|
55
55
|
});
|
|
56
|
-
|
|
56
|
+
process2.on(event.sigint, () => {
|
|
57
57
|
serverListenEvent.processInterrupted();
|
|
58
58
|
});
|
|
59
|
-
|
|
59
|
+
process2.on(event.sigterm, (signal) => {
|
|
60
60
|
serverListenEvent.sigtermSignalReceived(signal);
|
|
61
61
|
});
|
|
62
62
|
app.use((err, req, res, next) => {
|
|
@@ -65,7 +65,7 @@ function eventProcessHandler(localeLanguage) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// src/application/service/core.service.ts
|
|
68
|
-
import
|
|
68
|
+
import process4 from "process";
|
|
69
69
|
import chalk from "chalk";
|
|
70
70
|
import * as path from "path";
|
|
71
71
|
import * as fs from "fs";
|
|
@@ -75,8 +75,8 @@ import { TranslationLoader } from "opticore-translator";
|
|
|
75
75
|
import { getEnvironmentValue } from "opticore-env-access";
|
|
76
76
|
|
|
77
77
|
// src/utils/envPath.utils.ts
|
|
78
|
-
import
|
|
79
|
-
var envPath =
|
|
78
|
+
import process3 from "process";
|
|
79
|
+
var envPath = process3.cwd() + "/config/env/.env";
|
|
80
80
|
|
|
81
81
|
// src/application/service/loaderTranslationFile.service.ts
|
|
82
82
|
import { translationLoaderConfig } from "opticore-loader-translation";
|
|
@@ -112,16 +112,35 @@ var dependenciesContainerProvider = (localLang) => {
|
|
|
112
112
|
return new SContainer(localLang, dependencies);
|
|
113
113
|
};
|
|
114
114
|
|
|
115
|
+
// src/application/service/core.service.ts
|
|
116
|
+
import { SContainer as SContainer2 } from "opticore-dependency-inject";
|
|
117
|
+
|
|
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);
|
|
130
|
+
};
|
|
131
|
+
|
|
115
132
|
// src/application/service/core.service.ts
|
|
116
133
|
var CoreService = class {
|
|
117
134
|
localLanguage;
|
|
118
135
|
environmentPath;
|
|
119
136
|
serverLog;
|
|
120
137
|
logger;
|
|
138
|
+
container;
|
|
121
139
|
constructor(localLang, environmentPath) {
|
|
122
140
|
this.loadTranslationFiles();
|
|
123
141
|
this.environmentPath = environmentPath;
|
|
124
142
|
this.localLanguage = localLang;
|
|
143
|
+
this.container = new SContainer2(localLang);
|
|
125
144
|
this.serverLog = dependenciesContainerProvider(localLang).resolve("OpticoreLogger");
|
|
126
145
|
this.logger = dependenciesContainerProvider(localLang).resolve("LoggerCore");
|
|
127
146
|
}
|
|
@@ -148,7 +167,7 @@ var CoreService = class {
|
|
|
148
167
|
getEnvFileLoading(filePath) {
|
|
149
168
|
this.loadTranslationFiles();
|
|
150
169
|
try {
|
|
151
|
-
const fullPath = path.resolve(
|
|
170
|
+
const fullPath = path.resolve(process4.cwd(), filePath);
|
|
152
171
|
if (fs.existsSync(fullPath)) {
|
|
153
172
|
const env = fs.readFileSync(fullPath, "utf-8");
|
|
154
173
|
const lines = env.split("\n");
|
|
@@ -156,7 +175,7 @@ var CoreService = class {
|
|
|
156
175
|
const match = line.match(/^([^#=]+)=([^#]+)$/);
|
|
157
176
|
if (match) {
|
|
158
177
|
const key = match[1].trim();
|
|
159
|
-
|
|
178
|
+
process4.env[key] = match[2].trim();
|
|
160
179
|
}
|
|
161
180
|
});
|
|
162
181
|
}
|
|
@@ -174,7 +193,7 @@ var CoreService = class {
|
|
|
174
193
|
* Returns an Object containing a node version, openssl, and v0
|
|
175
194
|
*/
|
|
176
195
|
getVersions() {
|
|
177
|
-
const { node, openssl, v8 } =
|
|
196
|
+
const { node, openssl, v8 } = process4.versions;
|
|
178
197
|
const data = {
|
|
179
198
|
"node version": node,
|
|
180
199
|
"openssl": openssl,
|
|
@@ -195,14 +214,14 @@ var CoreService = class {
|
|
|
195
214
|
*/
|
|
196
215
|
getUsageMemory() {
|
|
197
216
|
this.loadTranslationFiles();
|
|
198
|
-
const memoryData =
|
|
217
|
+
const memoryData = process4.memoryUsage();
|
|
199
218
|
const data = {
|
|
200
219
|
[TranslationLoader.t("totalMemoryAllocated", this.localLanguage)]: this.formatMemoryUsage(memoryData.rss),
|
|
201
220
|
[TranslationLoader.t("sizeAllocatedHeap", this.localLanguage)]: this.formatMemoryUsage(memoryData.heapTotal),
|
|
202
221
|
[TranslationLoader.t("memoryUsedExecution", this.localLanguage)]: this.formatMemoryUsage(memoryData.heapUsed),
|
|
203
222
|
[TranslationLoader.t("externalMemory", this.localLanguage)]: this.formatMemoryUsage(memoryData.external),
|
|
204
|
-
[TranslationLoader.t("memoryUsageUser", this.localLanguage)]: this.formatMemoryUsage(
|
|
205
|
-
[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)
|
|
206
225
|
};
|
|
207
226
|
return {
|
|
208
227
|
"rss": data[TranslationLoader.t("totalMemoryAllocated", this.localLanguage)],
|
|
@@ -211,18 +230,18 @@ var CoreService = class {
|
|
|
211
230
|
"external": data[TranslationLoader.t("externalMemory", this.localLanguage)],
|
|
212
231
|
"user": data[TranslationLoader.t("memoryUsageUser", this.localLanguage)],
|
|
213
232
|
"system": data[TranslationLoader.t("memoryUsageSystem", this.localLanguage)],
|
|
214
|
-
"pid":
|
|
233
|
+
"pid": process4.pid
|
|
215
234
|
};
|
|
216
235
|
}
|
|
217
236
|
/**
|
|
218
237
|
* Return an Object containing a project path and running server time
|
|
219
238
|
*/
|
|
220
239
|
getProjectInfo() {
|
|
221
|
-
const startTime =
|
|
222
|
-
const endTime =
|
|
240
|
+
const startTime = process4.hrtime();
|
|
241
|
+
const endTime = process4.hrtime(startTime);
|
|
223
242
|
const executionTime = (endTime[0] * 1e9 + endTime[1]) / 1e6;
|
|
224
243
|
return {
|
|
225
|
-
"projectPath": path.join(
|
|
244
|
+
"projectPath": path.join(process4.cwd()),
|
|
226
245
|
"startingTime": `${executionTime.toFixed(5)} ms`
|
|
227
246
|
};
|
|
228
247
|
}
|
|
@@ -261,7 +280,7 @@ var CoreService = class {
|
|
|
261
280
|
this.loadTranslationFiles();
|
|
262
281
|
try {
|
|
263
282
|
const getEnvironment = getEnvironmentValue(this.environmentPath);
|
|
264
|
-
const msg5 = getEnvironment.
|
|
283
|
+
const msg5 = getEnvironment.protocolTransfer === "" ? colors.underline(`http://${host}:${port}`) : colors.underline(`${getEnvironment.protocolTransfer}://${host}:${port}`);
|
|
265
284
|
const messages = [
|
|
266
285
|
TranslationLoader.t("webServerListening", this.localLanguage),
|
|
267
286
|
TranslationLoader.t("webServerUsingNodeVersion", this.localLanguage, { nodeVersion: this.getVersions().nodeVersion }),
|
|
@@ -271,7 +290,7 @@ var CoreService = class {
|
|
|
271
290
|
const maxLength = Math.max(...messages.map((m) => {
|
|
272
291
|
return m.replace(/\u001b\[[0-9]{1,2}m/g, "").length;
|
|
273
292
|
})) + 4;
|
|
274
|
-
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")})`));
|
|
275
294
|
const border = chalk.bgGreen.white(" ".repeat(maxLength));
|
|
276
295
|
console.log(border);
|
|
277
296
|
messages.forEach((msg) => {
|
|
@@ -281,17 +300,31 @@ var CoreService = class {
|
|
|
281
300
|
});
|
|
282
301
|
console.log(border);
|
|
283
302
|
console.log("\n");
|
|
284
|
-
this.logger.success({
|
|
285
|
-
title: TranslationLoader.t("serverRunningTitle", this.localLanguage),
|
|
286
|
-
message: TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
|
|
287
|
-
});
|
|
288
303
|
this.serverLog.serverLog({
|
|
289
|
-
timestamp: (
|
|
304
|
+
timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
|
|
290
305
|
level: "SERVER",
|
|
291
306
|
title: TranslationLoader.t("serverRunningTitle", this.localLanguage),
|
|
292
307
|
typeName: TranslationLoader.t("opticoreServerTypeName", this.localLanguage),
|
|
293
308
|
message: TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
|
|
294
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
|
+
}
|
|
295
328
|
} catch (err) {
|
|
296
329
|
this.logger.error({
|
|
297
330
|
message: err.message,
|
|
@@ -358,10 +391,284 @@ var SServerStartError = (err, environmentPath) => {
|
|
|
358
391
|
};
|
|
359
392
|
|
|
360
393
|
// src/core/webServer.core.ts
|
|
361
|
-
import { SContainer as
|
|
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
|
+
|
|
429
|
+
// src/hotReload/hotReload.watcher.ts
|
|
430
|
+
import { watch as fsWatch } from "fs";
|
|
431
|
+
import { readdir, access } from "fs/promises";
|
|
432
|
+
import { join as join2, resolve as resolve2, extname, relative, basename as basename2 } from "path";
|
|
433
|
+
import { config as dotenvConfig } from "dotenv";
|
|
434
|
+
import chalk2 from "chalk";
|
|
435
|
+
import colors2 from "ansi-colors";
|
|
436
|
+
import gradient from "gradient-string";
|
|
437
|
+
var DEFAULT_WATCH_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
|
438
|
+
var DEFAULT_HOT_RELOAD_EXTENSIONS = [".json"];
|
|
439
|
+
var DEFAULT_IGNORE = [
|
|
440
|
+
"node_modules",
|
|
441
|
+
"dist",
|
|
442
|
+
".git",
|
|
443
|
+
"package.json",
|
|
444
|
+
"package-lock.json",
|
|
445
|
+
".idea",
|
|
446
|
+
".vscode",
|
|
447
|
+
"coverage",
|
|
448
|
+
"logs"
|
|
449
|
+
];
|
|
450
|
+
var DEFAULT_DEBOUNCE_MS = 300;
|
|
451
|
+
var HotReloadWatcher = class {
|
|
452
|
+
cfg;
|
|
453
|
+
server = null;
|
|
454
|
+
watchers = [];
|
|
455
|
+
debounceTimer = null;
|
|
456
|
+
restarting = false;
|
|
457
|
+
constructor(config) {
|
|
458
|
+
const c = config ?? {};
|
|
459
|
+
this.cfg = {
|
|
460
|
+
rootDir: resolve2(c.rootDir ?? process.cwd()),
|
|
461
|
+
watchDirs: c.watchDirs ?? [],
|
|
462
|
+
watchExtensions: c.watchExtensions ?? DEFAULT_WATCH_EXTENSIONS,
|
|
463
|
+
ignore: [...DEFAULT_IGNORE, ...c.ignore ?? []],
|
|
464
|
+
envFile: c.envFile ?? ".env",
|
|
465
|
+
hotReloadExtensions: c.hotReloadExtensions ?? DEFAULT_HOT_RELOAD_EXTENSIONS,
|
|
466
|
+
debounceMs: c.debounceMs ?? DEFAULT_DEBOUNCE_MS
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
// ─── Called by WebServerCore once the HTTP server is listening ────────────
|
|
470
|
+
async attach(server) {
|
|
471
|
+
this.server = server;
|
|
472
|
+
this.printBanner();
|
|
473
|
+
await this.setupWatchers();
|
|
474
|
+
this.setupProcessSignals();
|
|
475
|
+
}
|
|
476
|
+
// ─── File watching ────────────────────────────────────────────────────────
|
|
477
|
+
async setupWatchers() {
|
|
478
|
+
const dirs = [
|
|
479
|
+
this.cfg.rootDir,
|
|
480
|
+
...this.cfg.watchDirs.map((d) => resolve2(d))
|
|
481
|
+
];
|
|
482
|
+
for (const dir of dirs) {
|
|
483
|
+
await this.watchRecursive(dir);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
async watchRecursive(dir) {
|
|
487
|
+
if (this.shouldIgnoreDir(dir)) return;
|
|
488
|
+
try {
|
|
489
|
+
await access(dir);
|
|
490
|
+
} catch {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
const w = fsWatch(dir, (_, filename) => {
|
|
495
|
+
if (filename) this.onFileChange(join2(dir, filename));
|
|
496
|
+
});
|
|
497
|
+
w.on("error", () => {
|
|
498
|
+
});
|
|
499
|
+
this.watchers.push(w);
|
|
500
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
501
|
+
for (const entry of entries) {
|
|
502
|
+
if (entry.isDirectory()) await this.watchRecursive(join2(dir, entry.name));
|
|
503
|
+
}
|
|
504
|
+
} catch {
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
// ─── Change handling ──────────────────────────────────────────────────────
|
|
508
|
+
onFileChange(filePath) {
|
|
509
|
+
if (this.shouldIgnoreFile(filePath)) return;
|
|
510
|
+
if (!this.isWatched(filePath)) return;
|
|
511
|
+
if (this.restarting) return;
|
|
512
|
+
const rel = relative(this.cfg.rootDir, filePath);
|
|
513
|
+
if (this.isHotReloadable(filePath)) {
|
|
514
|
+
this.doHotReload(filePath, rel);
|
|
515
|
+
} else {
|
|
516
|
+
this.scheduleRestart(rel);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
doHotReload(filePath, rel) {
|
|
520
|
+
if (this.isEnvFile(filePath)) {
|
|
521
|
+
dotenvConfig({ path: filePath, override: true });
|
|
522
|
+
this.printHot(rel, "env variables reloaded");
|
|
523
|
+
} else {
|
|
524
|
+
this.printHot(rel, "json config reloaded");
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
scheduleRestart(rel) {
|
|
528
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
529
|
+
this.debounceTimer = setTimeout(() => {
|
|
530
|
+
this.restarting = true;
|
|
531
|
+
this.printReloading(rel);
|
|
532
|
+
this.closeAndExit();
|
|
533
|
+
}, this.cfg.debounceMs);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Gracefully close the HTTP server so in-flight requests can finish,
|
|
537
|
+
* then exit with code 0 — the external runner restarts the process.
|
|
538
|
+
*/
|
|
539
|
+
closeAndExit() {
|
|
540
|
+
if (!this.server) {
|
|
541
|
+
process.exit(0);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
this.server.close(() => {
|
|
545
|
+
process.exit(0);
|
|
546
|
+
});
|
|
547
|
+
setTimeout(() => {
|
|
548
|
+
process.exit(0);
|
|
549
|
+
}, 3e3).unref();
|
|
550
|
+
}
|
|
551
|
+
// ─── Filters ──────────────────────────────────────────────────────────────
|
|
552
|
+
shouldIgnoreDir(dir) {
|
|
553
|
+
return this.cfg.ignore.some(
|
|
554
|
+
(p) => dir.includes(`/${p}`) || dir.endsWith(`/${p}`) || dir.endsWith(p)
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
shouldIgnoreFile(filePath) {
|
|
558
|
+
const name = basename2(filePath);
|
|
559
|
+
const rel = relative(this.cfg.rootDir, filePath);
|
|
560
|
+
return this.cfg.ignore.some(
|
|
561
|
+
(p) => name === p || rel.includes(p) || filePath.includes(`/${p}/`)
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
isWatched(filePath) {
|
|
565
|
+
if (this.isEnvFile(filePath)) return true;
|
|
566
|
+
return this.cfg.watchExtensions.includes(extname(filePath));
|
|
567
|
+
}
|
|
568
|
+
isHotReloadable(filePath) {
|
|
569
|
+
if (this.isEnvFile(filePath)) return true;
|
|
570
|
+
return this.cfg.hotReloadExtensions.includes(extname(filePath));
|
|
571
|
+
}
|
|
572
|
+
isEnvFile(filePath) {
|
|
573
|
+
const name = basename2(filePath);
|
|
574
|
+
return name === this.cfg.envFile || name.startsWith(".env");
|
|
575
|
+
}
|
|
576
|
+
// ─── ANSI strip (box width calculation) ───────────────────────────────────
|
|
577
|
+
strip(str) {
|
|
578
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
579
|
+
}
|
|
580
|
+
// ─── Timestamp ────────────────────────────────────────────────────────────
|
|
581
|
+
ts() {
|
|
582
|
+
const n = /* @__PURE__ */ new Date();
|
|
583
|
+
return [n.getHours(), n.getMinutes(), n.getSeconds()].map((v) => String(v).padStart(2, "0")).join(":");
|
|
584
|
+
}
|
|
585
|
+
// ─── Console output ───────────────────────────────────────────────────────
|
|
586
|
+
/**
|
|
587
|
+
* Startup banner — same bgGreen box style as CoreService.infoServer().
|
|
588
|
+
*
|
|
589
|
+
* OPTICORE HOT RELOAD ← gradient
|
|
590
|
+
* ████████████████████████████████ ← bgGreen border
|
|
591
|
+
* root ./src
|
|
592
|
+
* watching .ts .js .json .env
|
|
593
|
+
* debounce 300ms
|
|
594
|
+
* ████████████████████████████████ ← bgGreen border
|
|
595
|
+
* watching for changes...
|
|
596
|
+
*/
|
|
597
|
+
printBanner() {
|
|
598
|
+
const rootRel = relative(process.cwd(), this.cfg.rootDir) || ".";
|
|
599
|
+
const extStr = [...this.cfg.watchExtensions, ".env"].join(" ");
|
|
600
|
+
const ignoreParts = this.cfg.ignore.slice(0, 4);
|
|
601
|
+
const ignoreMore = this.cfg.ignore.length > 4 ? ` ${chalk2.blackBright(`+${this.cfg.ignore.length - 4} more`)}` : "";
|
|
602
|
+
const ignoreStr = chalk2.blackBright(ignoreParts.join(" ")) + ignoreMore;
|
|
603
|
+
const TITLE = "OPTICORE HOT RELOAD";
|
|
604
|
+
const rows = [
|
|
605
|
+
` root ${chalk2.white(rootRel)}`,
|
|
606
|
+
` watching ${chalk2.cyan(extStr)}`,
|
|
607
|
+
` ignoring ${ignoreStr}`,
|
|
608
|
+
` debounce ${chalk2.white(`${this.cfg.debounceMs}ms`)}`
|
|
609
|
+
];
|
|
610
|
+
const maxLen = Math.max(
|
|
611
|
+
TITLE.length + 4,
|
|
612
|
+
...rows.map((r) => this.strip(r).length)
|
|
613
|
+
) + 4;
|
|
614
|
+
const border = chalk2.bgGreen.white(" ".repeat(maxLen));
|
|
615
|
+
const titlePad = " ".repeat(Math.max(0, Math.floor((maxLen - TITLE.length) / 2)));
|
|
616
|
+
console.log("\n" + titlePad + gradient(["#43e97b", "#38f9d7", "#00c6fb"])(TITLE));
|
|
617
|
+
console.log(border);
|
|
618
|
+
for (const row of rows) {
|
|
619
|
+
const pad = Math.max(0, maxLen - this.strip(row).length - 2);
|
|
620
|
+
console.log(chalk2.bgGreen.white(` ${row}${" ".repeat(pad)} `));
|
|
621
|
+
}
|
|
622
|
+
console.log(border);
|
|
623
|
+
console.log(chalk2.blackBright(` watching for changes...
|
|
624
|
+
`));
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* ✔ [ HOT ] 14:23:45 | .env → env variables reloaded
|
|
628
|
+
*/
|
|
629
|
+
printHot(file, action) {
|
|
630
|
+
const badge = colors2.bgCyan(colors2.white.bold(" HOT "));
|
|
631
|
+
const filePart = chalk2.cyan.bold(file.padEnd(32));
|
|
632
|
+
console.log(
|
|
633
|
+
` ${chalk2.green("\u2714")} ${badge} ${chalk2.blackBright(this.ts())} ${chalk2.blackBright("|")} ${filePart} ${chalk2.green("\u2192")} ${chalk2.green(action)}`
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* ⚡ [ RELOAD ] 14:24:03 | src/routes/user.ts → restarting server...
|
|
638
|
+
*/
|
|
639
|
+
printReloading(file) {
|
|
640
|
+
const badge = colors2.bgYellow(colors2.white.bold(" RELOAD "));
|
|
641
|
+
const filePart = chalk2.yellow.bold(file.padEnd(32));
|
|
642
|
+
console.log(
|
|
643
|
+
`
|
|
644
|
+
${chalk2.yellow("\u26A1")} ${badge} ${chalk2.blackBright(this.ts())} ${chalk2.blackBright("|")} ${filePart} ${chalk2.yellow("\u2192")} ${chalk2.yellow("restarting server...")}
|
|
645
|
+
`
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
// ─── Graceful shutdown on CTRL+C ──────────────────────────────────────────
|
|
649
|
+
setupProcessSignals() {
|
|
650
|
+
const shutdown = () => {
|
|
651
|
+
const badge = colors2.bgBlackBright(colors2.white.bold(" STOPPED"));
|
|
652
|
+
console.log(
|
|
653
|
+
`
|
|
654
|
+
${chalk2.gray("\u25A0")} ${badge} ${chalk2.blackBright(this.ts())} ${chalk2.blackBright("|")} ${chalk2.gray("watcher stopped")}
|
|
655
|
+
`
|
|
656
|
+
);
|
|
657
|
+
this.watchers.forEach((w) => {
|
|
658
|
+
w.close();
|
|
659
|
+
});
|
|
660
|
+
process.exit(0);
|
|
661
|
+
};
|
|
662
|
+
process.once("SIGINT", shutdown);
|
|
663
|
+
process.once("SIGTERM", shutdown);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
// src/core/webServer.core.ts
|
|
362
668
|
var WebServerCore = class {
|
|
363
669
|
serverUtility;
|
|
364
670
|
expressApp = express2();
|
|
671
|
+
container;
|
|
365
672
|
localLanguage;
|
|
366
673
|
loggerConfig;
|
|
367
674
|
routerExpressApp;
|
|
@@ -369,6 +676,7 @@ var WebServerCore = class {
|
|
|
369
676
|
environmentPath;
|
|
370
677
|
serverListenEvent;
|
|
371
678
|
dependenciesRegistered = false;
|
|
679
|
+
hotReloadCfg;
|
|
372
680
|
constructor(paramsConstructor) {
|
|
373
681
|
this.loadTranslationFiles();
|
|
374
682
|
this.stackTraceErrorHandling();
|
|
@@ -377,6 +685,7 @@ var WebServerCore = class {
|
|
|
377
685
|
this.loggerConfig = paramsConstructor.loggerConfig;
|
|
378
686
|
this.localLanguage = paramsConstructor.localLanguage;
|
|
379
687
|
this.environmentPath = paramsConstructor.environmentPath;
|
|
688
|
+
this.container = new SContainer3(paramsConstructor.localLanguage);
|
|
380
689
|
this.expressApp.use(express2.json());
|
|
381
690
|
this.expressApp.use(express2.raw());
|
|
382
691
|
this.expressApp.use(express2.text());
|
|
@@ -384,17 +693,20 @@ var WebServerCore = class {
|
|
|
384
693
|
this.expressApp.use(corsOrigin(paramsConstructor.corsOriginOptions));
|
|
385
694
|
this.serverListenEvent = new ServerListenEventError2(paramsConstructor.localLanguage);
|
|
386
695
|
this.serverUtility = new CoreService(paramsConstructor.localLanguage, paramsConstructor.environmentPath);
|
|
696
|
+
const hr = paramsConstructor.hotReload;
|
|
697
|
+
this.hotReloadCfg = !hr ? null : hr === true ? {} : hr;
|
|
387
698
|
}
|
|
699
|
+
/**
|
|
700
|
+
*
|
|
701
|
+
* @param dependencies
|
|
702
|
+
*/
|
|
388
703
|
registerDependencies(dependencies2) {
|
|
704
|
+
this.loadTranslationFiles();
|
|
389
705
|
try {
|
|
706
|
+
getEnvFileLoadingService(".env", this.localLanguage);
|
|
390
707
|
if (dependencies2 && dependencies2.length > 0) {
|
|
391
708
|
dependencies2.forEach((dependency) => {
|
|
392
|
-
|
|
393
|
-
});
|
|
394
|
-
} else {
|
|
395
|
-
SLogger(this.environmentPath).logger.info({
|
|
396
|
-
message: TranslationLoader2.t("NO_REGISTER_DEPENDENCIES", this.localLanguage),
|
|
397
|
-
title: TranslationLoader2.t("REGISTER_DEPENDENCIES", this.localLanguage)
|
|
709
|
+
this.container.register(dependency.key, dependency.factory, dependency.scope);
|
|
398
710
|
});
|
|
399
711
|
}
|
|
400
712
|
} catch (err) {
|
|
@@ -407,8 +719,15 @@ var WebServerCore = class {
|
|
|
407
719
|
});
|
|
408
720
|
}
|
|
409
721
|
}
|
|
722
|
+
/**
|
|
723
|
+
*
|
|
724
|
+
* @param routers
|
|
725
|
+
* @param databaseCallback
|
|
726
|
+
* @param dependenciesProvider
|
|
727
|
+
*/
|
|
410
728
|
onStartServer(routers, databaseCallback, dependenciesProvider) {
|
|
411
729
|
this.loadTranslationFiles();
|
|
730
|
+
getEnvFileLoadingService(".env", this.localLanguage);
|
|
412
731
|
if (this.getEnvironmentValue.appPort === "" && Number(this.getEnvironmentValue.appPort) === 0) {
|
|
413
732
|
this.serverListenEvent.hostPortUndefined(Number(this.getEnvironmentValue.appPort));
|
|
414
733
|
} else if (this.getEnvironmentValue.appHost === "") {
|
|
@@ -417,29 +736,41 @@ var WebServerCore = class {
|
|
|
417
736
|
this.serverListenEvent.portUndefined();
|
|
418
737
|
} else if (this.localLanguage === "") {
|
|
419
738
|
SLogger(this.environmentPath).logger.error({
|
|
420
|
-
message:
|
|
421
|
-
title:
|
|
422
|
-
errorType:
|
|
739
|
+
message: TranslationLoader3.t("noDefaultLocalLang", this.localLanguage),
|
|
740
|
+
title: TranslationLoader3.t("noLocalLang", this.localLanguage),
|
|
741
|
+
errorType: TranslationLoader3.t("localLangMissing", this.localLanguage),
|
|
423
742
|
stackTrace: void 0,
|
|
424
743
|
httpCodeValue: HttpStatusCode3.NOT_FOUND
|
|
425
744
|
});
|
|
426
745
|
} else {
|
|
427
|
-
|
|
746
|
+
const server = this.expressApp.listen(
|
|
428
747
|
Number(this.getEnvironmentValue.appPort),
|
|
429
748
|
() => {
|
|
430
749
|
this.loadTranslationFiles();
|
|
431
750
|
try {
|
|
432
751
|
databaseCallback(this.getEnvironmentValue);
|
|
433
|
-
|
|
434
|
-
|
|
752
|
+
if (dependenciesProvider) {
|
|
753
|
+
this.registerDependencies(dependenciesProvider);
|
|
754
|
+
}
|
|
755
|
+
this.container.loadServices();
|
|
756
|
+
this.expressApp.use(express2.static(path3.join(process6.cwd(), "public/template")));
|
|
435
757
|
this.registerRoutes(routers);
|
|
758
|
+
const resolvedHmr = this.resolveHotReloadConfig();
|
|
759
|
+
if (resolvedHmr !== null) {
|
|
760
|
+
new HotReloadWatcher(resolvedHmr).attach(server);
|
|
761
|
+
}
|
|
436
762
|
} catch (err) {
|
|
437
763
|
SServerStartError(err, this.environmentPath);
|
|
438
764
|
}
|
|
439
765
|
}
|
|
440
766
|
);
|
|
767
|
+
return server;
|
|
441
768
|
}
|
|
442
769
|
}
|
|
770
|
+
/**
|
|
771
|
+
*
|
|
772
|
+
* @param serverWeb
|
|
773
|
+
*/
|
|
443
774
|
onListeningOnServerEvent(serverWeb) {
|
|
444
775
|
this.loadTranslationFiles();
|
|
445
776
|
serverWeb.on(eventName2.error, (err) => {
|
|
@@ -452,6 +783,10 @@ var WebServerCore = class {
|
|
|
452
783
|
this.infoWebApp();
|
|
453
784
|
});
|
|
454
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
*
|
|
788
|
+
* @param serverWeb
|
|
789
|
+
*/
|
|
455
790
|
onRequestOnServerEvent(serverWeb) {
|
|
456
791
|
this.loadTranslationFiles();
|
|
457
792
|
serverWeb.on(eventName2.request, (req, res) => {
|
|
@@ -473,6 +808,62 @@ var WebServerCore = class {
|
|
|
473
808
|
loadTranslationFiles() {
|
|
474
809
|
loaderTranslationFile(this.localLanguage);
|
|
475
810
|
}
|
|
811
|
+
/**
|
|
812
|
+
* Resolve the final HotReloadConfig by merging constructor config with
|
|
813
|
+
* HMR environment variables.
|
|
814
|
+
*
|
|
815
|
+
* Priority (highest → lowest):
|
|
816
|
+
* 1. Constructor hotReload properties (explicit code-level config)
|
|
817
|
+
* 2. HMR_* env variables (runtime / per-environment config)
|
|
818
|
+
* 3. HotReloadWatcher internal defaults (built-in fallbacks)
|
|
819
|
+
*
|
|
820
|
+
* The watcher starts when:
|
|
821
|
+
* - constructor passed hotReload: true | HotReloadConfig
|
|
822
|
+
* - OR HMR_ENABLED=true in the .env file
|
|
823
|
+
*/
|
|
824
|
+
resolveHotReloadConfig() {
|
|
825
|
+
const env = this.getEnvironmentValue;
|
|
826
|
+
if (this.hotReloadCfg === null && !env.hmrEnabled) {
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
const base = this.hotReloadCfg ?? {};
|
|
830
|
+
const envExtensions = this.hmrExtractExtensions(env.hmrWatchPatterns);
|
|
831
|
+
const envIgnore = this.hmrExtractIgnore(env.hmrIgnorePatterns);
|
|
832
|
+
return {
|
|
833
|
+
rootDir: base.rootDir,
|
|
834
|
+
watchDirs: base.watchDirs,
|
|
835
|
+
watchExtensions: base.watchExtensions ?? (envExtensions.length ? envExtensions : void 0),
|
|
836
|
+
ignore: base.ignore ?? (envIgnore.length ? envIgnore : void 0),
|
|
837
|
+
hotReloadExtensions: base.hotReloadExtensions,
|
|
838
|
+
debounceMs: base.debounceMs ?? (env.hmrDebounceMs > 0 ? env.hmrDebounceMs : void 0),
|
|
839
|
+
envFile: base.envFile
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Extract file extensions from glob patterns such as "src/** /*.ts".
|
|
844
|
+
* "src/** /*.ts"
|
|
845
|
+
* ".env" skipped, handled natively by the watcher
|
|
846
|
+
*/
|
|
847
|
+
hmrExtractExtensions(patterns) {
|
|
848
|
+
const exts = /* @__PURE__ */ new Set();
|
|
849
|
+
for (const p of patterns) {
|
|
850
|
+
const m = p.match(/\*(\.[a-zA-Z0-9]+)$/);
|
|
851
|
+
if (m) {
|
|
852
|
+
exts.add(m[1]);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return [...exts];
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Extract ignore names from glob patterns such as "node_modules/**".
|
|
859
|
+
* "node_modules/**" → "node_modules"
|
|
860
|
+
* "dist/**" → "dist"
|
|
861
|
+
*/
|
|
862
|
+
hmrExtractIgnore(patterns) {
|
|
863
|
+
return [...new Set(
|
|
864
|
+
patterns.map((p) => p.split("/")[0]).filter(Boolean)
|
|
865
|
+
)];
|
|
866
|
+
}
|
|
476
867
|
/**
|
|
477
868
|
*
|
|
478
869
|
* @param allFeatureRoutes
|
|
@@ -505,6 +896,7 @@ var WebServerCore = class {
|
|
|
505
896
|
}
|
|
506
897
|
};
|
|
507
898
|
export {
|
|
899
|
+
HotReloadWatcher,
|
|
508
900
|
WebServerCore as WebServer,
|
|
509
901
|
envPath
|
|
510
902
|
};
|