@spfn/core 0.1.0-alpha.81 → 0.1.0-alpha.83
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/cache/index.js +6 -115
- package/dist/cache/index.js.map +1 -1
- package/dist/client/index.d.ts +2 -2
- package/dist/codegen/generators/index.js +6 -113
- package/dist/codegen/generators/index.js.map +1 -1
- package/dist/codegen/index.d.ts +2 -2
- package/dist/codegen/index.js +6 -113
- package/dist/codegen/index.js.map +1 -1
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.js +41 -114
- package/dist/db/index.js.map +1 -1
- package/dist/env/index.js +6 -113
- package/dist/env/index.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +160 -157
- package/dist/index.js.map +1 -1
- package/dist/logger/index.d.ts +90 -34
- package/dist/logger/index.js +7 -116
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.d.ts +29 -2
- package/dist/middleware/index.js +22 -117
- package/dist/middleware/index.js.map +1 -1
- package/dist/route/index.d.ts +3 -3
- package/dist/route/index.js +12 -131
- package/dist/route/index.js.map +1 -1
- package/dist/server/index.d.ts +110 -4
- package/dist/server/index.js +160 -157
- package/dist/server/index.js.map +1 -1
- package/dist/{types-DYueuoD6.d.ts → types/index.d.ts} +19 -60
- package/dist/types/index.js +38 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types-BXibIEyj.d.ts +60 -0
- package/package.json +7 -3
- package/dist/error-handler-wjLL3v-a.d.ts +0 -44
package/dist/server/index.d.ts
CHANGED
|
@@ -229,13 +229,119 @@ interface ServerConfig {
|
|
|
229
229
|
detailed?: boolean;
|
|
230
230
|
};
|
|
231
231
|
/**
|
|
232
|
-
*
|
|
232
|
+
* Infrastructure initialization control
|
|
233
|
+
* Controls automatic initialization of database and Redis
|
|
234
|
+
* @default Both enabled if credentials exist
|
|
233
235
|
*/
|
|
234
|
-
|
|
236
|
+
infrastructure?: {
|
|
237
|
+
/**
|
|
238
|
+
* Enable/disable automatic database initialization
|
|
239
|
+
* @default true if DATABASE_URL exists
|
|
240
|
+
*/
|
|
241
|
+
database?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* Enable/disable automatic Redis initialization
|
|
244
|
+
* @default true if REDIS_URL exists
|
|
245
|
+
*/
|
|
246
|
+
redis?: boolean;
|
|
247
|
+
};
|
|
235
248
|
/**
|
|
236
|
-
*
|
|
249
|
+
* Server lifecycle hooks for custom infrastructure setup and management
|
|
250
|
+
* Allows initialization of custom services and resources at different stages
|
|
237
251
|
*/
|
|
238
|
-
|
|
252
|
+
lifecycle?: {
|
|
253
|
+
/**
|
|
254
|
+
* Hook: Run before infrastructure initialization
|
|
255
|
+
* Execute before database and Redis are initialized
|
|
256
|
+
* Use this for pre-initialization setup (logging, monitoring, etc.)
|
|
257
|
+
*
|
|
258
|
+
* @param config - The final server configuration
|
|
259
|
+
* @example
|
|
260
|
+
* ```typescript
|
|
261
|
+
* beforeInfrastructure: async (config) => {
|
|
262
|
+
* await initMonitoring();
|
|
263
|
+
* await setupCustomLogger();
|
|
264
|
+
* }
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
beforeInfrastructure?: (config: ServerConfig) => Promise<void>;
|
|
268
|
+
/**
|
|
269
|
+
* Hook: Run after infrastructure (DB/Redis) is initialized
|
|
270
|
+
* Database and Redis instances are available via getDatabase() and getRedis()
|
|
271
|
+
* Use this for:
|
|
272
|
+
* - Running migrations
|
|
273
|
+
* - Seeding initial data
|
|
274
|
+
* - Initializing services that depend on DB/Redis
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* import { getDatabase } from '@spfn/core/db';
|
|
279
|
+
* import { migrate } from 'drizzle-orm/postgres-js/migrator';
|
|
280
|
+
*
|
|
281
|
+
* afterInfrastructure: async () => {
|
|
282
|
+
* const db = getDatabase();
|
|
283
|
+
* await migrate(db, { migrationsFolder: './drizzle' });
|
|
284
|
+
* await seedInitialData(db);
|
|
285
|
+
* }
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
afterInfrastructure?: () => Promise<void>;
|
|
289
|
+
/**
|
|
290
|
+
* Hook: Run before routes are loaded
|
|
291
|
+
* Use this to add global middleware or prepare the app before routes
|
|
292
|
+
*
|
|
293
|
+
* @param app - The Hono app instance
|
|
294
|
+
* @example
|
|
295
|
+
* ```typescript
|
|
296
|
+
* beforeRoutes: async (app) => {
|
|
297
|
+
* app.use('/*', globalMiddleware());
|
|
298
|
+
* }
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
beforeRoutes?: (app: Hono) => void | Promise<void>;
|
|
302
|
+
/**
|
|
303
|
+
* Hook: Run after routes are loaded
|
|
304
|
+
* Use this to add fallback handlers or final middleware
|
|
305
|
+
*
|
|
306
|
+
* @param app - The Hono app instance
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* afterRoutes: async (app) => {
|
|
310
|
+
* app.notFound((c) => c.json({ error: 'Not Found' }, 404));
|
|
311
|
+
* }
|
|
312
|
+
* ```
|
|
313
|
+
*/
|
|
314
|
+
afterRoutes?: (app: Hono) => void | Promise<void>;
|
|
315
|
+
/**
|
|
316
|
+
* Hook: Run after server starts successfully
|
|
317
|
+
* Server is listening and ready to accept requests
|
|
318
|
+
* Receives the server instance for runtime access
|
|
319
|
+
*
|
|
320
|
+
* @param instance - The server instance with server, app, config, and close()
|
|
321
|
+
* @example
|
|
322
|
+
* ```typescript
|
|
323
|
+
* afterStart: async (instance) => {
|
|
324
|
+
* console.log(`Server ready at http://${instance.config.host}:${instance.config.port}`);
|
|
325
|
+
* await notifyHealthCheckService();
|
|
326
|
+
* }
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
afterStart?: (instance: ServerInstance) => Promise<void>;
|
|
330
|
+
/**
|
|
331
|
+
* Hook: Run before graceful shutdown
|
|
332
|
+
* Infrastructure (DB/Redis) is still available
|
|
333
|
+
* Use this to cleanup custom resources and services
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* beforeShutdown: async () => {
|
|
338
|
+
* await closeMessageQueue();
|
|
339
|
+
* await closeSearchService();
|
|
340
|
+
* }
|
|
341
|
+
* ```
|
|
342
|
+
*/
|
|
343
|
+
beforeShutdown?: () => Promise<void>;
|
|
344
|
+
};
|
|
239
345
|
}
|
|
240
346
|
/**
|
|
241
347
|
* App Factory Function
|
package/dist/server/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import pino from 'pino';
|
|
2
1
|
import { readFileSync, existsSync, readdirSync, statSync, mkdirSync, accessSync, constants, writeFileSync, unlinkSync, createWriteStream, renameSync } from 'fs';
|
|
3
2
|
import { join, dirname, relative, basename } from 'path';
|
|
4
3
|
import { config } from 'dotenv';
|
|
@@ -24,55 +23,6 @@ var __export = (target, all) => {
|
|
|
24
23
|
for (var name in all)
|
|
25
24
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
26
25
|
};
|
|
27
|
-
var PinoAdapter;
|
|
28
|
-
var init_pino = __esm({
|
|
29
|
-
"src/logger/adapters/pino.ts"() {
|
|
30
|
-
PinoAdapter = class _PinoAdapter {
|
|
31
|
-
logger;
|
|
32
|
-
constructor(config) {
|
|
33
|
-
this.logger = pino({
|
|
34
|
-
level: config.level,
|
|
35
|
-
// 기본 필드
|
|
36
|
-
base: config.module ? { module: config.module } : void 0
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
child(module) {
|
|
40
|
-
const childLogger = new _PinoAdapter({ level: this.logger.level, module });
|
|
41
|
-
childLogger.logger = this.logger.child({ module });
|
|
42
|
-
return childLogger;
|
|
43
|
-
}
|
|
44
|
-
debug(message, context) {
|
|
45
|
-
this.logger.debug(context || {}, message);
|
|
46
|
-
}
|
|
47
|
-
info(message, context) {
|
|
48
|
-
this.logger.info(context || {}, message);
|
|
49
|
-
}
|
|
50
|
-
warn(message, errorOrContext, context) {
|
|
51
|
-
if (errorOrContext instanceof Error) {
|
|
52
|
-
this.logger.warn({ err: errorOrContext, ...context }, message);
|
|
53
|
-
} else {
|
|
54
|
-
this.logger.warn(errorOrContext || {}, message);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
error(message, errorOrContext, context) {
|
|
58
|
-
if (errorOrContext instanceof Error) {
|
|
59
|
-
this.logger.error({ err: errorOrContext, ...context }, message);
|
|
60
|
-
} else {
|
|
61
|
-
this.logger.error(errorOrContext || {}, message);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
fatal(message, errorOrContext, context) {
|
|
65
|
-
if (errorOrContext instanceof Error) {
|
|
66
|
-
this.logger.fatal({ err: errorOrContext, ...context }, message);
|
|
67
|
-
} else {
|
|
68
|
-
this.logger.fatal(errorOrContext || {}, message);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
async close() {
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
26
|
|
|
77
27
|
// src/logger/types.ts
|
|
78
28
|
var LOG_LEVEL_PRIORITY;
|
|
@@ -748,7 +698,7 @@ var init_config = __esm({
|
|
|
748
698
|
}
|
|
749
699
|
});
|
|
750
700
|
|
|
751
|
-
// src/logger/
|
|
701
|
+
// src/logger/factory.ts
|
|
752
702
|
function initializeTransports() {
|
|
753
703
|
const transports = [];
|
|
754
704
|
const consoleConfig = getConsoleConfig();
|
|
@@ -759,89 +709,19 @@ function initializeTransports() {
|
|
|
759
709
|
}
|
|
760
710
|
return transports;
|
|
761
711
|
}
|
|
762
|
-
var CustomAdapter;
|
|
763
|
-
var init_custom = __esm({
|
|
764
|
-
"src/logger/adapters/custom.ts"() {
|
|
765
|
-
init_logger();
|
|
766
|
-
init_console();
|
|
767
|
-
init_file();
|
|
768
|
-
init_config();
|
|
769
|
-
CustomAdapter = class _CustomAdapter {
|
|
770
|
-
logger;
|
|
771
|
-
constructor(config) {
|
|
772
|
-
this.logger = new Logger({
|
|
773
|
-
level: config.level,
|
|
774
|
-
module: config.module,
|
|
775
|
-
transports: initializeTransports()
|
|
776
|
-
});
|
|
777
|
-
}
|
|
778
|
-
child(module) {
|
|
779
|
-
const adapter = new _CustomAdapter({ level: this.logger.level, module });
|
|
780
|
-
adapter.logger = this.logger.child(module);
|
|
781
|
-
return adapter;
|
|
782
|
-
}
|
|
783
|
-
debug(message, context) {
|
|
784
|
-
this.logger.debug(message, context);
|
|
785
|
-
}
|
|
786
|
-
info(message, context) {
|
|
787
|
-
this.logger.info(message, context);
|
|
788
|
-
}
|
|
789
|
-
warn(message, errorOrContext, context) {
|
|
790
|
-
if (errorOrContext instanceof Error) {
|
|
791
|
-
this.logger.warn(message, errorOrContext, context);
|
|
792
|
-
} else {
|
|
793
|
-
this.logger.warn(message, errorOrContext);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
error(message, errorOrContext, context) {
|
|
797
|
-
if (errorOrContext instanceof Error) {
|
|
798
|
-
this.logger.error(message, errorOrContext, context);
|
|
799
|
-
} else {
|
|
800
|
-
this.logger.error(message, errorOrContext);
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
fatal(message, errorOrContext, context) {
|
|
804
|
-
if (errorOrContext instanceof Error) {
|
|
805
|
-
this.logger.fatal(message, errorOrContext, context);
|
|
806
|
-
} else {
|
|
807
|
-
this.logger.fatal(message, errorOrContext);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
async close() {
|
|
811
|
-
await this.logger.close();
|
|
812
|
-
}
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
});
|
|
816
|
-
|
|
817
|
-
// src/logger/adapter-factory.ts
|
|
818
|
-
function createAdapter(type) {
|
|
819
|
-
const level = getDefaultLogLevel();
|
|
820
|
-
switch (type) {
|
|
821
|
-
case "pino":
|
|
822
|
-
return new PinoAdapter({ level });
|
|
823
|
-
case "custom":
|
|
824
|
-
return new CustomAdapter({ level });
|
|
825
|
-
default:
|
|
826
|
-
return new PinoAdapter({ level });
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
function getAdapterType() {
|
|
830
|
-
const adapterEnv = process.env.LOGGER_ADAPTER;
|
|
831
|
-
if (adapterEnv === "custom" || adapterEnv === "pino") {
|
|
832
|
-
return adapterEnv;
|
|
833
|
-
}
|
|
834
|
-
return "pino";
|
|
835
|
-
}
|
|
836
712
|
function initializeLogger() {
|
|
837
713
|
validateConfig();
|
|
838
|
-
return
|
|
714
|
+
return new Logger({
|
|
715
|
+
level: getDefaultLogLevel(),
|
|
716
|
+
transports: initializeTransports()
|
|
717
|
+
});
|
|
839
718
|
}
|
|
840
719
|
var logger;
|
|
841
|
-
var
|
|
842
|
-
"src/logger/
|
|
843
|
-
|
|
844
|
-
|
|
720
|
+
var init_factory = __esm({
|
|
721
|
+
"src/logger/factory.ts"() {
|
|
722
|
+
init_logger();
|
|
723
|
+
init_console();
|
|
724
|
+
init_file();
|
|
845
725
|
init_config();
|
|
846
726
|
logger = initializeLogger();
|
|
847
727
|
}
|
|
@@ -850,7 +730,8 @@ var init_adapter_factory = __esm({
|
|
|
850
730
|
// src/logger/index.ts
|
|
851
731
|
var init_logger2 = __esm({
|
|
852
732
|
"src/logger/index.ts"() {
|
|
853
|
-
|
|
733
|
+
init_factory();
|
|
734
|
+
init_logger();
|
|
854
735
|
}
|
|
855
736
|
});
|
|
856
737
|
|
|
@@ -1571,7 +1452,7 @@ async function createDatabaseFromEnv(options) {
|
|
|
1571
1452
|
}
|
|
1572
1453
|
}
|
|
1573
1454
|
var dbLogger2;
|
|
1574
|
-
var
|
|
1455
|
+
var init_factory2 = __esm({
|
|
1575
1456
|
"src/db/manager/factory.ts"() {
|
|
1576
1457
|
init_logger2();
|
|
1577
1458
|
init_env();
|
|
@@ -1687,17 +1568,51 @@ var dbLogger3;
|
|
|
1687
1568
|
var init_health_check = __esm({
|
|
1688
1569
|
"src/db/manager/health-check.ts"() {
|
|
1689
1570
|
init_logger2();
|
|
1690
|
-
|
|
1571
|
+
init_factory2();
|
|
1691
1572
|
init_global_state();
|
|
1692
1573
|
dbLogger3 = logger.child("database");
|
|
1693
1574
|
}
|
|
1694
1575
|
});
|
|
1695
1576
|
|
|
1696
1577
|
// src/db/manager/manager.ts
|
|
1578
|
+
function getCallerInfo() {
|
|
1579
|
+
try {
|
|
1580
|
+
const stack = new Error().stack;
|
|
1581
|
+
if (!stack) return void 0;
|
|
1582
|
+
const lines = stack.split("\n");
|
|
1583
|
+
for (let i = 3; i < lines.length; i++) {
|
|
1584
|
+
const line = lines[i];
|
|
1585
|
+
if (!line.includes("node_modules") && !line.includes("/db/manager/")) {
|
|
1586
|
+
const match = line.match(/\((.+):(\d+):(\d+)\)/) || line.match(/at (.+):(\d+):(\d+)/);
|
|
1587
|
+
if (match) {
|
|
1588
|
+
const fullPath = match[1];
|
|
1589
|
+
const parts = fullPath.split("/");
|
|
1590
|
+
const srcIndex = parts.lastIndexOf("src");
|
|
1591
|
+
if (srcIndex !== -1) {
|
|
1592
|
+
const relativePath = parts.slice(srcIndex).join("/");
|
|
1593
|
+
return `${relativePath}:${match[2]}`;
|
|
1594
|
+
}
|
|
1595
|
+
return `${fullPath}:${match[2]}`;
|
|
1596
|
+
}
|
|
1597
|
+
break;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
} catch {
|
|
1601
|
+
}
|
|
1602
|
+
return void 0;
|
|
1603
|
+
}
|
|
1697
1604
|
function getDatabase(type) {
|
|
1698
1605
|
const writeInst = getWriteInstance();
|
|
1699
1606
|
const readInst = getReadInstance();
|
|
1700
|
-
|
|
1607
|
+
if (process.env.DB_DEBUG_TRACE === "true") {
|
|
1608
|
+
const caller = getCallerInfo();
|
|
1609
|
+
dbLogger4.debug("getDatabase() called", {
|
|
1610
|
+
type: type || "write",
|
|
1611
|
+
hasWrite: !!writeInst,
|
|
1612
|
+
hasRead: !!readInst,
|
|
1613
|
+
caller
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1701
1616
|
if (type === "read") {
|
|
1702
1617
|
return readInst ?? writeInst;
|
|
1703
1618
|
}
|
|
@@ -1802,7 +1717,7 @@ var dbLogger4;
|
|
|
1802
1717
|
var init_manager = __esm({
|
|
1803
1718
|
"src/db/manager/manager.ts"() {
|
|
1804
1719
|
init_logger2();
|
|
1805
|
-
|
|
1720
|
+
init_factory2();
|
|
1806
1721
|
init_config3();
|
|
1807
1722
|
init_global_state();
|
|
1808
1723
|
init_health_check();
|
|
@@ -1813,7 +1728,7 @@ var init_manager = __esm({
|
|
|
1813
1728
|
// src/db/manager/index.ts
|
|
1814
1729
|
var init_manager2 = __esm({
|
|
1815
1730
|
"src/db/manager/index.ts"() {
|
|
1816
|
-
|
|
1731
|
+
init_factory2();
|
|
1817
1732
|
init_manager();
|
|
1818
1733
|
init_connection();
|
|
1819
1734
|
}
|
|
@@ -2996,6 +2911,22 @@ function generateRequestId() {
|
|
|
2996
2911
|
const randomPart = randomBytes(6).toString("hex");
|
|
2997
2912
|
return `req_${timestamp2}_${randomPart}`;
|
|
2998
2913
|
}
|
|
2914
|
+
function maskSensitiveData2(obj, sensitiveFields, seen = /* @__PURE__ */ new WeakSet()) {
|
|
2915
|
+
if (!obj || typeof obj !== "object") return obj;
|
|
2916
|
+
if (seen.has(obj)) return "[Circular]";
|
|
2917
|
+
seen.add(obj);
|
|
2918
|
+
const lowerFields = sensitiveFields.map((f) => f.toLowerCase());
|
|
2919
|
+
const masked = Array.isArray(obj) ? [...obj] : { ...obj };
|
|
2920
|
+
for (const key in masked) {
|
|
2921
|
+
const lowerKey = key.toLowerCase();
|
|
2922
|
+
if (lowerFields.some((field) => lowerKey.includes(field))) {
|
|
2923
|
+
masked[key] = "***MASKED***";
|
|
2924
|
+
} else if (typeof masked[key] === "object" && masked[key] !== null) {
|
|
2925
|
+
masked[key] = maskSensitiveData2(masked[key], sensitiveFields, seen);
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
return masked;
|
|
2929
|
+
}
|
|
2999
2930
|
function RequestLogger(config) {
|
|
3000
2931
|
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
3001
2932
|
const apiLogger = logger.child("api");
|
|
@@ -3021,8 +2952,6 @@ function RequestLogger(config) {
|
|
|
3021
2952
|
await next();
|
|
3022
2953
|
const duration = Date.now() - startTime;
|
|
3023
2954
|
const status = c.res.status;
|
|
3024
|
-
const logLevel = status >= 400 ? "warn" : "info";
|
|
3025
|
-
const isSlowRequest = duration >= cfg.slowRequestThreshold;
|
|
3026
2955
|
const logData = {
|
|
3027
2956
|
requestId,
|
|
3028
2957
|
method,
|
|
@@ -3030,9 +2959,25 @@ function RequestLogger(config) {
|
|
|
3030
2959
|
status,
|
|
3031
2960
|
duration
|
|
3032
2961
|
};
|
|
2962
|
+
const isSlowRequest = duration >= cfg.slowRequestThreshold;
|
|
3033
2963
|
if (isSlowRequest) {
|
|
3034
2964
|
logData.slow = true;
|
|
3035
2965
|
}
|
|
2966
|
+
if (status >= 400) {
|
|
2967
|
+
try {
|
|
2968
|
+
const responseBody = await c.res.clone().json();
|
|
2969
|
+
logData.response = responseBody;
|
|
2970
|
+
} catch {
|
|
2971
|
+
}
|
|
2972
|
+
if (["POST", "PUT", "PATCH"].includes(method)) {
|
|
2973
|
+
try {
|
|
2974
|
+
const requestBody = await c.req.json();
|
|
2975
|
+
logData.request = maskSensitiveData2(requestBody, cfg.sensitiveFields);
|
|
2976
|
+
} catch {
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
}
|
|
2980
|
+
const logLevel = status >= 500 ? "error" : status >= 400 ? "warn" : "info";
|
|
3036
2981
|
apiLogger[logLevel]("Request completed", logData);
|
|
3037
2982
|
} catch (error) {
|
|
3038
2983
|
const duration = Date.now() - startTime;
|
|
@@ -3230,11 +3175,11 @@ function registerHealthCheckEndpoint(app, config) {
|
|
|
3230
3175
|
}
|
|
3231
3176
|
}
|
|
3232
3177
|
async function executeBeforeRoutesHook(app, config) {
|
|
3233
|
-
if (!config?.beforeRoutes) {
|
|
3178
|
+
if (!config?.lifecycle?.beforeRoutes) {
|
|
3234
3179
|
return;
|
|
3235
3180
|
}
|
|
3236
3181
|
try {
|
|
3237
|
-
await config.beforeRoutes(app);
|
|
3182
|
+
await config.lifecycle.beforeRoutes(app);
|
|
3238
3183
|
} catch (error) {
|
|
3239
3184
|
serverLogger.error("beforeRoutes hook failed", error);
|
|
3240
3185
|
throw new Error("Server initialization failed in beforeRoutes hook");
|
|
@@ -3249,11 +3194,11 @@ async function loadAppRoutes(app, config) {
|
|
|
3249
3194
|
});
|
|
3250
3195
|
}
|
|
3251
3196
|
async function executeAfterRoutesHook(app, config) {
|
|
3252
|
-
if (!config?.afterRoutes) {
|
|
3197
|
+
if (!config?.lifecycle?.afterRoutes) {
|
|
3253
3198
|
return;
|
|
3254
3199
|
}
|
|
3255
3200
|
try {
|
|
3256
|
-
await config.afterRoutes(app);
|
|
3201
|
+
await config.lifecycle.afterRoutes(app);
|
|
3257
3202
|
} catch (error) {
|
|
3258
3203
|
serverLogger.error("afterRoutes hook failed", error);
|
|
3259
3204
|
throw new Error("Server initialization failed in afterRoutes hook");
|
|
@@ -3363,10 +3308,10 @@ async function startServer(config) {
|
|
|
3363
3308
|
port
|
|
3364
3309
|
});
|
|
3365
3310
|
logServerStarted(debug, host, port, finalConfig, timeouts);
|
|
3366
|
-
const shutdownServer = createShutdownHandler(server);
|
|
3311
|
+
const shutdownServer = createShutdownHandler(server, finalConfig);
|
|
3367
3312
|
const shutdown = createGracefulShutdown(shutdownServer, finalConfig);
|
|
3368
3313
|
registerShutdownHandlers(shutdown);
|
|
3369
|
-
|
|
3314
|
+
const serverInstance = {
|
|
3370
3315
|
server,
|
|
3371
3316
|
app,
|
|
3372
3317
|
config: finalConfig,
|
|
@@ -3375,10 +3320,19 @@ async function startServer(config) {
|
|
|
3375
3320
|
await shutdownServer();
|
|
3376
3321
|
}
|
|
3377
3322
|
};
|
|
3323
|
+
if (finalConfig.lifecycle?.afterStart) {
|
|
3324
|
+
serverLogger2.debug("Executing afterStart hook...");
|
|
3325
|
+
try {
|
|
3326
|
+
await finalConfig.lifecycle.afterStart(serverInstance);
|
|
3327
|
+
} catch (error) {
|
|
3328
|
+
serverLogger2.error("afterStart hook failed", error);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
return serverInstance;
|
|
3378
3332
|
} catch (error) {
|
|
3379
3333
|
const err = error;
|
|
3380
3334
|
serverLogger2.error("Server initialization failed", err);
|
|
3381
|
-
await cleanupOnFailure();
|
|
3335
|
+
await cleanupOnFailure(finalConfig);
|
|
3382
3336
|
throw error;
|
|
3383
3337
|
}
|
|
3384
3338
|
}
|
|
@@ -3416,10 +3370,38 @@ function logMiddlewareOrder(config) {
|
|
|
3416
3370
|
});
|
|
3417
3371
|
}
|
|
3418
3372
|
async function initializeInfrastructure(config) {
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3373
|
+
if (config.lifecycle?.beforeInfrastructure) {
|
|
3374
|
+
serverLogger2.debug("Executing beforeInfrastructure hook...");
|
|
3375
|
+
try {
|
|
3376
|
+
await config.lifecycle.beforeInfrastructure(config);
|
|
3377
|
+
} catch (error) {
|
|
3378
|
+
serverLogger2.error("beforeInfrastructure hook failed", error);
|
|
3379
|
+
throw new Error("Server initialization failed in beforeInfrastructure hook");
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
const shouldInitDatabase = config.infrastructure?.database !== false;
|
|
3383
|
+
if (shouldInitDatabase) {
|
|
3384
|
+
serverLogger2.debug("Initializing database...");
|
|
3385
|
+
await initDatabase(config.database);
|
|
3386
|
+
} else {
|
|
3387
|
+
serverLogger2.debug("Database initialization disabled");
|
|
3388
|
+
}
|
|
3389
|
+
const shouldInitRedis = config.infrastructure?.redis !== false;
|
|
3390
|
+
if (shouldInitRedis) {
|
|
3391
|
+
serverLogger2.debug("Initializing Redis...");
|
|
3392
|
+
await initRedis();
|
|
3393
|
+
} else {
|
|
3394
|
+
serverLogger2.debug("Redis initialization disabled");
|
|
3395
|
+
}
|
|
3396
|
+
if (config.lifecycle?.afterInfrastructure) {
|
|
3397
|
+
serverLogger2.debug("Executing afterInfrastructure hook...");
|
|
3398
|
+
try {
|
|
3399
|
+
await config.lifecycle.afterInfrastructure();
|
|
3400
|
+
} catch (error) {
|
|
3401
|
+
serverLogger2.error("afterInfrastructure hook failed", error);
|
|
3402
|
+
throw new Error("Server initialization failed in afterInfrastructure hook");
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3423
3405
|
}
|
|
3424
3406
|
function startHttpServer(app, host, port) {
|
|
3425
3407
|
serverLogger2.debug(`Starting server on ${host}:${port}...`);
|
|
@@ -3445,7 +3427,7 @@ function logServerStarted(debug, host, port, config, timeouts) {
|
|
|
3445
3427
|
config: startupConfig
|
|
3446
3428
|
});
|
|
3447
3429
|
}
|
|
3448
|
-
function createShutdownHandler(server) {
|
|
3430
|
+
function createShutdownHandler(server, config) {
|
|
3449
3431
|
return async () => {
|
|
3450
3432
|
serverLogger2.debug("Closing HTTP server...");
|
|
3451
3433
|
await new Promise((resolve) => {
|
|
@@ -3454,10 +3436,24 @@ function createShutdownHandler(server) {
|
|
|
3454
3436
|
resolve();
|
|
3455
3437
|
});
|
|
3456
3438
|
});
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3439
|
+
if (config.lifecycle?.beforeShutdown) {
|
|
3440
|
+
serverLogger2.debug("Executing beforeShutdown hook...");
|
|
3441
|
+
try {
|
|
3442
|
+
await config.lifecycle.beforeShutdown();
|
|
3443
|
+
} catch (error) {
|
|
3444
|
+
serverLogger2.error("beforeShutdown hook failed", error);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
const shouldCloseDatabase = config.infrastructure?.database !== false;
|
|
3448
|
+
const shouldCloseRedis = config.infrastructure?.redis !== false;
|
|
3449
|
+
if (shouldCloseDatabase) {
|
|
3450
|
+
serverLogger2.debug("Closing database connections...");
|
|
3451
|
+
await closeDatabase();
|
|
3452
|
+
}
|
|
3453
|
+
if (shouldCloseRedis) {
|
|
3454
|
+
serverLogger2.debug("Closing Redis connections...");
|
|
3455
|
+
await closeRedis();
|
|
3456
|
+
}
|
|
3461
3457
|
serverLogger2.info("Server shutdown completed");
|
|
3462
3458
|
};
|
|
3463
3459
|
}
|
|
@@ -3489,6 +3485,7 @@ function createGracefulShutdown(shutdownServer, config) {
|
|
|
3489
3485
|
};
|
|
3490
3486
|
}
|
|
3491
3487
|
function registerShutdownHandlers(shutdown) {
|
|
3488
|
+
process.setMaxListeners(15);
|
|
3492
3489
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
3493
3490
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
3494
3491
|
process.on("uncaughtException", (error) => {
|
|
@@ -3503,11 +3500,17 @@ function registerShutdownHandlers(shutdown) {
|
|
|
3503
3500
|
shutdown("UNHANDLED_REJECTION");
|
|
3504
3501
|
});
|
|
3505
3502
|
}
|
|
3506
|
-
async function cleanupOnFailure() {
|
|
3503
|
+
async function cleanupOnFailure(config) {
|
|
3507
3504
|
try {
|
|
3508
3505
|
serverLogger2.debug("Cleaning up after initialization failure...");
|
|
3509
|
-
|
|
3510
|
-
|
|
3506
|
+
const shouldCleanupDatabase = config.infrastructure?.database !== false;
|
|
3507
|
+
const shouldCleanupRedis = config.infrastructure?.redis !== false;
|
|
3508
|
+
if (shouldCleanupDatabase) {
|
|
3509
|
+
await closeDatabase();
|
|
3510
|
+
}
|
|
3511
|
+
if (shouldCleanupRedis) {
|
|
3512
|
+
await closeRedis();
|
|
3513
|
+
}
|
|
3511
3514
|
serverLogger2.debug("Cleanup completed");
|
|
3512
3515
|
} catch (cleanupError) {
|
|
3513
3516
|
serverLogger2.error("Cleanup failed", cleanupError);
|