opticore-webapp 1.0.70 → 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 +506 -24
- package/dist/index.d.cts +178 -1
- package/dist/index.d.ts +178 -1
- package/dist/index.js +529 -48
- 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.cjs
CHANGED
|
@@ -30,20 +30,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
HotReloadWatcher: () => HotReloadWatcher,
|
|
33
34
|
WebServer: () => WebServerCore,
|
|
34
35
|
envPath: () => envPath
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(index_exports);
|
|
37
38
|
|
|
38
39
|
// src/core/webServer.core.ts
|
|
39
|
-
var
|
|
40
|
-
var
|
|
40
|
+
var path3 = __toESM(require("path"), 1);
|
|
41
|
+
var import_node_process4 = __toESM(require("process"), 1);
|
|
41
42
|
var import_cors = __toESM(require("cors"), 1);
|
|
42
43
|
var import_opticore_catch_exception_error3 = require("opticore-catch-exception-error");
|
|
43
44
|
var import_opticore_express2 = require("opticore-express");
|
|
44
45
|
var import_opticore_env_access2 = require("opticore-env-access");
|
|
45
|
-
var
|
|
46
|
-
var
|
|
46
|
+
var import_opticore_http_response4 = require("opticore-http-response");
|
|
47
|
+
var import_opticore_translator3 = require("opticore-translator");
|
|
47
48
|
var import_opticore_request_call_event = require("opticore-request-call-event");
|
|
48
49
|
|
|
49
50
|
// src/core/handlers/eventProcess.handler.ts
|
|
@@ -149,16 +150,35 @@ var dependenciesContainerProvider = (localLang) => {
|
|
|
149
150
|
return new import_opticore_dependency_inject.SContainer(localLang, dependencies);
|
|
150
151
|
};
|
|
151
152
|
|
|
153
|
+
// src/application/service/core.service.ts
|
|
154
|
+
var import_opticore_dependency_inject2 = require("opticore-dependency-inject");
|
|
155
|
+
|
|
156
|
+
// src/utils/parsingDateTime.utils.ts
|
|
157
|
+
var parsingDateTimeUtils = (localFormatTime) => {
|
|
158
|
+
const now = /* @__PURE__ */ new Date();
|
|
159
|
+
const formatter = new Intl.DateTimeFormat(localFormatTime, {
|
|
160
|
+
day: "2-digit",
|
|
161
|
+
month: "2-digit",
|
|
162
|
+
year: "numeric",
|
|
163
|
+
hour: "2-digit",
|
|
164
|
+
minute: "2-digit",
|
|
165
|
+
hour12: false
|
|
166
|
+
});
|
|
167
|
+
return formatter.format(now);
|
|
168
|
+
};
|
|
169
|
+
|
|
152
170
|
// src/application/service/core.service.ts
|
|
153
171
|
var CoreService = class {
|
|
154
172
|
localLanguage;
|
|
155
173
|
environmentPath;
|
|
156
174
|
serverLog;
|
|
157
175
|
logger;
|
|
176
|
+
container;
|
|
158
177
|
constructor(localLang, environmentPath) {
|
|
159
178
|
this.loadTranslationFiles();
|
|
160
179
|
this.environmentPath = environmentPath;
|
|
161
180
|
this.localLanguage = localLang;
|
|
181
|
+
this.container = new import_opticore_dependency_inject2.SContainer(localLang);
|
|
162
182
|
this.serverLog = dependenciesContainerProvider(localLang).resolve("OpticoreLogger");
|
|
163
183
|
this.logger = dependenciesContainerProvider(localLang).resolve("LoggerCore");
|
|
164
184
|
}
|
|
@@ -298,7 +318,7 @@ var CoreService = class {
|
|
|
298
318
|
this.loadTranslationFiles();
|
|
299
319
|
try {
|
|
300
320
|
const getEnvironment = (0, import_opticore_env_access.getEnvironmentValue)(this.environmentPath);
|
|
301
|
-
const msg5 = getEnvironment.
|
|
321
|
+
const msg5 = getEnvironment.protocolTransfer === "" ? import_ansi_colors.default.underline(`http://${host}:${port}`) : import_ansi_colors.default.underline(`${getEnvironment.protocolTransfer}://${host}:${port}`);
|
|
302
322
|
const messages = [
|
|
303
323
|
import_opticore_translator.TranslationLoader.t("webServerListening", this.localLanguage),
|
|
304
324
|
import_opticore_translator.TranslationLoader.t("webServerUsingNodeVersion", this.localLanguage, { nodeVersion: this.getVersions().nodeVersion }),
|
|
@@ -318,17 +338,31 @@ var CoreService = class {
|
|
|
318
338
|
});
|
|
319
339
|
console.log(border);
|
|
320
340
|
console.log("\n");
|
|
321
|
-
this.logger.success({
|
|
322
|
-
title: import_opticore_translator.TranslationLoader.t("serverRunningTitle", this.localLanguage),
|
|
323
|
-
message: import_opticore_translator.TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
|
|
324
|
-
});
|
|
325
341
|
this.serverLog.serverLog({
|
|
326
|
-
timestamp: (
|
|
342
|
+
timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
|
|
327
343
|
level: "SERVER",
|
|
328
344
|
title: import_opticore_translator.TranslationLoader.t("serverRunningTitle", this.localLanguage),
|
|
329
345
|
typeName: import_opticore_translator.TranslationLoader.t("opticoreServerTypeName", this.localLanguage),
|
|
330
346
|
message: import_opticore_translator.TranslationLoader.t("serverRunningAt", this.localLanguage, { server: msg5 })
|
|
331
347
|
});
|
|
348
|
+
const dependenciesList = this.container.listDependencies();
|
|
349
|
+
if (dependenciesList.length > 0) {
|
|
350
|
+
this.serverLog.opticoreLog({
|
|
351
|
+
level: "OPTICORE",
|
|
352
|
+
message: import_opticore_translator.TranslationLoader.t("DEPENDENCIES_STORED", this.localLanguage, { dependenciesList: dependenciesList.join(", ") }),
|
|
353
|
+
timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
|
|
354
|
+
title: import_opticore_translator.TranslationLoader.t("DEPENDENCIES_CONTAINER", this.localLanguage),
|
|
355
|
+
typeName: "DEPENDENCIES_CONTAINER"
|
|
356
|
+
});
|
|
357
|
+
} else {
|
|
358
|
+
this.serverLog.opticoreLog({
|
|
359
|
+
level: "OPTICORE",
|
|
360
|
+
message: import_opticore_translator.TranslationLoader.t("NO_DEPENDENCIES_STORED", this.localLanguage),
|
|
361
|
+
timestamp: parsingDateTimeUtils(getEnvironment.localFormatTime),
|
|
362
|
+
title: import_opticore_translator.TranslationLoader.t("DEPENDENCIES_CONTAINER", this.localLanguage),
|
|
363
|
+
typeName: "DEPENDENCIES_CONTAINER"
|
|
364
|
+
});
|
|
365
|
+
}
|
|
332
366
|
} catch (err) {
|
|
333
367
|
this.logger.error({
|
|
334
368
|
message: err.message,
|
|
@@ -392,10 +426,46 @@ var SServerStartError = (err, environmentPath) => {
|
|
|
392
426
|
};
|
|
393
427
|
|
|
394
428
|
// src/core/webServer.core.ts
|
|
395
|
-
var
|
|
429
|
+
var import_opticore_dependency_inject3 = require("opticore-dependency-inject");
|
|
430
|
+
|
|
431
|
+
// src/application/service/getEnvFileLoading.service.ts
|
|
432
|
+
var import_path = __toESM(require("path"), 1);
|
|
433
|
+
var import_node_process3 = __toESM(require("process"), 1);
|
|
434
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
435
|
+
var import_opticore_translator2 = require("opticore-translator");
|
|
436
|
+
var import_opticore_http_response3 = require("opticore-http-response");
|
|
437
|
+
var getEnvFileLoadingService = (filePath, localLanguage) => {
|
|
438
|
+
loaderTranslationFile(localLanguage);
|
|
439
|
+
const logger = dependenciesContainerProvider(localLanguage).resolve("LoggerCore");
|
|
440
|
+
try {
|
|
441
|
+
const fullPath = import_path.default.resolve(import_node_process3.default.cwd(), filePath);
|
|
442
|
+
if (import_fs.default.existsSync(fullPath)) {
|
|
443
|
+
const env = import_fs.default.readFileSync(fullPath, "utf-8");
|
|
444
|
+
const lines = env.split("\n");
|
|
445
|
+
lines.forEach((line) => {
|
|
446
|
+
const match = line.match(/^([^#=]+)=([^#]+)$/);
|
|
447
|
+
if (match) {
|
|
448
|
+
const key = match[1].trim();
|
|
449
|
+
import_node_process3.default.env[key] = match[2].trim();
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
} catch (err) {
|
|
454
|
+
logger.error({
|
|
455
|
+
message: err.message,
|
|
456
|
+
title: import_opticore_translator2.TranslationLoader.t("EnvFileLoading", localLanguage),
|
|
457
|
+
errorType: err.code,
|
|
458
|
+
stackTrace: err.stack,
|
|
459
|
+
httpCodeValue: import_opticore_http_response3.HttpStatusCode.INTERNAL_SERVER_ERROR
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// src/core/webServer.core.ts
|
|
396
465
|
var WebServerCore = class {
|
|
397
466
|
serverUtility;
|
|
398
467
|
expressApp = (0, import_opticore_express2.express)();
|
|
468
|
+
container;
|
|
399
469
|
localLanguage;
|
|
400
470
|
loggerConfig;
|
|
401
471
|
routerExpressApp;
|
|
@@ -411,6 +481,7 @@ var WebServerCore = class {
|
|
|
411
481
|
this.loggerConfig = paramsConstructor.loggerConfig;
|
|
412
482
|
this.localLanguage = paramsConstructor.localLanguage;
|
|
413
483
|
this.environmentPath = paramsConstructor.environmentPath;
|
|
484
|
+
this.container = new import_opticore_dependency_inject3.SContainer(paramsConstructor.localLanguage);
|
|
414
485
|
this.expressApp.use(import_opticore_express2.express.json());
|
|
415
486
|
this.expressApp.use(import_opticore_express2.express.raw());
|
|
416
487
|
this.expressApp.use(import_opticore_express2.express.text());
|
|
@@ -419,16 +490,17 @@ var WebServerCore = class {
|
|
|
419
490
|
this.serverListenEvent = new import_opticore_catch_exception_error3.ServerListenEventError(paramsConstructor.localLanguage);
|
|
420
491
|
this.serverUtility = new CoreService(paramsConstructor.localLanguage, paramsConstructor.environmentPath);
|
|
421
492
|
}
|
|
493
|
+
/**
|
|
494
|
+
*
|
|
495
|
+
* @param dependencies
|
|
496
|
+
*/
|
|
422
497
|
registerDependencies(dependencies2) {
|
|
498
|
+
this.loadTranslationFiles();
|
|
423
499
|
try {
|
|
500
|
+
getEnvFileLoadingService(".env", this.localLanguage);
|
|
424
501
|
if (dependencies2 && dependencies2.length > 0) {
|
|
425
502
|
dependencies2.forEach((dependency) => {
|
|
426
|
-
|
|
427
|
-
});
|
|
428
|
-
} else {
|
|
429
|
-
SLogger(this.environmentPath).logger.info({
|
|
430
|
-
message: import_opticore_translator2.TranslationLoader.t("NO_REGISTER_DEPENDENCIES", this.localLanguage),
|
|
431
|
-
title: import_opticore_translator2.TranslationLoader.t("REGISTER_DEPENDENCIES", this.localLanguage)
|
|
503
|
+
this.container.register(dependency.key, dependency.factory, dependency.scope);
|
|
432
504
|
});
|
|
433
505
|
}
|
|
434
506
|
} catch (err) {
|
|
@@ -437,12 +509,19 @@ var WebServerCore = class {
|
|
|
437
509
|
title: "Register Dependencies",
|
|
438
510
|
errorType: err.code,
|
|
439
511
|
stackTrace: err.stack,
|
|
440
|
-
httpCodeValue:
|
|
512
|
+
httpCodeValue: import_opticore_http_response4.HttpStatusCode.INTERNAL_SERVER_ERROR
|
|
441
513
|
});
|
|
442
514
|
}
|
|
443
515
|
}
|
|
516
|
+
/**
|
|
517
|
+
*
|
|
518
|
+
* @param routers
|
|
519
|
+
* @param databaseCallback
|
|
520
|
+
* @param dependenciesProvider
|
|
521
|
+
*/
|
|
444
522
|
onStartServer(routers, databaseCallback, dependenciesProvider) {
|
|
445
523
|
this.loadTranslationFiles();
|
|
524
|
+
getEnvFileLoadingService(".env", this.localLanguage);
|
|
446
525
|
if (this.getEnvironmentValue.appPort === "" && Number(this.getEnvironmentValue.appPort) === 0) {
|
|
447
526
|
this.serverListenEvent.hostPortUndefined(Number(this.getEnvironmentValue.appPort));
|
|
448
527
|
} else if (this.getEnvironmentValue.appHost === "") {
|
|
@@ -451,11 +530,11 @@ var WebServerCore = class {
|
|
|
451
530
|
this.serverListenEvent.portUndefined();
|
|
452
531
|
} else if (this.localLanguage === "") {
|
|
453
532
|
SLogger(this.environmentPath).logger.error({
|
|
454
|
-
message:
|
|
455
|
-
title:
|
|
456
|
-
errorType:
|
|
533
|
+
message: import_opticore_translator3.TranslationLoader.t("noDefaultLocalLang", this.localLanguage),
|
|
534
|
+
title: import_opticore_translator3.TranslationLoader.t("noLocalLang", this.localLanguage),
|
|
535
|
+
errorType: import_opticore_translator3.TranslationLoader.t("localLangMissing", this.localLanguage),
|
|
457
536
|
stackTrace: void 0,
|
|
458
|
-
httpCodeValue:
|
|
537
|
+
httpCodeValue: import_opticore_http_response4.HttpStatusCode.NOT_FOUND
|
|
459
538
|
});
|
|
460
539
|
} else {
|
|
461
540
|
return this.expressApp.listen(
|
|
@@ -464,8 +543,11 @@ var WebServerCore = class {
|
|
|
464
543
|
this.loadTranslationFiles();
|
|
465
544
|
try {
|
|
466
545
|
databaseCallback(this.getEnvironmentValue);
|
|
467
|
-
|
|
468
|
-
|
|
546
|
+
if (dependenciesProvider) {
|
|
547
|
+
this.registerDependencies(dependenciesProvider);
|
|
548
|
+
}
|
|
549
|
+
this.container.loadServices();
|
|
550
|
+
this.expressApp.use(import_opticore_express2.express.static(path3.join(import_node_process4.default.cwd(), "public/template")));
|
|
469
551
|
this.registerRoutes(routers);
|
|
470
552
|
} catch (err) {
|
|
471
553
|
SServerStartError(err, this.environmentPath);
|
|
@@ -474,6 +556,10 @@ var WebServerCore = class {
|
|
|
474
556
|
);
|
|
475
557
|
}
|
|
476
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
*
|
|
561
|
+
* @param serverWeb
|
|
562
|
+
*/
|
|
477
563
|
onListeningOnServerEvent(serverWeb) {
|
|
478
564
|
this.loadTranslationFiles();
|
|
479
565
|
serverWeb.on(import_opticore_catch_exception_error3.CEventNameError.error, (err) => {
|
|
@@ -486,6 +572,10 @@ var WebServerCore = class {
|
|
|
486
572
|
this.infoWebApp();
|
|
487
573
|
});
|
|
488
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
*
|
|
577
|
+
* @param serverWeb
|
|
578
|
+
*/
|
|
489
579
|
onRequestOnServerEvent(serverWeb) {
|
|
490
580
|
this.loadTranslationFiles();
|
|
491
581
|
serverWeb.on(import_opticore_catch_exception_error3.CEventNameError.request, (req, res) => {
|
|
@@ -538,8 +628,400 @@ var WebServerCore = class {
|
|
|
538
628
|
);
|
|
539
629
|
}
|
|
540
630
|
};
|
|
631
|
+
|
|
632
|
+
// src/hotReload/hotReload.watcher.ts
|
|
633
|
+
var import_child_process = require("child_process");
|
|
634
|
+
var import_fs2 = require("fs");
|
|
635
|
+
var import_promises = require("fs/promises");
|
|
636
|
+
var import_path2 = require("path");
|
|
637
|
+
var import_dotenv = require("dotenv");
|
|
638
|
+
var import_chalk2 = __toESM(require("chalk"), 1);
|
|
639
|
+
var import_ansi_colors2 = __toESM(require("ansi-colors"), 1);
|
|
640
|
+
var import_gradient_string = __toESM(require("gradient-string"), 1);
|
|
641
|
+
var DEFAULT_WATCH_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs", ".json"];
|
|
642
|
+
var DEFAULT_HOT_RELOAD_EXTENSIONS = [".json"];
|
|
643
|
+
var DEFAULT_IGNORE = [
|
|
644
|
+
"node_modules",
|
|
645
|
+
"dist",
|
|
646
|
+
".git",
|
|
647
|
+
"package.json",
|
|
648
|
+
"package-lock.json",
|
|
649
|
+
".idea",
|
|
650
|
+
".vscode",
|
|
651
|
+
"coverage",
|
|
652
|
+
"logs"
|
|
653
|
+
];
|
|
654
|
+
var DEFAULT_DEBOUNCE_MS = 300;
|
|
655
|
+
var DEFAULT_MAX_CRASH_RESTARTS = 5;
|
|
656
|
+
var HotReloadWatcher = class {
|
|
657
|
+
cfg;
|
|
658
|
+
child = null;
|
|
659
|
+
watchers = [];
|
|
660
|
+
debounceTimer = null;
|
|
661
|
+
isRestarting = false;
|
|
662
|
+
crashRestartCount = 0;
|
|
663
|
+
started = false;
|
|
664
|
+
restartStart = 0;
|
|
665
|
+
constructor(config) {
|
|
666
|
+
this.cfg = {
|
|
667
|
+
entry: (0, import_path2.resolve)(config.entry),
|
|
668
|
+
runtime: config.runtime ?? "node",
|
|
669
|
+
runtimeArgs: config.runtimeArgs ?? [],
|
|
670
|
+
rootDir: (0, import_path2.resolve)(config.rootDir ?? process.cwd()),
|
|
671
|
+
watchDirs: config.watchDirs ?? [],
|
|
672
|
+
watchExtensions: config.watchExtensions ?? DEFAULT_WATCH_EXTENSIONS,
|
|
673
|
+
ignore: [...DEFAULT_IGNORE, ...config.ignore ?? []],
|
|
674
|
+
envFile: config.envFile ?? ".env",
|
|
675
|
+
hotReloadExtensions: config.hotReloadExtensions ?? DEFAULT_HOT_RELOAD_EXTENSIONS,
|
|
676
|
+
debounceMs: config.debounceMs ?? DEFAULT_DEBOUNCE_MS,
|
|
677
|
+
restartOnCrash: config.restartOnCrash ?? true,
|
|
678
|
+
maxCrashRestarts: config.maxCrashRestarts ?? DEFAULT_MAX_CRASH_RESTARTS
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
// ─── Public API ──────────────────────────────────────────────────────────
|
|
682
|
+
async start() {
|
|
683
|
+
if (this.started) return;
|
|
684
|
+
this.started = true;
|
|
685
|
+
this.printBanner();
|
|
686
|
+
this.spawnChild();
|
|
687
|
+
await this.setupWatchers();
|
|
688
|
+
this.setupProcessSignals();
|
|
689
|
+
}
|
|
690
|
+
async stop() {
|
|
691
|
+
this.watchers.forEach((w) => {
|
|
692
|
+
w.close();
|
|
693
|
+
});
|
|
694
|
+
this.watchers = [];
|
|
695
|
+
await this.killChild(true);
|
|
696
|
+
this.printStopped();
|
|
697
|
+
}
|
|
698
|
+
// ─── Child process management ────────────────────────────────────────────
|
|
699
|
+
spawnChild() {
|
|
700
|
+
const { entry, runtime, runtimeArgs } = this.cfg;
|
|
701
|
+
try {
|
|
702
|
+
this.child = runtime === "node" ? (0, import_child_process.fork)(entry, [], {
|
|
703
|
+
stdio: ["inherit", "inherit", "inherit", "ipc"],
|
|
704
|
+
env: process.env
|
|
705
|
+
}) : (0, import_child_process.spawn)(runtime, [...runtimeArgs, entry], {
|
|
706
|
+
stdio: "inherit",
|
|
707
|
+
env: process.env,
|
|
708
|
+
shell: false
|
|
709
|
+
});
|
|
710
|
+
this.child.on("exit", (code) => {
|
|
711
|
+
if (this.isRestarting) return;
|
|
712
|
+
if (code !== 0 && code !== null) {
|
|
713
|
+
this.printCrash(code);
|
|
714
|
+
if (this.cfg.restartOnCrash && this.crashRestartCount < this.cfg.maxCrashRestarts) {
|
|
715
|
+
this.crashRestartCount++;
|
|
716
|
+
this.printCrashRetry(this.crashRestartCount, this.cfg.maxCrashRestarts);
|
|
717
|
+
setTimeout(() => {
|
|
718
|
+
this.spawnChild();
|
|
719
|
+
}, 1e3);
|
|
720
|
+
} else if (this.crashRestartCount >= this.cfg.maxCrashRestarts) {
|
|
721
|
+
this.printCrashLimit(this.cfg.maxCrashRestarts);
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
this.child.on("error", (err) => {
|
|
727
|
+
this.printError(`Failed to start child process: ${err.message}`);
|
|
728
|
+
});
|
|
729
|
+
} catch (err) {
|
|
730
|
+
this.printError(`Cannot spawn '${runtime} ${entry}': ${err.message}`);
|
|
731
|
+
process.exit(1);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
async killChild(silent = false) {
|
|
735
|
+
if (!this.child) return;
|
|
736
|
+
const child = this.child;
|
|
737
|
+
this.child = null;
|
|
738
|
+
await new Promise((done) => {
|
|
739
|
+
const forceKill = setTimeout(() => {
|
|
740
|
+
child.kill("SIGKILL");
|
|
741
|
+
done();
|
|
742
|
+
}, 3e3);
|
|
743
|
+
child.once("exit", () => {
|
|
744
|
+
clearTimeout(forceKill);
|
|
745
|
+
done();
|
|
746
|
+
});
|
|
747
|
+
child.kill("SIGTERM");
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
// ─── File watching ────────────────────────────────────────────────────────
|
|
751
|
+
async setupWatchers() {
|
|
752
|
+
const dirs = [
|
|
753
|
+
this.cfg.rootDir,
|
|
754
|
+
...this.cfg.watchDirs.map((d) => (0, import_path2.resolve)(d))
|
|
755
|
+
];
|
|
756
|
+
for (const dir of dirs) {
|
|
757
|
+
await this.watchDirectoryRecursive(dir);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
async watchDirectoryRecursive(dir) {
|
|
761
|
+
if (this.shouldIgnoreDir(dir)) return;
|
|
762
|
+
try {
|
|
763
|
+
await (0, import_promises.access)(dir);
|
|
764
|
+
} catch {
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
try {
|
|
768
|
+
const watcher = (0, import_fs2.watch)(dir, (event2, filename) => {
|
|
769
|
+
if (!filename) return;
|
|
770
|
+
this.onFileChange((0, import_path2.join)(dir, filename));
|
|
771
|
+
});
|
|
772
|
+
watcher.on("error", () => {
|
|
773
|
+
});
|
|
774
|
+
this.watchers.push(watcher);
|
|
775
|
+
const entries = await (0, import_promises.readdir)(dir, { withFileTypes: true });
|
|
776
|
+
for (const entry of entries) {
|
|
777
|
+
if (entry.isDirectory()) {
|
|
778
|
+
await this.watchDirectoryRecursive((0, import_path2.join)(dir, entry.name));
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
} catch {
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
// ─── Change handling ─────────────────────────────────────────────────────
|
|
785
|
+
onFileChange(filePath) {
|
|
786
|
+
if (this.shouldIgnoreFile(filePath)) return;
|
|
787
|
+
if (!this.isWatchedFile(filePath)) return;
|
|
788
|
+
const rel = (0, import_path2.relative)(this.cfg.rootDir, filePath);
|
|
789
|
+
if (this.isHotReloadable(filePath)) {
|
|
790
|
+
this.doHotReload(filePath, rel);
|
|
791
|
+
} else {
|
|
792
|
+
this.scheduleRestart(rel);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
doHotReload(filePath, relativePath) {
|
|
796
|
+
if (this.isEnvFile(filePath)) {
|
|
797
|
+
(0, import_dotenv.config)({ path: filePath, override: true });
|
|
798
|
+
this.printHot(relativePath, "env variables reloaded");
|
|
799
|
+
this.sendIpc({ type: "hot-reload", file: relativePath, kind: "env" });
|
|
800
|
+
} else {
|
|
801
|
+
this.printHot(relativePath, "json config reloaded");
|
|
802
|
+
this.sendIpc({ type: "hot-reload", file: relativePath, kind: "json" });
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
scheduleRestart(relativePath) {
|
|
806
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
807
|
+
this.debounceTimer = setTimeout(async () => {
|
|
808
|
+
this.printReloading(relativePath);
|
|
809
|
+
this.restartStart = Date.now();
|
|
810
|
+
this.isRestarting = true;
|
|
811
|
+
this.crashRestartCount = 0;
|
|
812
|
+
await this.killChild(true);
|
|
813
|
+
this.isRestarting = false;
|
|
814
|
+
this.spawnChild();
|
|
815
|
+
this.printReady(Date.now() - this.restartStart);
|
|
816
|
+
}, this.cfg.debounceMs);
|
|
817
|
+
}
|
|
818
|
+
// ─── IPC ─────────────────────────────────────────────────────────────────
|
|
819
|
+
sendIpc(message) {
|
|
820
|
+
if (this.child && typeof this.child.send === "function") {
|
|
821
|
+
try {
|
|
822
|
+
this.child.send(message);
|
|
823
|
+
} catch {
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
// ─── Ignore / watch filters ───────────────────────────────────────────────
|
|
828
|
+
shouldIgnoreDir(dirPath) {
|
|
829
|
+
return this.cfg.ignore.some((p) => dirPath.includes(`/${p}`) || dirPath.endsWith(p));
|
|
830
|
+
}
|
|
831
|
+
shouldIgnoreFile(filePath) {
|
|
832
|
+
const name = (0, import_path2.basename)(filePath);
|
|
833
|
+
const rel = (0, import_path2.relative)(this.cfg.rootDir, filePath);
|
|
834
|
+
return this.cfg.ignore.some((p) => name === p || rel.includes(p) || filePath.includes(`/${p}/`));
|
|
835
|
+
}
|
|
836
|
+
isWatchedFile(filePath) {
|
|
837
|
+
if (this.isEnvFile(filePath)) return true;
|
|
838
|
+
return this.cfg.watchExtensions.includes((0, import_path2.extname)(filePath));
|
|
839
|
+
}
|
|
840
|
+
isHotReloadable(filePath) {
|
|
841
|
+
if (this.isEnvFile(filePath)) return true;
|
|
842
|
+
return this.cfg.hotReloadExtensions.includes((0, import_path2.extname)(filePath));
|
|
843
|
+
}
|
|
844
|
+
isEnvFile(filePath) {
|
|
845
|
+
const name = (0, import_path2.basename)(filePath);
|
|
846
|
+
return name === this.cfg.envFile || name.startsWith(".env");
|
|
847
|
+
}
|
|
848
|
+
// ─── ANSI strip helper (for width calculation) ────────────────────────────
|
|
849
|
+
strip(str) {
|
|
850
|
+
return str.replace(/\[[0-9;]*m/g, "").replace(/\][^]*/g, "");
|
|
851
|
+
}
|
|
852
|
+
// ─── Timestamp ────────────────────────────────────────────────────────────
|
|
853
|
+
ts() {
|
|
854
|
+
const n = /* @__PURE__ */ new Date();
|
|
855
|
+
const h = String(n.getHours()).padStart(2, "0");
|
|
856
|
+
const m = String(n.getMinutes()).padStart(2, "0");
|
|
857
|
+
const s = String(n.getSeconds()).padStart(2, "0");
|
|
858
|
+
return `${h}:${m}:${s}`;
|
|
859
|
+
}
|
|
860
|
+
// ─── Console output ───────────────────────────────────────────────────────
|
|
861
|
+
/**
|
|
862
|
+
* Startup banner — mirrors the infoServer() box style from CoreService.
|
|
863
|
+
*
|
|
864
|
+
* ╔══════════════════════════════════════════╗
|
|
865
|
+
* gradient title
|
|
866
|
+
* ╔══ bgGreen box ════════════════════════╗
|
|
867
|
+
* entry dist/index.js
|
|
868
|
+
* runtime node (IPC enabled)
|
|
869
|
+
* root ./src
|
|
870
|
+
* watching .ts .js .json .env
|
|
871
|
+
* debounce 300ms
|
|
872
|
+
* ╚══════════════════════════════════════════╝
|
|
873
|
+
*/
|
|
874
|
+
printBanner() {
|
|
875
|
+
const entryRel = (0, import_path2.relative)(process.cwd(), this.cfg.entry) || this.cfg.entry;
|
|
876
|
+
const rootRel = (0, import_path2.relative)(process.cwd(), this.cfg.rootDir) || ".";
|
|
877
|
+
const runtimeStr = this.cfg.runtime === "node" ? `node ${import_chalk2.default.blackBright("(IPC enabled)")}` : this.cfg.runtime;
|
|
878
|
+
const extStr = [...this.cfg.watchExtensions, ".env"].join(" ");
|
|
879
|
+
const ignoreStr = this.cfg.ignore.slice(0, 4).join(" ") + (this.cfg.ignore.length > 4 ? ` ${import_chalk2.default.blackBright(`+${this.cfg.ignore.length - 4} more`)}` : "");
|
|
880
|
+
const TITLE = "OPTICORE HOT RELOAD";
|
|
881
|
+
const rows = [
|
|
882
|
+
` entry ${import_chalk2.default.white.bold(entryRel)}`,
|
|
883
|
+
` runtime ${runtimeStr}`,
|
|
884
|
+
` root ${import_chalk2.default.white(rootRel)}`,
|
|
885
|
+
` watching ${import_chalk2.default.cyan(extStr)}`,
|
|
886
|
+
` ignoring ${import_chalk2.default.blackBright(ignoreStr)}`,
|
|
887
|
+
` debounce ${import_chalk2.default.white(`${this.cfg.debounceMs}ms`)}`
|
|
888
|
+
];
|
|
889
|
+
const maxLen = Math.max(
|
|
890
|
+
TITLE.length + 4,
|
|
891
|
+
...rows.map((r) => this.strip(r).length)
|
|
892
|
+
) + 4;
|
|
893
|
+
const border = import_chalk2.default.bgGreen.white(" ".repeat(maxLen));
|
|
894
|
+
const titlePad = " ".repeat(Math.max(0, Math.floor((maxLen - TITLE.length) / 2)));
|
|
895
|
+
console.log("\n" + titlePad + (0, import_gradient_string.default)(["#43e97b", "#38f9d7", "#00c6fb"])(TITLE));
|
|
896
|
+
console.log(border);
|
|
897
|
+
rows.forEach((row) => {
|
|
898
|
+
const cleanLen = this.strip(row).length;
|
|
899
|
+
const padding = Math.max(0, maxLen - cleanLen - 2);
|
|
900
|
+
console.log(import_chalk2.default.bgGreen.white(` ${row}${" ".repeat(padding)} `));
|
|
901
|
+
});
|
|
902
|
+
console.log(border);
|
|
903
|
+
console.log(import_chalk2.default.blackBright(` watching for changes...
|
|
904
|
+
`));
|
|
905
|
+
}
|
|
906
|
+
/**
|
|
907
|
+
* HOT event — in-process reload, no server restart.
|
|
908
|
+
*
|
|
909
|
+
* ✔ [ HOT ] 14:23:45 | .env → env variables reloaded
|
|
910
|
+
*/
|
|
911
|
+
printHot(file, action) {
|
|
912
|
+
const badge = import_ansi_colors2.default.bgCyan(import_ansi_colors2.default.white.bold(" HOT "));
|
|
913
|
+
const ts = import_chalk2.default.blackBright(this.ts());
|
|
914
|
+
const sep = import_chalk2.default.blackBright("|");
|
|
915
|
+
const filePart = import_chalk2.default.cyan.bold(file.padEnd(32));
|
|
916
|
+
const arrow = import_chalk2.default.green("\u2192");
|
|
917
|
+
const msg = import_chalk2.default.green(action);
|
|
918
|
+
console.log(` ${import_chalk2.default.green("\u2714")} ${badge} ${ts} ${sep} ${filePart} ${arrow} ${msg}`);
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* RELOAD event — server restart triggered.
|
|
922
|
+
*
|
|
923
|
+
* ⚡ [ RELOAD ] 14:24:03 | src/routes/user.ts → restarting server...
|
|
924
|
+
*/
|
|
925
|
+
printReloading(file) {
|
|
926
|
+
const badge = import_ansi_colors2.default.bgYellow(import_ansi_colors2.default.white.bold(" RELOAD "));
|
|
927
|
+
const ts = import_chalk2.default.blackBright(this.ts());
|
|
928
|
+
const sep = import_chalk2.default.blackBright("|");
|
|
929
|
+
const filePart = import_chalk2.default.yellow.bold(file.padEnd(32));
|
|
930
|
+
const arrow = import_chalk2.default.yellow("\u2192");
|
|
931
|
+
const msg = import_chalk2.default.yellow("restarting server...");
|
|
932
|
+
console.log(`
|
|
933
|
+
${import_chalk2.default.yellow("\u26A1")} ${badge} ${ts} ${sep} ${filePart} ${arrow} ${msg}`);
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* READY event — server successfully restarted.
|
|
937
|
+
* Uses the same full-width bgGreen border as infoServer().
|
|
938
|
+
*
|
|
939
|
+
* ════════════════════════════════════════════
|
|
940
|
+
* READY server restarted in 245ms
|
|
941
|
+
* ════════════════════════════════════════════
|
|
942
|
+
*/
|
|
943
|
+
printReady(elapsedMs) {
|
|
944
|
+
const msg = ` server restarted in ${import_chalk2.default.white.bold(`${elapsedMs}ms`)}`;
|
|
945
|
+
const cleanLen = this.strip(msg).length;
|
|
946
|
+
const width = Math.max(52, cleanLen + 6);
|
|
947
|
+
const border = import_chalk2.default.bgGreen.white(" ".repeat(width));
|
|
948
|
+
const padding = Math.max(0, width - cleanLen - 2);
|
|
949
|
+
const line = import_chalk2.default.bgGreen.white(` ${import_chalk2.default.bgGreen.white.bold(" READY ")} ${msg}${" ".repeat(padding)} `);
|
|
950
|
+
console.log(` ${border}`);
|
|
951
|
+
console.log(` ${line}`);
|
|
952
|
+
console.log(` ${border}
|
|
953
|
+
`);
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* CRASH event — unexpected child process exit.
|
|
957
|
+
*
|
|
958
|
+
* ✘ [ CRASH ] 14:24:10 | server exited with code 1
|
|
959
|
+
*/
|
|
960
|
+
printCrash(code) {
|
|
961
|
+
const badge = import_ansi_colors2.default.bgRed(import_ansi_colors2.default.white.bold(" CRASH "));
|
|
962
|
+
const ts = import_chalk2.default.blackBright(this.ts());
|
|
963
|
+
const sep = import_chalk2.default.blackBright("|");
|
|
964
|
+
const msg = import_chalk2.default.red(`server exited with code ${import_chalk2.default.bold(String(code))}`);
|
|
965
|
+
console.log(`
|
|
966
|
+
${import_chalk2.default.red("\u2718")} ${badge} ${ts} ${sep} ${msg}`);
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* RETRY event — auto-restart after crash.
|
|
970
|
+
*
|
|
971
|
+
* ↺ [ RETRY ] 14:24:11 | attempt 1 / 5
|
|
972
|
+
*/
|
|
973
|
+
printCrashRetry(attempt, max) {
|
|
974
|
+
const badge = import_ansi_colors2.default.bgMagenta(import_ansi_colors2.default.white.bold(" RETRY "));
|
|
975
|
+
const ts = import_chalk2.default.blackBright(this.ts());
|
|
976
|
+
const sep = import_chalk2.default.blackBright("|");
|
|
977
|
+
const msg = import_chalk2.default.magenta(`auto-restart attempt ${import_chalk2.default.bold(`${attempt} / ${max}`)}`);
|
|
978
|
+
console.log(` ${import_chalk2.default.magenta("\u21BA")} ${badge} ${ts} ${sep} ${msg}`);
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* LIMIT reached — give up restarting.
|
|
982
|
+
*/
|
|
983
|
+
printCrashLimit(max) {
|
|
984
|
+
const badge = import_ansi_colors2.default.bgRed(import_ansi_colors2.default.white.bold(" ERROR "));
|
|
985
|
+
const ts = import_chalk2.default.blackBright(this.ts());
|
|
986
|
+
const sep = import_chalk2.default.blackBright("|");
|
|
987
|
+
const msg = import_chalk2.default.red(`max crash restarts reached (${import_chalk2.default.bold(String(max))}), giving up`);
|
|
988
|
+
console.log(`
|
|
989
|
+
${import_chalk2.default.red("\u2718")} ${badge} ${ts} ${sep} ${msg}
|
|
990
|
+
`);
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Error — miscellaneous internal error.
|
|
994
|
+
*/
|
|
995
|
+
printError(message) {
|
|
996
|
+
const badge = import_ansi_colors2.default.bgRed(import_ansi_colors2.default.white.bold(" ERROR "));
|
|
997
|
+
const ts = import_chalk2.default.blackBright(this.ts());
|
|
998
|
+
const sep = import_chalk2.default.blackBright("|");
|
|
999
|
+
console.error(` ${import_chalk2.default.red("\u2718")} ${badge} ${ts} ${sep} ${import_chalk2.default.red(message)}`);
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Stopped — watcher shut down.
|
|
1003
|
+
*/
|
|
1004
|
+
printStopped() {
|
|
1005
|
+
const badge = import_ansi_colors2.default.bgBlackBright(import_ansi_colors2.default.white.bold(" STOPPED"));
|
|
1006
|
+
const ts = import_chalk2.default.blackBright(this.ts());
|
|
1007
|
+
const sep = import_chalk2.default.blackBright("|");
|
|
1008
|
+
console.log(` ${import_chalk2.default.gray("\u25A0")} ${badge} ${ts} ${sep} ${import_chalk2.default.gray("watcher stopped")}
|
|
1009
|
+
`);
|
|
1010
|
+
}
|
|
1011
|
+
// ─── Graceful shutdown ────────────────────────────────────────────────────
|
|
1012
|
+
setupProcessSignals() {
|
|
1013
|
+
const shutdown = async () => {
|
|
1014
|
+
console.log("");
|
|
1015
|
+
await this.stop();
|
|
1016
|
+
process.exit(0);
|
|
1017
|
+
};
|
|
1018
|
+
process.once("SIGINT", shutdown);
|
|
1019
|
+
process.once("SIGTERM", shutdown);
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
541
1022
|
// Annotate the CommonJS export names for ESM import in node:
|
|
542
1023
|
0 && (module.exports = {
|
|
1024
|
+
HotReloadWatcher,
|
|
543
1025
|
WebServer,
|
|
544
1026
|
envPath
|
|
545
1027
|
});
|