@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/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/adapters/custom.ts
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 createAdapter(getAdapterType());
714
+ return new Logger({
715
+ level: getDefaultLogLevel(),
716
+ transports: initializeTransports()
717
+ });
839
718
  }
840
719
  var logger;
841
- var init_adapter_factory = __esm({
842
- "src/logger/adapter-factory.ts"() {
843
- init_pino();
844
- init_custom();
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
- init_adapter_factory();
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 init_factory = __esm({
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
- init_factory();
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
- 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
+ }
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
- init_factory();
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
- init_factory();
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;
@@ -3235,11 +3180,11 @@ function registerHealthCheckEndpoint(app, config) {
3235
3180
  }
3236
3181
  }
3237
3182
  async function executeBeforeRoutesHook(app, config) {
3238
- if (!config?.beforeRoutes) {
3183
+ if (!config?.lifecycle?.beforeRoutes) {
3239
3184
  return;
3240
3185
  }
3241
3186
  try {
3242
- await config.beforeRoutes(app);
3187
+ await config.lifecycle.beforeRoutes(app);
3243
3188
  } catch (error) {
3244
3189
  serverLogger.error("beforeRoutes hook failed", error);
3245
3190
  throw new Error("Server initialization failed in beforeRoutes hook");
@@ -3254,11 +3199,11 @@ async function loadAppRoutes(app, config) {
3254
3199
  });
3255
3200
  }
3256
3201
  async function executeAfterRoutesHook(app, config) {
3257
- if (!config?.afterRoutes) {
3202
+ if (!config?.lifecycle?.afterRoutes) {
3258
3203
  return;
3259
3204
  }
3260
3205
  try {
3261
- await config.afterRoutes(app);
3206
+ await config.lifecycle.afterRoutes(app);
3262
3207
  } catch (error) {
3263
3208
  serverLogger.error("afterRoutes hook failed", error);
3264
3209
  throw new Error("Server initialization failed in afterRoutes hook");
@@ -3368,10 +3313,10 @@ async function startServer(config) {
3368
3313
  port
3369
3314
  });
3370
3315
  logServerStarted(debug, host, port, finalConfig, timeouts);
3371
- const shutdownServer = createShutdownHandler(server);
3316
+ const shutdownServer = createShutdownHandler(server, finalConfig);
3372
3317
  const shutdown = createGracefulShutdown(shutdownServer, finalConfig);
3373
3318
  registerShutdownHandlers(shutdown);
3374
- return {
3319
+ const serverInstance = {
3375
3320
  server,
3376
3321
  app,
3377
3322
  config: finalConfig,
@@ -3380,10 +3325,19 @@ async function startServer(config) {
3380
3325
  await shutdownServer();
3381
3326
  }
3382
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;
3383
3337
  } catch (error) {
3384
3338
  const err = error;
3385
3339
  serverLogger2.error("Server initialization failed", err);
3386
- await cleanupOnFailure();
3340
+ await cleanupOnFailure(finalConfig);
3387
3341
  throw error;
3388
3342
  }
3389
3343
  }
@@ -3421,10 +3375,38 @@ function logMiddlewareOrder(config) {
3421
3375
  });
3422
3376
  }
3423
3377
  async function initializeInfrastructure(config) {
3424
- serverLogger2.debug("Initializing database...");
3425
- await initDatabase(config.database);
3426
- serverLogger2.debug("Initializing Redis...");
3427
- 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
+ }
3428
3410
  }
3429
3411
  function startHttpServer(app, host, port) {
3430
3412
  serverLogger2.debug(`Starting server on ${host}:${port}...`);
@@ -3450,7 +3432,7 @@ function logServerStarted(debug, host, port, config, timeouts) {
3450
3432
  config: startupConfig
3451
3433
  });
3452
3434
  }
3453
- function createShutdownHandler(server) {
3435
+ function createShutdownHandler(server, config) {
3454
3436
  return async () => {
3455
3437
  serverLogger2.debug("Closing HTTP server...");
3456
3438
  await new Promise((resolve) => {
@@ -3459,10 +3441,24 @@ function createShutdownHandler(server) {
3459
3441
  resolve();
3460
3442
  });
3461
3443
  });
3462
- serverLogger2.debug("Closing database connections...");
3463
- await closeDatabase();
3464
- serverLogger2.debug("Closing Redis connections...");
3465
- 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
+ }
3466
3462
  serverLogger2.info("Server shutdown completed");
3467
3463
  };
3468
3464
  }
@@ -3494,6 +3490,7 @@ function createGracefulShutdown(shutdownServer, config) {
3494
3490
  };
3495
3491
  }
3496
3492
  function registerShutdownHandlers(shutdown) {
3493
+ process.setMaxListeners(15);
3497
3494
  process.on("SIGTERM", () => shutdown("SIGTERM"));
3498
3495
  process.on("SIGINT", () => shutdown("SIGINT"));
3499
3496
  process.on("uncaughtException", (error) => {
@@ -3508,11 +3505,17 @@ function registerShutdownHandlers(shutdown) {
3508
3505
  shutdown("UNHANDLED_REJECTION");
3509
3506
  });
3510
3507
  }
3511
- async function cleanupOnFailure() {
3508
+ async function cleanupOnFailure(config) {
3512
3509
  try {
3513
3510
  serverLogger2.debug("Cleaning up after initialization failure...");
3514
- await closeDatabase();
3515
- 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
+ }
3516
3519
  serverLogger2.debug("Cleanup completed");
3517
3520
  } catch (cleanupError) {
3518
3521
  serverLogger2.error("Cleanup failed", cleanupError);