@spfn/core 0.1.0-alpha.21 → 0.1.0-alpha.22

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.
@@ -96,7 +96,7 @@ var init_pino = __esm({
96
96
  "src/logger/adapters/pino.ts"() {
97
97
  PinoAdapter = class _PinoAdapter {
98
98
  logger;
99
- constructor(config2) {
99
+ constructor(config) {
100
100
  const isProduction = process.env.NODE_ENV === "production";
101
101
  const isDevelopment = process.env.NODE_ENV === "development";
102
102
  const fileLoggingEnabled = process.env.LOGGER_FILE_ENABLED === "true";
@@ -129,11 +129,11 @@ var init_pino = __esm({
129
129
  });
130
130
  }
131
131
  this.logger = pino({
132
- level: config2.level,
132
+ level: config.level,
133
133
  // Transport 설정 (targets가 있으면 사용, 없으면 기본 stdout)
134
134
  transport: targets.length > 0 ? { targets } : void 0,
135
135
  // 기본 필드
136
- base: config2.module ? { module: config2.module } : void 0
136
+ base: config.module ? { module: config.module } : void 0
137
137
  });
138
138
  }
139
139
  child(module) {
@@ -181,9 +181,9 @@ var init_logger = __esm({
181
181
  Logger = class _Logger {
182
182
  config;
183
183
  module;
184
- constructor(config2) {
185
- this.config = config2;
186
- this.module = config2.module;
184
+ constructor(config) {
185
+ this.config = config;
186
+ this.module = config.module;
187
187
  }
188
188
  /**
189
189
  * Get current log level
@@ -416,10 +416,10 @@ var init_console = __esm({
416
416
  level;
417
417
  enabled;
418
418
  colorize;
419
- constructor(config2) {
420
- this.level = config2.level;
421
- this.enabled = config2.enabled;
422
- this.colorize = config2.colorize ?? true;
419
+ constructor(config) {
420
+ this.level = config.level;
421
+ this.enabled = config.enabled;
422
+ this.colorize = config.colorize ?? true;
423
423
  }
424
424
  async log(metadata) {
425
425
  if (!this.enabled) {
@@ -450,10 +450,10 @@ var init_file = __esm({
450
450
  logDir;
451
451
  currentStream = null;
452
452
  currentFilename = null;
453
- constructor(config2) {
454
- this.level = config2.level;
455
- this.enabled = config2.enabled;
456
- this.logDir = config2.logDir;
453
+ constructor(config) {
454
+ this.level = config.level;
455
+ this.enabled = config.enabled;
456
+ this.logDir = config.logDir;
457
457
  if (!existsSync(this.logDir)) {
458
458
  mkdirSync(this.logDir, { recursive: true });
459
459
  }
@@ -598,10 +598,10 @@ var init_custom = __esm({
598
598
  init_config();
599
599
  CustomAdapter = class _CustomAdapter {
600
600
  logger;
601
- constructor(config2) {
601
+ constructor(config) {
602
602
  this.logger = new Logger({
603
- level: config2.level,
604
- module: config2.module,
603
+ level: config.level,
604
+ module: config.module,
605
605
  transports: initializeTransports()
606
606
  });
607
607
  }
@@ -1096,14 +1096,225 @@ var init_config2 = __esm({
1096
1096
  "src/db/manager/config.ts"() {
1097
1097
  }
1098
1098
  });
1099
+
1100
+ // src/env/config.ts
1101
+ var ENV_FILE_PRIORITY, TEST_ONLY_FILES;
1102
+ var init_config3 = __esm({
1103
+ "src/env/config.ts"() {
1104
+ ENV_FILE_PRIORITY = [
1105
+ ".env",
1106
+ // Base configuration (lowest priority)
1107
+ ".env.{NODE_ENV}",
1108
+ // Environment-specific
1109
+ ".env.local",
1110
+ // Local overrides
1111
+ ".env.{NODE_ENV}.local"
1112
+ // Local environment-specific (highest priority)
1113
+ ];
1114
+ TEST_ONLY_FILES = [
1115
+ ".env.test",
1116
+ ".env.test.local"
1117
+ ];
1118
+ }
1119
+ });
1120
+ function buildFileList(basePath, nodeEnv) {
1121
+ const files = [];
1122
+ for (const pattern of ENV_FILE_PRIORITY) {
1123
+ const fileName = pattern.replace("{NODE_ENV}", nodeEnv);
1124
+ if (nodeEnv !== "test" && TEST_ONLY_FILES.includes(fileName)) {
1125
+ continue;
1126
+ }
1127
+ files.push(join(basePath, fileName));
1128
+ }
1129
+ return files;
1130
+ }
1131
+ function loadSingleFile(filePath, debug) {
1132
+ if (!existsSync(filePath)) {
1133
+ if (debug) {
1134
+ envLogger.debug("Environment file not found (optional)", {
1135
+ path: filePath
1136
+ });
1137
+ }
1138
+ return { success: false, parsed: {}, error: "File not found" };
1139
+ }
1140
+ try {
1141
+ const result = config({ path: filePath });
1142
+ if (result.error) {
1143
+ envLogger.warn("Failed to parse environment file", {
1144
+ path: filePath,
1145
+ error: result.error.message
1146
+ });
1147
+ return {
1148
+ success: false,
1149
+ parsed: {},
1150
+ error: result.error.message
1151
+ };
1152
+ }
1153
+ const parsed = result.parsed || {};
1154
+ if (debug) {
1155
+ envLogger.debug("Environment file loaded successfully", {
1156
+ path: filePath,
1157
+ variables: Object.keys(parsed),
1158
+ count: Object.keys(parsed).length
1159
+ });
1160
+ }
1161
+ return { success: true, parsed };
1162
+ } catch (error) {
1163
+ const message = error instanceof Error ? error.message : "Unknown error";
1164
+ envLogger.error("Error loading environment file", {
1165
+ path: filePath,
1166
+ error: message
1167
+ });
1168
+ return { success: false, parsed: {}, error: message };
1169
+ }
1170
+ }
1171
+ function validateRequiredVars(required, debug) {
1172
+ const missing = [];
1173
+ for (const varName of required) {
1174
+ if (!process.env[varName]) {
1175
+ missing.push(varName);
1176
+ }
1177
+ }
1178
+ if (missing.length > 0) {
1179
+ const error = `Required environment variables missing: ${missing.join(", ")}`;
1180
+ envLogger.error("Environment validation failed", {
1181
+ missing,
1182
+ required
1183
+ });
1184
+ throw new Error(error);
1185
+ }
1186
+ if (debug) {
1187
+ envLogger.debug("Required environment variables validated", {
1188
+ required,
1189
+ allPresent: true
1190
+ });
1191
+ }
1192
+ }
1193
+ function loadEnvironment(options = {}) {
1194
+ const {
1195
+ basePath = process.cwd(),
1196
+ customPaths = [],
1197
+ debug = false,
1198
+ nodeEnv = process.env.NODE_ENV || "development",
1199
+ required = [],
1200
+ useCache = true
1201
+ } = options;
1202
+ if (useCache && environmentLoaded && cachedLoadResult) {
1203
+ if (debug) {
1204
+ envLogger.debug("Returning cached environment", {
1205
+ loaded: cachedLoadResult.loaded.length,
1206
+ variables: Object.keys(cachedLoadResult.parsed).length
1207
+ });
1208
+ }
1209
+ return cachedLoadResult;
1210
+ }
1211
+ if (debug) {
1212
+ envLogger.debug("Loading environment variables", {
1213
+ basePath,
1214
+ nodeEnv,
1215
+ customPaths,
1216
+ required
1217
+ });
1218
+ }
1219
+ const result = {
1220
+ success: true,
1221
+ loaded: [],
1222
+ failed: [],
1223
+ parsed: {},
1224
+ warnings: []
1225
+ };
1226
+ const standardFiles = buildFileList(basePath, nodeEnv);
1227
+ const allFiles = [...standardFiles, ...customPaths];
1228
+ if (debug) {
1229
+ envLogger.debug("Environment files to load", {
1230
+ standardFiles,
1231
+ customPaths,
1232
+ total: allFiles.length
1233
+ });
1234
+ }
1235
+ const reversedFiles = [...allFiles].reverse();
1236
+ for (const filePath of reversedFiles) {
1237
+ const fileResult = loadSingleFile(filePath, debug);
1238
+ if (fileResult.success) {
1239
+ result.loaded.push(filePath);
1240
+ Object.assign(result.parsed, fileResult.parsed);
1241
+ } else if (fileResult.error) {
1242
+ result.failed.push({
1243
+ path: filePath,
1244
+ reason: fileResult.error
1245
+ });
1246
+ }
1247
+ }
1248
+ if (debug || result.loaded.length > 0) {
1249
+ envLogger.info("Environment loading complete", {
1250
+ loaded: result.loaded.length,
1251
+ failed: result.failed.length,
1252
+ variables: Object.keys(result.parsed).length,
1253
+ files: result.loaded
1254
+ });
1255
+ }
1256
+ if (required.length > 0) {
1257
+ try {
1258
+ validateRequiredVars(required, debug);
1259
+ } catch (error) {
1260
+ result.success = false;
1261
+ result.errors = [
1262
+ error instanceof Error ? error.message : "Validation failed"
1263
+ ];
1264
+ throw error;
1265
+ }
1266
+ }
1267
+ environmentLoaded = true;
1268
+ cachedLoadResult = result;
1269
+ return result;
1270
+ }
1271
+ var envLogger, environmentLoaded, cachedLoadResult;
1272
+ var init_loader = __esm({
1273
+ "src/env/loader.ts"() {
1274
+ init_logger2();
1275
+ init_config3();
1276
+ envLogger = logger.child("environment");
1277
+ environmentLoaded = false;
1278
+ }
1279
+ });
1280
+
1281
+ // src/env/validator.ts
1282
+ var init_validator = __esm({
1283
+ "src/env/validator.ts"() {
1284
+ }
1285
+ });
1286
+
1287
+ // src/env/index.ts
1288
+ var init_env = __esm({
1289
+ "src/env/index.ts"() {
1290
+ init_loader();
1291
+ init_config3();
1292
+ init_validator();
1293
+ }
1294
+ });
1099
1295
  function hasDatabaseConfig() {
1100
1296
  return !!(process.env.DATABASE_URL || process.env.DATABASE_WRITE_URL || process.env.DATABASE_READ_URL);
1101
1297
  }
1102
1298
  async function createDatabaseFromEnv(options) {
1103
1299
  if (!hasDatabaseConfig()) {
1104
- config({ path: ".env.local" });
1300
+ dbLogger2.debug("No DATABASE_URL found, loading environment variables");
1301
+ const result = loadEnvironment({
1302
+ debug: true
1303
+ });
1304
+ dbLogger2.debug("Environment variables loaded", {
1305
+ success: result.success,
1306
+ loaded: result.loaded.length,
1307
+ hasDatabaseUrl: !!process.env.DATABASE_URL,
1308
+ hasWriteUrl: !!process.env.DATABASE_WRITE_URL,
1309
+ hasReadUrl: !!process.env.DATABASE_READ_URL
1310
+ });
1105
1311
  }
1106
1312
  if (!hasDatabaseConfig()) {
1313
+ dbLogger2.warn("No database configuration found", {
1314
+ cwd: process.cwd(),
1315
+ nodeEnv: process.env.NODE_ENV,
1316
+ checkedVars: ["DATABASE_URL", "DATABASE_WRITE_URL", "DATABASE_READ_URL"]
1317
+ });
1107
1318
  return { write: void 0, read: void 0 };
1108
1319
  }
1109
1320
  try {
@@ -1193,6 +1404,7 @@ var init_factory = __esm({
1193
1404
  init_connection();
1194
1405
  init_config2();
1195
1406
  init_logger2();
1407
+ init_env();
1196
1408
  dbLogger2 = logger.child("database");
1197
1409
  }
1198
1410
  });
@@ -1209,28 +1421,28 @@ function setDatabase(write, read) {
1209
1421
  readInstance2 = read ?? write;
1210
1422
  }
1211
1423
  function getHealthCheckConfig(options) {
1212
- const parseBoolean = (value, defaultValue) => {
1424
+ const parseBoolean2 = (value, defaultValue) => {
1213
1425
  if (value === void 0) return defaultValue;
1214
1426
  return value.toLowerCase() === "true";
1215
1427
  };
1216
1428
  return {
1217
- enabled: options?.enabled ?? parseBoolean(process.env.DB_HEALTH_CHECK_ENABLED, true),
1429
+ enabled: options?.enabled ?? parseBoolean2(process.env.DB_HEALTH_CHECK_ENABLED, true),
1218
1430
  interval: options?.interval ?? (parseInt(process.env.DB_HEALTH_CHECK_INTERVAL || "", 10) || 6e4),
1219
- reconnect: options?.reconnect ?? parseBoolean(process.env.DB_HEALTH_CHECK_RECONNECT, true),
1431
+ reconnect: options?.reconnect ?? parseBoolean2(process.env.DB_HEALTH_CHECK_RECONNECT, true),
1220
1432
  maxRetries: options?.maxRetries ?? (parseInt(process.env.DB_HEALTH_CHECK_MAX_RETRIES || "", 10) || 3),
1221
1433
  retryInterval: options?.retryInterval ?? (parseInt(process.env.DB_HEALTH_CHECK_RETRY_INTERVAL || "", 10) || 5e3)
1222
1434
  };
1223
1435
  }
1224
1436
  function getMonitoringConfig(options) {
1225
1437
  const isDevelopment = process.env.NODE_ENV !== "production";
1226
- const parseBoolean = (value, defaultValue) => {
1438
+ const parseBoolean2 = (value, defaultValue) => {
1227
1439
  if (value === void 0) return defaultValue;
1228
1440
  return value.toLowerCase() === "true";
1229
1441
  };
1230
1442
  return {
1231
- enabled: options?.enabled ?? parseBoolean(process.env.DB_MONITORING_ENABLED, isDevelopment),
1443
+ enabled: options?.enabled ?? parseBoolean2(process.env.DB_MONITORING_ENABLED, isDevelopment),
1232
1444
  slowThreshold: options?.slowThreshold ?? (parseInt(process.env.DB_MONITORING_SLOW_THRESHOLD || "", 10) || 1e3),
1233
- logQueries: options?.logQueries ?? parseBoolean(process.env.DB_MONITORING_LOG_QUERIES, false)
1445
+ logQueries: options?.logQueries ?? parseBoolean2(process.env.DB_MONITORING_LOG_QUERIES, false)
1234
1446
  };
1235
1447
  }
1236
1448
  async function initDatabase(options) {
@@ -1316,14 +1528,14 @@ function getDatabaseInfo() {
1316
1528
  isReplica: !!(readInstance2 && readInstance2 !== writeInstance2)
1317
1529
  };
1318
1530
  }
1319
- function startHealthCheck(config2) {
1531
+ function startHealthCheck(config) {
1320
1532
  if (healthCheckInterval) {
1321
1533
  dbLogger3.debug("Health check already running");
1322
1534
  return;
1323
1535
  }
1324
1536
  dbLogger3.info("Starting database health check", {
1325
- interval: `${config2.interval}ms`,
1326
- reconnect: config2.reconnect
1537
+ interval: `${config.interval}ms`,
1538
+ reconnect: config.reconnect
1327
1539
  });
1328
1540
  healthCheckInterval = setInterval(async () => {
1329
1541
  try {
@@ -1339,22 +1551,22 @@ function startHealthCheck(config2) {
1339
1551
  } catch (error) {
1340
1552
  const message = error instanceof Error ? error.message : "Unknown error";
1341
1553
  dbLogger3.error("Database health check failed", { error: message });
1342
- if (config2.reconnect) {
1343
- await attemptReconnection(config2);
1554
+ if (config.reconnect) {
1555
+ await attemptReconnection(config);
1344
1556
  }
1345
1557
  }
1346
- }, config2.interval);
1558
+ }, config.interval);
1347
1559
  }
1348
- async function attemptReconnection(config2) {
1560
+ async function attemptReconnection(config) {
1349
1561
  dbLogger3.warn("Attempting database reconnection", {
1350
- maxRetries: config2.maxRetries,
1351
- retryInterval: `${config2.retryInterval}ms`
1562
+ maxRetries: config.maxRetries,
1563
+ retryInterval: `${config.retryInterval}ms`
1352
1564
  });
1353
- for (let attempt = 1; attempt <= config2.maxRetries; attempt++) {
1565
+ for (let attempt = 1; attempt <= config.maxRetries; attempt++) {
1354
1566
  try {
1355
- dbLogger3.debug(`Reconnection attempt ${attempt}/${config2.maxRetries}`);
1567
+ dbLogger3.debug(`Reconnection attempt ${attempt}/${config.maxRetries}`);
1356
1568
  await closeDatabase();
1357
- await new Promise((resolve) => setTimeout(resolve, config2.retryInterval));
1569
+ await new Promise((resolve) => setTimeout(resolve, config.retryInterval));
1358
1570
  const result = await createDatabaseFromEnv();
1359
1571
  if (result.write) {
1360
1572
  await result.write.execute("SELECT 1");
@@ -1370,9 +1582,9 @@ async function attemptReconnection(config2) {
1370
1582
  dbLogger3.error(`Reconnection attempt ${attempt} failed`, {
1371
1583
  error: message,
1372
1584
  attempt,
1373
- maxRetries: config2.maxRetries
1585
+ maxRetries: config.maxRetries
1374
1586
  });
1375
- if (attempt === config2.maxRetries) {
1587
+ if (attempt === config.maxRetries) {
1376
1588
  dbLogger3.error("Max reconnection attempts reached, giving up");
1377
1589
  }
1378
1590
  }
@@ -1930,21 +2142,21 @@ var init_repository = __esm({
1930
2142
  * @returns Result of the operation
1931
2143
  */
1932
2144
  async executeWithMonitoring(operation, fn) {
1933
- const config2 = getDatabaseMonitoringConfig();
1934
- if (!config2?.enabled) {
2145
+ const config = getDatabaseMonitoringConfig();
2146
+ if (!config?.enabled) {
1935
2147
  return fn();
1936
2148
  }
1937
2149
  const startTime = performance.now();
1938
2150
  try {
1939
2151
  const result = await fn();
1940
2152
  const duration = performance.now() - startTime;
1941
- if (duration >= config2.slowThreshold) {
2153
+ if (duration >= config.slowThreshold) {
1942
2154
  const dbLogger4 = logger.child("database");
1943
2155
  const logData = {
1944
2156
  operation,
1945
2157
  table: this.table._.name,
1946
2158
  duration: `${duration.toFixed(2)}ms`,
1947
- threshold: `${config2.slowThreshold}ms`
2159
+ threshold: `${config.slowThreshold}ms`
1948
2160
  };
1949
2161
  dbLogger4.warn("Slow query detected", logData);
1950
2162
  }
@@ -2480,14 +2692,14 @@ function getDbCredentials(dialect, url) {
2480
2692
  }
2481
2693
  }
2482
2694
  function generateDrizzleConfigFile(options = {}) {
2483
- const config2 = getDrizzleConfig(options);
2695
+ const config = getDrizzleConfig(options);
2484
2696
  return `import { defineConfig } from 'drizzle-kit';
2485
2697
 
2486
2698
  export default defineConfig({
2487
- schema: '${config2.schema}',
2488
- out: '${config2.out}',
2489
- dialect: '${config2.dialect}',
2490
- dbCredentials: ${JSON.stringify(config2.dbCredentials, null, 4)},
2699
+ schema: '${config.schema}',
2700
+ out: '${config.out}',
2701
+ dialect: '${config.dialect}',
2702
+ dbCredentials: ${JSON.stringify(config.dbCredentials, null, 4)},
2491
2703
  });
2492
2704
  `;
2493
2705
  }
@@ -2907,8 +3119,8 @@ function generateRequestId() {
2907
3119
  const randomPart = randomBytes(6).toString("hex");
2908
3120
  return `req_${timestamp2}_${randomPart}`;
2909
3121
  }
2910
- function RequestLogger(config2) {
2911
- const cfg = { ...DEFAULT_CONFIG, ...config2 };
3122
+ function RequestLogger(config) {
3123
+ const cfg = { ...DEFAULT_CONFIG, ...config };
2912
3124
  const apiLogger = logger.child("api");
2913
3125
  return async (c, next) => {
2914
3126
  const path = new URL(c.req.url).pathname;
@@ -2972,7 +3184,7 @@ init_cache();
2972
3184
  init_db();
2973
3185
  init_logger2();
2974
3186
  var serverLogger = logger.child("server");
2975
- async function createServer(config2) {
3187
+ async function createServer(config) {
2976
3188
  const cwd = process.cwd();
2977
3189
  const appPath = join(cwd, "src", "server", "app.ts");
2978
3190
  const appJsPath = join(cwd, "src", "server", "app.js");
@@ -2983,23 +3195,23 @@ async function createServer(config2) {
2983
3195
  throw new Error("app.ts must export a default function that returns a Hono app");
2984
3196
  }
2985
3197
  const app2 = await appFactory();
2986
- const debug2 = config2?.debug ?? process.env.NODE_ENV === "development";
2987
- await loadRoutes(app2, { routesDir: config2?.routesPath, debug: debug2 });
3198
+ const debug2 = config?.debug ?? process.env.NODE_ENV === "development";
3199
+ await loadRoutes(app2, { routesDir: config?.routesPath, debug: debug2 });
2988
3200
  return app2;
2989
3201
  }
2990
3202
  const app = new Hono();
2991
- const middlewareConfig = config2?.middleware ?? {};
3203
+ const middlewareConfig = config?.middleware ?? {};
2992
3204
  const enableLogger = middlewareConfig.logger !== false;
2993
3205
  const enableCors = middlewareConfig.cors !== false;
2994
3206
  const enableErrorHandler = middlewareConfig.errorHandler !== false;
2995
3207
  if (enableLogger) {
2996
3208
  app.use("*", RequestLogger());
2997
3209
  }
2998
- if (enableCors && config2?.cors !== false) {
2999
- app.use("*", cors(config2?.cors));
3210
+ if (enableCors && config?.cors !== false) {
3211
+ app.use("*", cors(config?.cors));
3000
3212
  }
3001
- config2?.use?.forEach((mw) => app.use("*", mw));
3002
- const healthCheckConfig = config2?.healthCheck ?? {};
3213
+ config?.use?.forEach((mw) => app.use("*", mw));
3214
+ const healthCheckConfig = config?.healthCheck ?? {};
3003
3215
  const healthCheckEnabled = healthCheckConfig.enabled !== false;
3004
3216
  const healthCheckPath = healthCheckConfig.path ?? "/health";
3005
3217
  const healthCheckDetailed = healthCheckConfig.detailed ?? process.env.NODE_ENV === "development";
@@ -3041,20 +3253,20 @@ async function createServer(config2) {
3041
3253
  });
3042
3254
  serverLogger.debug(`Health check endpoint enabled at ${healthCheckPath}`);
3043
3255
  }
3044
- await config2?.beforeRoutes?.(app);
3045
- const debug = config2?.debug ?? process.env.NODE_ENV === "development";
3256
+ await config?.beforeRoutes?.(app);
3257
+ const debug = config?.debug ?? process.env.NODE_ENV === "development";
3046
3258
  await loadRoutes(app, {
3047
- routesDir: config2?.routesPath,
3259
+ routesDir: config?.routesPath,
3048
3260
  debug,
3049
- middlewares: config2?.middlewares
3261
+ middlewares: config?.middlewares
3050
3262
  });
3051
- await config2?.afterRoutes?.(app);
3263
+ await config?.afterRoutes?.(app);
3052
3264
  if (enableErrorHandler) {
3053
3265
  app.onError(ErrorHandler());
3054
3266
  }
3055
3267
  return app;
3056
3268
  }
3057
- async function startServer(config2) {
3269
+ async function startServer(config) {
3058
3270
  const cwd = process.cwd();
3059
3271
  const configPath = join(cwd, "src", "server", "server.config.ts");
3060
3272
  const configJsPath = join(cwd, "src", "server", "server.config.js");
@@ -3065,9 +3277,9 @@ async function startServer(config2) {
3065
3277
  }
3066
3278
  const finalConfig = {
3067
3279
  ...fileConfig,
3068
- ...config2,
3069
- port: config2?.port ?? fileConfig?.port ?? (parseInt(process.env.PORT || "", 10) || 4e3),
3070
- host: config2?.host ?? fileConfig?.host ?? (process.env.HOST || "localhost")
3280
+ ...config,
3281
+ port: config?.port ?? fileConfig?.port ?? (parseInt(process.env.PORT || "", 10) || 4e3),
3282
+ host: config?.host ?? fileConfig?.host ?? (process.env.HOST || "localhost")
3071
3283
  };
3072
3284
  const { host, port, debug } = finalConfig;
3073
3285
  try {