@spfn/core 0.1.0-alpha.82 → 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/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,76 +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
- const isDevelopment = process.env.NODE_ENV === "development";
34
- const transport = isDevelopment ? {
35
- target: "pino-pretty",
36
- options: {
37
- colorize: true,
38
- translateTime: "HH:MM:ss.l",
39
- ignore: "pid,hostname",
40
- singleLine: false,
41
- messageFormat: "{module} {msg}",
42
- errorLikeObjectKeys: ["err", "error"]
43
- }
44
- } : void 0;
45
- try {
46
- this.logger = pino({
47
- level: config.level,
48
- // 기본 필드
49
- base: config.module ? { module: config.module } : void 0,
50
- // Transport (pretty print in development if available)
51
- transport
52
- });
53
- } catch (error) {
54
- this.logger = pino({
55
- level: config.level,
56
- base: config.module ? { module: config.module } : void 0
57
- });
58
- }
59
- }
60
- child(module) {
61
- const childLogger = new _PinoAdapter({ level: this.logger.level, module });
62
- childLogger.logger = this.logger.child({ module });
63
- return childLogger;
64
- }
65
- debug(message, context) {
66
- this.logger.debug(context || {}, message);
67
- }
68
- info(message, context) {
69
- this.logger.info(context || {}, message);
70
- }
71
- warn(message, errorOrContext, context) {
72
- if (errorOrContext instanceof Error) {
73
- this.logger.warn({ err: errorOrContext, ...context }, message);
74
- } else {
75
- this.logger.warn(errorOrContext || {}, message);
76
- }
77
- }
78
- error(message, errorOrContext, context) {
79
- if (errorOrContext instanceof Error) {
80
- this.logger.error({ err: errorOrContext, ...context }, message);
81
- } else {
82
- this.logger.error(errorOrContext || {}, message);
83
- }
84
- }
85
- fatal(message, errorOrContext, context) {
86
- if (errorOrContext instanceof Error) {
87
- this.logger.fatal({ err: errorOrContext, ...context }, message);
88
- } else {
89
- this.logger.fatal(errorOrContext || {}, message);
90
- }
91
- }
92
- async close() {
93
- }
94
- };
95
- }
96
- });
97
26
 
98
27
  // src/logger/types.ts
99
28
  var LOG_LEVEL_PRIORITY;
@@ -769,7 +698,7 @@ var init_config = __esm({
769
698
  }
770
699
  });
771
700
 
772
- // src/logger/adapters/custom.ts
701
+ // src/logger/factory.ts
773
702
  function initializeTransports() {
774
703
  const transports = [];
775
704
  const consoleConfig = getConsoleConfig();
@@ -780,89 +709,19 @@ function initializeTransports() {
780
709
  }
781
710
  return transports;
782
711
  }
783
- var CustomAdapter;
784
- var init_custom = __esm({
785
- "src/logger/adapters/custom.ts"() {
786
- init_logger();
787
- init_console();
788
- init_file();
789
- init_config();
790
- CustomAdapter = class _CustomAdapter {
791
- logger;
792
- constructor(config) {
793
- this.logger = new Logger({
794
- level: config.level,
795
- module: config.module,
796
- transports: initializeTransports()
797
- });
798
- }
799
- child(module) {
800
- const adapter = new _CustomAdapter({ level: this.logger.level, module });
801
- adapter.logger = this.logger.child(module);
802
- return adapter;
803
- }
804
- debug(message, context) {
805
- this.logger.debug(message, context);
806
- }
807
- info(message, context) {
808
- this.logger.info(message, context);
809
- }
810
- warn(message, errorOrContext, context) {
811
- if (errorOrContext instanceof Error) {
812
- this.logger.warn(message, errorOrContext, context);
813
- } else {
814
- this.logger.warn(message, errorOrContext);
815
- }
816
- }
817
- error(message, errorOrContext, context) {
818
- if (errorOrContext instanceof Error) {
819
- this.logger.error(message, errorOrContext, context);
820
- } else {
821
- this.logger.error(message, errorOrContext);
822
- }
823
- }
824
- fatal(message, errorOrContext, context) {
825
- if (errorOrContext instanceof Error) {
826
- this.logger.fatal(message, errorOrContext, context);
827
- } else {
828
- this.logger.fatal(message, errorOrContext);
829
- }
830
- }
831
- async close() {
832
- await this.logger.close();
833
- }
834
- };
835
- }
836
- });
837
-
838
- // src/logger/adapter-factory.ts
839
- function createAdapter(type) {
840
- const level = getDefaultLogLevel();
841
- switch (type) {
842
- case "pino":
843
- return new PinoAdapter({ level });
844
- case "custom":
845
- return new CustomAdapter({ level });
846
- default:
847
- return new PinoAdapter({ level });
848
- }
849
- }
850
- function getAdapterType() {
851
- const adapterEnv = process.env.LOGGER_ADAPTER;
852
- if (adapterEnv === "custom" || adapterEnv === "pino") {
853
- return adapterEnv;
854
- }
855
- return "pino";
856
- }
857
712
  function initializeLogger() {
858
713
  validateConfig();
859
- return createAdapter(getAdapterType());
714
+ return new Logger({
715
+ level: getDefaultLogLevel(),
716
+ transports: initializeTransports()
717
+ });
860
718
  }
861
719
  var logger;
862
- var init_adapter_factory = __esm({
863
- "src/logger/adapter-factory.ts"() {
864
- init_pino();
865
- init_custom();
720
+ var init_factory = __esm({
721
+ "src/logger/factory.ts"() {
722
+ init_logger();
723
+ init_console();
724
+ init_file();
866
725
  init_config();
867
726
  logger = initializeLogger();
868
727
  }
@@ -871,7 +730,8 @@ var init_adapter_factory = __esm({
871
730
  // src/logger/index.ts
872
731
  var init_logger2 = __esm({
873
732
  "src/logger/index.ts"() {
874
- init_adapter_factory();
733
+ init_factory();
734
+ init_logger();
875
735
  }
876
736
  });
877
737
 
@@ -1592,7 +1452,7 @@ async function createDatabaseFromEnv(options) {
1592
1452
  }
1593
1453
  }
1594
1454
  var dbLogger2;
1595
- var init_factory = __esm({
1455
+ var init_factory2 = __esm({
1596
1456
  "src/db/manager/factory.ts"() {
1597
1457
  init_logger2();
1598
1458
  init_env();
@@ -1708,17 +1568,51 @@ var dbLogger3;
1708
1568
  var init_health_check = __esm({
1709
1569
  "src/db/manager/health-check.ts"() {
1710
1570
  init_logger2();
1711
- init_factory();
1571
+ init_factory2();
1712
1572
  init_global_state();
1713
1573
  dbLogger3 = logger.child("database");
1714
1574
  }
1715
1575
  });
1716
1576
 
1717
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
+ }
1718
1604
  function getDatabase(type) {
1719
1605
  const writeInst = getWriteInstance();
1720
1606
  const readInst = getReadInstance();
1721
- dbLogger4.debug(`getDatabase() called with type=${type}, writeInstance=${!!writeInst}, readInstance=${!!readInst}`);
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
+ }
1722
1616
  if (type === "read") {
1723
1617
  return readInst ?? writeInst;
1724
1618
  }
@@ -1823,7 +1717,7 @@ var dbLogger4;
1823
1717
  var init_manager = __esm({
1824
1718
  "src/db/manager/manager.ts"() {
1825
1719
  init_logger2();
1826
- init_factory();
1720
+ init_factory2();
1827
1721
  init_config3();
1828
1722
  init_global_state();
1829
1723
  init_health_check();
@@ -1834,7 +1728,7 @@ var init_manager = __esm({
1834
1728
  // src/db/manager/index.ts
1835
1729
  var init_manager2 = __esm({
1836
1730
  "src/db/manager/index.ts"() {
1837
- init_factory();
1731
+ init_factory2();
1838
1732
  init_manager();
1839
1733
  init_connection();
1840
1734
  }
@@ -3017,6 +2911,22 @@ function generateRequestId() {
3017
2911
  const randomPart = randomBytes(6).toString("hex");
3018
2912
  return `req_${timestamp2}_${randomPart}`;
3019
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
+ }
3020
2930
  function RequestLogger(config) {
3021
2931
  const cfg = { ...DEFAULT_CONFIG, ...config };
3022
2932
  const apiLogger = logger.child("api");
@@ -3042,8 +2952,6 @@ function RequestLogger(config) {
3042
2952
  await next();
3043
2953
  const duration = Date.now() - startTime;
3044
2954
  const status = c.res.status;
3045
- const logLevel = status >= 400 ? "warn" : "info";
3046
- const isSlowRequest = duration >= cfg.slowRequestThreshold;
3047
2955
  const logData = {
3048
2956
  requestId,
3049
2957
  method,
@@ -3051,9 +2959,25 @@ function RequestLogger(config) {
3051
2959
  status,
3052
2960
  duration
3053
2961
  };
2962
+ const isSlowRequest = duration >= cfg.slowRequestThreshold;
3054
2963
  if (isSlowRequest) {
3055
2964
  logData.slow = true;
3056
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";
3057
2981
  apiLogger[logLevel]("Request completed", logData);
3058
2982
  } catch (error) {
3059
2983
  const duration = Date.now() - startTime;
@@ -3256,11 +3180,11 @@ function registerHealthCheckEndpoint(app, config) {
3256
3180
  }
3257
3181
  }
3258
3182
  async function executeBeforeRoutesHook(app, config) {
3259
- if (!config?.beforeRoutes) {
3183
+ if (!config?.lifecycle?.beforeRoutes) {
3260
3184
  return;
3261
3185
  }
3262
3186
  try {
3263
- await config.beforeRoutes(app);
3187
+ await config.lifecycle.beforeRoutes(app);
3264
3188
  } catch (error) {
3265
3189
  serverLogger.error("beforeRoutes hook failed", error);
3266
3190
  throw new Error("Server initialization failed in beforeRoutes hook");
@@ -3275,11 +3199,11 @@ async function loadAppRoutes(app, config) {
3275
3199
  });
3276
3200
  }
3277
3201
  async function executeAfterRoutesHook(app, config) {
3278
- if (!config?.afterRoutes) {
3202
+ if (!config?.lifecycle?.afterRoutes) {
3279
3203
  return;
3280
3204
  }
3281
3205
  try {
3282
- await config.afterRoutes(app);
3206
+ await config.lifecycle.afterRoutes(app);
3283
3207
  } catch (error) {
3284
3208
  serverLogger.error("afterRoutes hook failed", error);
3285
3209
  throw new Error("Server initialization failed in afterRoutes hook");
@@ -3389,10 +3313,10 @@ async function startServer(config) {
3389
3313
  port
3390
3314
  });
3391
3315
  logServerStarted(debug, host, port, finalConfig, timeouts);
3392
- const shutdownServer = createShutdownHandler(server);
3316
+ const shutdownServer = createShutdownHandler(server, finalConfig);
3393
3317
  const shutdown = createGracefulShutdown(shutdownServer, finalConfig);
3394
3318
  registerShutdownHandlers(shutdown);
3395
- return {
3319
+ const serverInstance = {
3396
3320
  server,
3397
3321
  app,
3398
3322
  config: finalConfig,
@@ -3401,10 +3325,19 @@ async function startServer(config) {
3401
3325
  await shutdownServer();
3402
3326
  }
3403
3327
  };
3328
+ if (finalConfig.lifecycle?.afterStart) {
3329
+ serverLogger2.debug("Executing afterStart hook...");
3330
+ try {
3331
+ await finalConfig.lifecycle.afterStart(serverInstance);
3332
+ } catch (error) {
3333
+ serverLogger2.error("afterStart hook failed", error);
3334
+ }
3335
+ }
3336
+ return serverInstance;
3404
3337
  } catch (error) {
3405
3338
  const err = error;
3406
3339
  serverLogger2.error("Server initialization failed", err);
3407
- await cleanupOnFailure();
3340
+ await cleanupOnFailure(finalConfig);
3408
3341
  throw error;
3409
3342
  }
3410
3343
  }
@@ -3442,10 +3375,38 @@ function logMiddlewareOrder(config) {
3442
3375
  });
3443
3376
  }
3444
3377
  async function initializeInfrastructure(config) {
3445
- serverLogger2.debug("Initializing database...");
3446
- await initDatabase(config.database);
3447
- serverLogger2.debug("Initializing Redis...");
3448
- await initRedis();
3378
+ if (config.lifecycle?.beforeInfrastructure) {
3379
+ serverLogger2.debug("Executing beforeInfrastructure hook...");
3380
+ try {
3381
+ await config.lifecycle.beforeInfrastructure(config);
3382
+ } catch (error) {
3383
+ serverLogger2.error("beforeInfrastructure hook failed", error);
3384
+ throw new Error("Server initialization failed in beforeInfrastructure hook");
3385
+ }
3386
+ }
3387
+ const shouldInitDatabase = config.infrastructure?.database !== false;
3388
+ if (shouldInitDatabase) {
3389
+ serverLogger2.debug("Initializing database...");
3390
+ await initDatabase(config.database);
3391
+ } else {
3392
+ serverLogger2.debug("Database initialization disabled");
3393
+ }
3394
+ const shouldInitRedis = config.infrastructure?.redis !== false;
3395
+ if (shouldInitRedis) {
3396
+ serverLogger2.debug("Initializing Redis...");
3397
+ await initRedis();
3398
+ } else {
3399
+ serverLogger2.debug("Redis initialization disabled");
3400
+ }
3401
+ if (config.lifecycle?.afterInfrastructure) {
3402
+ serverLogger2.debug("Executing afterInfrastructure hook...");
3403
+ try {
3404
+ await config.lifecycle.afterInfrastructure();
3405
+ } catch (error) {
3406
+ serverLogger2.error("afterInfrastructure hook failed", error);
3407
+ throw new Error("Server initialization failed in afterInfrastructure hook");
3408
+ }
3409
+ }
3449
3410
  }
3450
3411
  function startHttpServer(app, host, port) {
3451
3412
  serverLogger2.debug(`Starting server on ${host}:${port}...`);
@@ -3471,7 +3432,7 @@ function logServerStarted(debug, host, port, config, timeouts) {
3471
3432
  config: startupConfig
3472
3433
  });
3473
3434
  }
3474
- function createShutdownHandler(server) {
3435
+ function createShutdownHandler(server, config) {
3475
3436
  return async () => {
3476
3437
  serverLogger2.debug("Closing HTTP server...");
3477
3438
  await new Promise((resolve) => {
@@ -3480,10 +3441,24 @@ function createShutdownHandler(server) {
3480
3441
  resolve();
3481
3442
  });
3482
3443
  });
3483
- serverLogger2.debug("Closing database connections...");
3484
- await closeDatabase();
3485
- serverLogger2.debug("Closing Redis connections...");
3486
- await closeRedis();
3444
+ if (config.lifecycle?.beforeShutdown) {
3445
+ serverLogger2.debug("Executing beforeShutdown hook...");
3446
+ try {
3447
+ await config.lifecycle.beforeShutdown();
3448
+ } catch (error) {
3449
+ serverLogger2.error("beforeShutdown hook failed", error);
3450
+ }
3451
+ }
3452
+ const shouldCloseDatabase = config.infrastructure?.database !== false;
3453
+ const shouldCloseRedis = config.infrastructure?.redis !== false;
3454
+ if (shouldCloseDatabase) {
3455
+ serverLogger2.debug("Closing database connections...");
3456
+ await closeDatabase();
3457
+ }
3458
+ if (shouldCloseRedis) {
3459
+ serverLogger2.debug("Closing Redis connections...");
3460
+ await closeRedis();
3461
+ }
3487
3462
  serverLogger2.info("Server shutdown completed");
3488
3463
  };
3489
3464
  }
@@ -3515,6 +3490,7 @@ function createGracefulShutdown(shutdownServer, config) {
3515
3490
  };
3516
3491
  }
3517
3492
  function registerShutdownHandlers(shutdown) {
3493
+ process.setMaxListeners(15);
3518
3494
  process.on("SIGTERM", () => shutdown("SIGTERM"));
3519
3495
  process.on("SIGINT", () => shutdown("SIGINT"));
3520
3496
  process.on("uncaughtException", (error) => {
@@ -3529,11 +3505,17 @@ function registerShutdownHandlers(shutdown) {
3529
3505
  shutdown("UNHANDLED_REJECTION");
3530
3506
  });
3531
3507
  }
3532
- async function cleanupOnFailure() {
3508
+ async function cleanupOnFailure(config) {
3533
3509
  try {
3534
3510
  serverLogger2.debug("Cleaning up after initialization failure...");
3535
- await closeDatabase();
3536
- await closeRedis();
3511
+ const shouldCleanupDatabase = config.infrastructure?.database !== false;
3512
+ const shouldCleanupRedis = config.infrastructure?.redis !== false;
3513
+ if (shouldCleanupDatabase) {
3514
+ await closeDatabase();
3515
+ }
3516
+ if (shouldCleanupRedis) {
3517
+ await closeRedis();
3518
+ }
3537
3519
  serverLogger2.debug("Cleanup completed");
3538
3520
  } catch (cleanupError) {
3539
3521
  serverLogger2.error("Cleanup failed", cleanupError);