@spfn/core 0.1.0-alpha.88 → 0.2.0-beta.2
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/README.md +1046 -384
- package/dist/boss-D-fGtVgM.d.ts +187 -0
- package/dist/cache/index.d.ts +13 -33
- package/dist/cache/index.js +14 -703
- package/dist/cache/index.js.map +1 -1
- package/dist/codegen/index.d.ts +167 -17
- package/dist/codegen/index.js +76 -1419
- package/dist/codegen/index.js.map +1 -1
- package/dist/config/index.d.ts +1191 -0
- package/dist/config/index.js +264 -0
- package/dist/config/index.js.map +1 -0
- package/dist/db/index.d.ts +728 -59
- package/dist/db/index.js +1028 -1225
- package/dist/db/index.js.map +1 -1
- package/dist/env/index.d.ts +579 -308
- package/dist/env/index.js +438 -930
- package/dist/env/index.js.map +1 -1
- package/dist/errors/index.d.ts +417 -29
- package/dist/errors/index.js +359 -98
- package/dist/errors/index.js.map +1 -1
- package/dist/event/index.d.ts +108 -0
- package/dist/event/index.js +122 -0
- package/dist/event/index.js.map +1 -0
- package/dist/job/index.d.ts +172 -0
- package/dist/job/index.js +361 -0
- package/dist/job/index.js.map +1 -0
- package/dist/logger/index.d.ts +20 -79
- package/dist/logger/index.js +82 -387
- package/dist/logger/index.js.map +1 -1
- package/dist/middleware/index.d.ts +2 -11
- package/dist/middleware/index.js +49 -703
- package/dist/middleware/index.js.map +1 -1
- package/dist/nextjs/index.d.ts +120 -0
- package/dist/nextjs/index.js +416 -0
- package/dist/nextjs/index.js.map +1 -0
- package/dist/{client/nextjs/index.d.ts → nextjs/server.d.ts} +288 -262
- package/dist/nextjs/server.js +568 -0
- package/dist/nextjs/server.js.map +1 -0
- package/dist/route/index.d.ts +686 -25
- package/dist/route/index.js +440 -1287
- package/dist/route/index.js.map +1 -1
- package/dist/route/types.d.ts +38 -0
- package/dist/route/types.js +3 -0
- package/dist/route/types.js.map +1 -0
- package/dist/server/index.d.ts +201 -67
- package/dist/server/index.js +921 -3182
- package/dist/server/index.js.map +1 -1
- package/dist/types-BGl4QL1w.d.ts +77 -0
- package/dist/types-DRG2XMTR.d.ts +157 -0
- package/package.json +52 -47
- package/dist/auto-loader-JFaZ9gON.d.ts +0 -80
- package/dist/client/index.d.ts +0 -358
- package/dist/client/index.js +0 -357
- package/dist/client/index.js.map +0 -1
- package/dist/client/nextjs/index.js +0 -371
- package/dist/client/nextjs/index.js.map +0 -1
- package/dist/codegen/generators/index.d.ts +0 -19
- package/dist/codegen/generators/index.js +0 -1404
- package/dist/codegen/generators/index.js.map +0 -1
- package/dist/database-errors-BNNmLTJE.d.ts +0 -86
- package/dist/events/index.d.ts +0 -183
- package/dist/events/index.js +0 -77
- package/dist/events/index.js.map +0 -1
- package/dist/index-DHiAqhKv.d.ts +0 -101
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -3674
- package/dist/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -121
- package/dist/types/index.js +0 -38
- package/dist/types/index.js.map +0 -1
- package/dist/types-BXibIEyj.d.ts +0 -60
package/dist/cache/index.js
CHANGED
|
@@ -1,696 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
|
|
4
|
-
// src/logger/types.ts
|
|
5
|
-
var LOG_LEVEL_PRIORITY = {
|
|
6
|
-
debug: 0,
|
|
7
|
-
info: 1,
|
|
8
|
-
warn: 2,
|
|
9
|
-
error: 3,
|
|
10
|
-
fatal: 4
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
// src/logger/formatters.ts
|
|
14
|
-
var SENSITIVE_KEYS = [
|
|
15
|
-
"password",
|
|
16
|
-
"passwd",
|
|
17
|
-
"pwd",
|
|
18
|
-
"secret",
|
|
19
|
-
"token",
|
|
20
|
-
"apikey",
|
|
21
|
-
"api_key",
|
|
22
|
-
"accesstoken",
|
|
23
|
-
"access_token",
|
|
24
|
-
"refreshtoken",
|
|
25
|
-
"refresh_token",
|
|
26
|
-
"authorization",
|
|
27
|
-
"auth",
|
|
28
|
-
"cookie",
|
|
29
|
-
"session",
|
|
30
|
-
"sessionid",
|
|
31
|
-
"session_id",
|
|
32
|
-
"privatekey",
|
|
33
|
-
"private_key",
|
|
34
|
-
"creditcard",
|
|
35
|
-
"credit_card",
|
|
36
|
-
"cardnumber",
|
|
37
|
-
"card_number",
|
|
38
|
-
"cvv",
|
|
39
|
-
"ssn",
|
|
40
|
-
"pin"
|
|
41
|
-
];
|
|
42
|
-
var MASKED_VALUE = "***MASKED***";
|
|
43
|
-
function isSensitiveKey(key) {
|
|
44
|
-
const lowerKey = key.toLowerCase();
|
|
45
|
-
return SENSITIVE_KEYS.some((sensitive) => lowerKey.includes(sensitive));
|
|
46
|
-
}
|
|
47
|
-
function maskSensitiveData(data) {
|
|
48
|
-
if (data === null || data === void 0) {
|
|
49
|
-
return data;
|
|
50
|
-
}
|
|
51
|
-
if (Array.isArray(data)) {
|
|
52
|
-
return data.map((item) => maskSensitiveData(item));
|
|
53
|
-
}
|
|
54
|
-
if (typeof data === "object") {
|
|
55
|
-
const masked = {};
|
|
56
|
-
for (const [key, value] of Object.entries(data)) {
|
|
57
|
-
if (isSensitiveKey(key)) {
|
|
58
|
-
masked[key] = MASKED_VALUE;
|
|
59
|
-
} else if (typeof value === "object" && value !== null) {
|
|
60
|
-
masked[key] = maskSensitiveData(value);
|
|
61
|
-
} else {
|
|
62
|
-
masked[key] = value;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return masked;
|
|
66
|
-
}
|
|
67
|
-
return data;
|
|
68
|
-
}
|
|
69
|
-
var COLORS = {
|
|
70
|
-
reset: "\x1B[0m",
|
|
71
|
-
bright: "\x1B[1m",
|
|
72
|
-
dim: "\x1B[2m",
|
|
73
|
-
// 로그 레벨 컬러
|
|
74
|
-
debug: "\x1B[36m",
|
|
75
|
-
// cyan
|
|
76
|
-
info: "\x1B[32m",
|
|
77
|
-
// green
|
|
78
|
-
warn: "\x1B[33m",
|
|
79
|
-
// yellow
|
|
80
|
-
error: "\x1B[31m",
|
|
81
|
-
// red
|
|
82
|
-
fatal: "\x1B[35m",
|
|
83
|
-
// magenta
|
|
84
|
-
// 추가 컬러
|
|
85
|
-
gray: "\x1B[90m"
|
|
86
|
-
};
|
|
87
|
-
function formatTimestamp(date) {
|
|
88
|
-
return date.toISOString();
|
|
89
|
-
}
|
|
90
|
-
function formatTimestampHuman(date) {
|
|
91
|
-
const year = date.getFullYear();
|
|
92
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
93
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
94
|
-
const hours = String(date.getHours()).padStart(2, "0");
|
|
95
|
-
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
96
|
-
const seconds = String(date.getSeconds()).padStart(2, "0");
|
|
97
|
-
const ms = String(date.getMilliseconds()).padStart(3, "0");
|
|
98
|
-
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${ms}`;
|
|
99
|
-
}
|
|
100
|
-
function formatError(error) {
|
|
101
|
-
const lines = [];
|
|
102
|
-
lines.push(`${error.name}: ${error.message}`);
|
|
103
|
-
if (error.stack) {
|
|
104
|
-
const stackLines = error.stack.split("\n").slice(1);
|
|
105
|
-
lines.push(...stackLines);
|
|
106
|
-
}
|
|
107
|
-
return lines.join("\n");
|
|
108
|
-
}
|
|
109
|
-
function formatConsole(metadata, colorize = true) {
|
|
110
|
-
const parts = [];
|
|
111
|
-
const timestamp = formatTimestampHuman(metadata.timestamp);
|
|
112
|
-
if (colorize) {
|
|
113
|
-
parts.push(`${COLORS.gray}[${timestamp}]${COLORS.reset}`);
|
|
114
|
-
} else {
|
|
115
|
-
parts.push(`[${timestamp}]`);
|
|
116
|
-
}
|
|
117
|
-
if (metadata.module) {
|
|
118
|
-
if (colorize) {
|
|
119
|
-
parts.push(`${COLORS.dim}[module=${metadata.module}]${COLORS.reset}`);
|
|
120
|
-
} else {
|
|
121
|
-
parts.push(`[module=${metadata.module}]`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (metadata.context && Object.keys(metadata.context).length > 0) {
|
|
125
|
-
Object.entries(metadata.context).forEach(([key, value]) => {
|
|
126
|
-
let valueStr;
|
|
127
|
-
if (typeof value === "string") {
|
|
128
|
-
valueStr = value;
|
|
129
|
-
} else if (typeof value === "object" && value !== null) {
|
|
130
|
-
try {
|
|
131
|
-
valueStr = JSON.stringify(value);
|
|
132
|
-
} catch (error) {
|
|
133
|
-
valueStr = "[circular]";
|
|
134
|
-
}
|
|
135
|
-
} else {
|
|
136
|
-
valueStr = String(value);
|
|
137
|
-
}
|
|
138
|
-
if (colorize) {
|
|
139
|
-
parts.push(`${COLORS.dim}[${key}=${valueStr}]${COLORS.reset}`);
|
|
140
|
-
} else {
|
|
141
|
-
parts.push(`[${key}=${valueStr}]`);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
const levelStr = metadata.level.toUpperCase();
|
|
146
|
-
if (colorize) {
|
|
147
|
-
const color = COLORS[metadata.level];
|
|
148
|
-
parts.push(`${color}(${levelStr})${COLORS.reset}:`);
|
|
149
|
-
} else {
|
|
150
|
-
parts.push(`(${levelStr}):`);
|
|
151
|
-
}
|
|
152
|
-
if (colorize) {
|
|
153
|
-
parts.push(`${COLORS.bright}${metadata.message}${COLORS.reset}`);
|
|
154
|
-
} else {
|
|
155
|
-
parts.push(metadata.message);
|
|
156
|
-
}
|
|
157
|
-
let output = parts.join(" ");
|
|
158
|
-
if (metadata.error) {
|
|
159
|
-
output += "\n" + formatError(metadata.error);
|
|
160
|
-
}
|
|
161
|
-
return output;
|
|
162
|
-
}
|
|
163
|
-
function formatJSON(metadata) {
|
|
164
|
-
const obj = {
|
|
165
|
-
timestamp: formatTimestamp(metadata.timestamp),
|
|
166
|
-
level: metadata.level,
|
|
167
|
-
message: metadata.message
|
|
168
|
-
};
|
|
169
|
-
if (metadata.module) {
|
|
170
|
-
obj.module = metadata.module;
|
|
171
|
-
}
|
|
172
|
-
if (metadata.context) {
|
|
173
|
-
obj.context = metadata.context;
|
|
174
|
-
}
|
|
175
|
-
if (metadata.error) {
|
|
176
|
-
obj.error = {
|
|
177
|
-
name: metadata.error.name,
|
|
178
|
-
message: metadata.error.message,
|
|
179
|
-
stack: metadata.error.stack
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
return JSON.stringify(obj);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// src/logger/logger.ts
|
|
186
|
-
var Logger = class _Logger {
|
|
187
|
-
config;
|
|
188
|
-
module;
|
|
189
|
-
constructor(config) {
|
|
190
|
-
this.config = config;
|
|
191
|
-
this.module = config.module;
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Get current log level
|
|
195
|
-
*/
|
|
196
|
-
get level() {
|
|
197
|
-
return this.config.level;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Create child logger (per module)
|
|
201
|
-
*/
|
|
202
|
-
child(module) {
|
|
203
|
-
return new _Logger({
|
|
204
|
-
...this.config,
|
|
205
|
-
module
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Debug log
|
|
210
|
-
*/
|
|
211
|
-
debug(message, context) {
|
|
212
|
-
this.log("debug", message, void 0, context);
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Info log
|
|
216
|
-
*/
|
|
217
|
-
info(message, context) {
|
|
218
|
-
this.log("info", message, void 0, context);
|
|
219
|
-
}
|
|
220
|
-
warn(message, errorOrContext, context) {
|
|
221
|
-
if (errorOrContext instanceof Error) {
|
|
222
|
-
this.log("warn", message, errorOrContext, context);
|
|
223
|
-
} else {
|
|
224
|
-
this.log("warn", message, void 0, errorOrContext);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
error(message, errorOrContext, context) {
|
|
228
|
-
if (errorOrContext instanceof Error) {
|
|
229
|
-
this.log("error", message, errorOrContext, context);
|
|
230
|
-
} else {
|
|
231
|
-
this.log("error", message, void 0, errorOrContext);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
fatal(message, errorOrContext, context) {
|
|
235
|
-
if (errorOrContext instanceof Error) {
|
|
236
|
-
this.log("fatal", message, errorOrContext, context);
|
|
237
|
-
} else {
|
|
238
|
-
this.log("fatal", message, void 0, errorOrContext);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Log processing (internal)
|
|
243
|
-
*/
|
|
244
|
-
log(level, message, error, context) {
|
|
245
|
-
if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[this.config.level]) {
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
const metadata = {
|
|
249
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
250
|
-
level,
|
|
251
|
-
message,
|
|
252
|
-
module: this.module,
|
|
253
|
-
error,
|
|
254
|
-
// Mask sensitive information in context to prevent credential leaks
|
|
255
|
-
context: context ? maskSensitiveData(context) : void 0
|
|
256
|
-
};
|
|
257
|
-
this.processTransports(metadata);
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Process Transports
|
|
261
|
-
*/
|
|
262
|
-
processTransports(metadata) {
|
|
263
|
-
const promises = this.config.transports.filter((transport) => transport.enabled).map((transport) => this.safeTransportLog(transport, metadata));
|
|
264
|
-
Promise.all(promises).catch((error) => {
|
|
265
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
266
|
-
process.stderr.write(`[Logger] Transport error: ${errorMessage}
|
|
267
|
-
`);
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Transport log (error-safe)
|
|
272
|
-
*/
|
|
273
|
-
async safeTransportLog(transport, metadata) {
|
|
274
|
-
try {
|
|
275
|
-
await transport.log(metadata);
|
|
276
|
-
} catch (error) {
|
|
277
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
278
|
-
process.stderr.write(`[Logger] Transport "${transport.name}" failed: ${errorMessage}
|
|
279
|
-
`);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* Close all Transports
|
|
284
|
-
*/
|
|
285
|
-
async close() {
|
|
286
|
-
const closePromises = this.config.transports.filter((transport) => transport.close).map((transport) => transport.close());
|
|
287
|
-
await Promise.all(closePromises);
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
// src/logger/transports/console.ts
|
|
292
|
-
var ConsoleTransport = class {
|
|
293
|
-
name = "console";
|
|
294
|
-
level;
|
|
295
|
-
enabled;
|
|
296
|
-
colorize;
|
|
297
|
-
constructor(config) {
|
|
298
|
-
this.level = config.level;
|
|
299
|
-
this.enabled = config.enabled;
|
|
300
|
-
this.colorize = config.colorize ?? true;
|
|
301
|
-
}
|
|
302
|
-
async log(metadata) {
|
|
303
|
-
if (!this.enabled) {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
if (LOG_LEVEL_PRIORITY[metadata.level] < LOG_LEVEL_PRIORITY[this.level]) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
const message = formatConsole(metadata, this.colorize);
|
|
310
|
-
if (metadata.level === "warn" || metadata.level === "error" || metadata.level === "fatal") {
|
|
311
|
-
console.error(message);
|
|
312
|
-
} else {
|
|
313
|
-
console.log(message);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
var FileTransport = class {
|
|
318
|
-
name = "file";
|
|
319
|
-
level;
|
|
320
|
-
enabled;
|
|
321
|
-
logDir;
|
|
322
|
-
maxFileSize;
|
|
323
|
-
maxFiles;
|
|
324
|
-
currentStream = null;
|
|
325
|
-
currentFilename = null;
|
|
326
|
-
constructor(config) {
|
|
327
|
-
this.level = config.level;
|
|
328
|
-
this.enabled = config.enabled;
|
|
329
|
-
this.logDir = config.logDir;
|
|
330
|
-
this.maxFileSize = config.maxFileSize ?? 10 * 1024 * 1024;
|
|
331
|
-
this.maxFiles = config.maxFiles ?? 10;
|
|
332
|
-
if (!existsSync(this.logDir)) {
|
|
333
|
-
mkdirSync(this.logDir, { recursive: true });
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
async log(metadata) {
|
|
337
|
-
if (!this.enabled) {
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
if (LOG_LEVEL_PRIORITY[metadata.level] < LOG_LEVEL_PRIORITY[this.level]) {
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
const message = formatJSON(metadata);
|
|
344
|
-
const filename = this.getLogFilename(metadata.timestamp);
|
|
345
|
-
if (this.currentFilename !== filename) {
|
|
346
|
-
await this.rotateStream(filename);
|
|
347
|
-
await this.cleanOldFiles();
|
|
348
|
-
} else if (this.currentFilename) {
|
|
349
|
-
await this.checkAndRotateBySize();
|
|
350
|
-
}
|
|
351
|
-
if (this.currentStream) {
|
|
352
|
-
return new Promise((resolve, reject) => {
|
|
353
|
-
this.currentStream.write(message + "\n", "utf-8", (error) => {
|
|
354
|
-
if (error) {
|
|
355
|
-
process.stderr.write(`[FileTransport] Failed to write log: ${error.message}
|
|
356
|
-
`);
|
|
357
|
-
reject(error);
|
|
358
|
-
} else {
|
|
359
|
-
resolve();
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
/**
|
|
366
|
-
* 스트림 교체 (날짜 변경 시)
|
|
367
|
-
*/
|
|
368
|
-
async rotateStream(filename) {
|
|
369
|
-
if (this.currentStream) {
|
|
370
|
-
await this.closeStream();
|
|
371
|
-
}
|
|
372
|
-
const filepath = join(this.logDir, filename);
|
|
373
|
-
this.currentStream = createWriteStream(filepath, {
|
|
374
|
-
flags: "a",
|
|
375
|
-
// append mode
|
|
376
|
-
encoding: "utf-8"
|
|
377
|
-
});
|
|
378
|
-
this.currentFilename = filename;
|
|
379
|
-
this.currentStream.on("error", (error) => {
|
|
380
|
-
process.stderr.write(`[FileTransport] Stream error: ${error.message}
|
|
381
|
-
`);
|
|
382
|
-
this.currentStream = null;
|
|
383
|
-
this.currentFilename = null;
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* 현재 스트림 닫기
|
|
388
|
-
*/
|
|
389
|
-
async closeStream() {
|
|
390
|
-
if (!this.currentStream) {
|
|
391
|
-
return;
|
|
392
|
-
}
|
|
393
|
-
return new Promise((resolve, reject) => {
|
|
394
|
-
this.currentStream.end((error) => {
|
|
395
|
-
if (error) {
|
|
396
|
-
reject(error);
|
|
397
|
-
} else {
|
|
398
|
-
this.currentStream = null;
|
|
399
|
-
this.currentFilename = null;
|
|
400
|
-
resolve();
|
|
401
|
-
}
|
|
402
|
-
});
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
/**
|
|
406
|
-
* 파일 크기 체크 및 크기 기반 로테이션
|
|
407
|
-
*/
|
|
408
|
-
async checkAndRotateBySize() {
|
|
409
|
-
if (!this.currentFilename) {
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
const filepath = join(this.logDir, this.currentFilename);
|
|
413
|
-
if (!existsSync(filepath)) {
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
try {
|
|
417
|
-
const stats = statSync(filepath);
|
|
418
|
-
if (stats.size >= this.maxFileSize) {
|
|
419
|
-
await this.rotateBySize();
|
|
420
|
-
}
|
|
421
|
-
} catch (error) {
|
|
422
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
423
|
-
process.stderr.write(`[FileTransport] Failed to check file size: ${errorMessage}
|
|
424
|
-
`);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* 크기 기반 로테이션 수행
|
|
429
|
-
* 예: 2025-01-01.log -> 2025-01-01.1.log, 2025-01-01.1.log -> 2025-01-01.2.log
|
|
430
|
-
*/
|
|
431
|
-
async rotateBySize() {
|
|
432
|
-
if (!this.currentFilename) {
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
await this.closeStream();
|
|
436
|
-
const baseName = this.currentFilename.replace(/\.log$/, "");
|
|
437
|
-
const files = readdirSync(this.logDir);
|
|
438
|
-
const relatedFiles = files.filter((file) => file.startsWith(baseName) && file.endsWith(".log")).sort().reverse();
|
|
439
|
-
for (const file of relatedFiles) {
|
|
440
|
-
const match = file.match(/\.(\d+)\.log$/);
|
|
441
|
-
if (match) {
|
|
442
|
-
const oldNum = parseInt(match[1], 10);
|
|
443
|
-
const newNum = oldNum + 1;
|
|
444
|
-
const oldPath = join(this.logDir, file);
|
|
445
|
-
const newPath2 = join(this.logDir, `${baseName}.${newNum}.log`);
|
|
446
|
-
try {
|
|
447
|
-
renameSync(oldPath, newPath2);
|
|
448
|
-
} catch (error) {
|
|
449
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
450
|
-
process.stderr.write(`[FileTransport] Failed to rotate file: ${errorMessage}
|
|
451
|
-
`);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
const currentPath = join(this.logDir, this.currentFilename);
|
|
456
|
-
const newPath = join(this.logDir, `${baseName}.1.log`);
|
|
457
|
-
try {
|
|
458
|
-
if (existsSync(currentPath)) {
|
|
459
|
-
renameSync(currentPath, newPath);
|
|
460
|
-
}
|
|
461
|
-
} catch (error) {
|
|
462
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
463
|
-
process.stderr.write(`[FileTransport] Failed to rotate current file: ${errorMessage}
|
|
464
|
-
`);
|
|
465
|
-
}
|
|
466
|
-
await this.rotateStream(this.currentFilename);
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* 오래된 로그 파일 정리
|
|
470
|
-
* maxFiles 개수를 초과하는 로그 파일 삭제
|
|
471
|
-
*/
|
|
472
|
-
async cleanOldFiles() {
|
|
473
|
-
try {
|
|
474
|
-
if (!existsSync(this.logDir)) {
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
const files = readdirSync(this.logDir);
|
|
478
|
-
const logFiles = files.filter((file) => file.endsWith(".log")).map((file) => {
|
|
479
|
-
const filepath = join(this.logDir, file);
|
|
480
|
-
const stats = statSync(filepath);
|
|
481
|
-
return { file, mtime: stats.mtime };
|
|
482
|
-
}).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
483
|
-
if (logFiles.length > this.maxFiles) {
|
|
484
|
-
const filesToDelete = logFiles.slice(this.maxFiles);
|
|
485
|
-
for (const { file } of filesToDelete) {
|
|
486
|
-
const filepath = join(this.logDir, file);
|
|
487
|
-
try {
|
|
488
|
-
unlinkSync(filepath);
|
|
489
|
-
} catch (error) {
|
|
490
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
491
|
-
process.stderr.write(`[FileTransport] Failed to delete old file "${file}": ${errorMessage}
|
|
492
|
-
`);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
} catch (error) {
|
|
497
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
498
|
-
process.stderr.write(`[FileTransport] Failed to clean old files: ${errorMessage}
|
|
499
|
-
`);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* 날짜별 로그 파일명 생성
|
|
504
|
-
*/
|
|
505
|
-
getLogFilename(date) {
|
|
506
|
-
const year = date.getFullYear();
|
|
507
|
-
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
508
|
-
const day = String(date.getDate()).padStart(2, "0");
|
|
509
|
-
return `${year}-${month}-${day}.log`;
|
|
510
|
-
}
|
|
511
|
-
async close() {
|
|
512
|
-
await this.closeStream();
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
function isFileLoggingEnabled() {
|
|
516
|
-
return process.env.LOGGER_FILE_ENABLED === "true";
|
|
517
|
-
}
|
|
518
|
-
function getDefaultLogLevel() {
|
|
519
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
520
|
-
const isDevelopment = process.env.NODE_ENV === "development";
|
|
521
|
-
if (isDevelopment) {
|
|
522
|
-
return "debug";
|
|
523
|
-
}
|
|
524
|
-
if (isProduction) {
|
|
525
|
-
return "info";
|
|
526
|
-
}
|
|
527
|
-
return "warn";
|
|
528
|
-
}
|
|
529
|
-
function getConsoleConfig() {
|
|
530
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
531
|
-
return {
|
|
532
|
-
level: "debug",
|
|
533
|
-
enabled: true,
|
|
534
|
-
colorize: !isProduction
|
|
535
|
-
// Dev: colored output, Production: plain text
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
|
-
function getFileConfig() {
|
|
539
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
540
|
-
return {
|
|
541
|
-
level: "info",
|
|
542
|
-
enabled: isProduction,
|
|
543
|
-
// File logging in production only
|
|
544
|
-
logDir: process.env.LOG_DIR || "./logs",
|
|
545
|
-
maxFileSize: 10 * 1024 * 1024,
|
|
546
|
-
// 10MB
|
|
547
|
-
maxFiles: 10
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
function validateDirectoryWritable(dirPath) {
|
|
551
|
-
if (!existsSync(dirPath)) {
|
|
552
|
-
try {
|
|
553
|
-
mkdirSync(dirPath, { recursive: true });
|
|
554
|
-
} catch (error) {
|
|
555
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
556
|
-
throw new Error(`Failed to create log directory "${dirPath}": ${errorMessage}`);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
try {
|
|
560
|
-
accessSync(dirPath, constants.W_OK);
|
|
561
|
-
} catch {
|
|
562
|
-
throw new Error(`Log directory "${dirPath}" is not writable. Please check permissions.`);
|
|
563
|
-
}
|
|
564
|
-
const testFile = join(dirPath, ".logger-write-test");
|
|
565
|
-
try {
|
|
566
|
-
writeFileSync(testFile, "test", "utf-8");
|
|
567
|
-
unlinkSync(testFile);
|
|
568
|
-
} catch (error) {
|
|
569
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
570
|
-
throw new Error(`Cannot write to log directory "${dirPath}": ${errorMessage}`);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
function validateFileConfig() {
|
|
574
|
-
if (!isFileLoggingEnabled()) {
|
|
575
|
-
return;
|
|
576
|
-
}
|
|
577
|
-
const logDir = process.env.LOG_DIR;
|
|
578
|
-
if (!logDir) {
|
|
579
|
-
throw new Error(
|
|
580
|
-
"LOG_DIR environment variable is required when LOGGER_FILE_ENABLED=true. Example: LOG_DIR=/var/log/myapp"
|
|
581
|
-
);
|
|
582
|
-
}
|
|
583
|
-
validateDirectoryWritable(logDir);
|
|
584
|
-
}
|
|
585
|
-
function validateSlackConfig() {
|
|
586
|
-
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
|
|
587
|
-
if (!webhookUrl) {
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
if (!webhookUrl.startsWith("https://hooks.slack.com/")) {
|
|
591
|
-
throw new Error(
|
|
592
|
-
`Invalid SLACK_WEBHOOK_URL: "${webhookUrl}". Slack webhook URLs must start with "https://hooks.slack.com/"`
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
function validateEmailConfig() {
|
|
597
|
-
const smtpHost = process.env.SMTP_HOST;
|
|
598
|
-
const smtpPort = process.env.SMTP_PORT;
|
|
599
|
-
const emailFrom = process.env.EMAIL_FROM;
|
|
600
|
-
const emailTo = process.env.EMAIL_TO;
|
|
601
|
-
const hasAnyEmailConfig = smtpHost || smtpPort || emailFrom || emailTo;
|
|
602
|
-
if (!hasAnyEmailConfig) {
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
const missingFields = [];
|
|
606
|
-
if (!smtpHost) missingFields.push("SMTP_HOST");
|
|
607
|
-
if (!smtpPort) missingFields.push("SMTP_PORT");
|
|
608
|
-
if (!emailFrom) missingFields.push("EMAIL_FROM");
|
|
609
|
-
if (!emailTo) missingFields.push("EMAIL_TO");
|
|
610
|
-
if (missingFields.length > 0) {
|
|
611
|
-
throw new Error(
|
|
612
|
-
`Email transport configuration incomplete. Missing: ${missingFields.join(", ")}. Either set all required fields or remove all email configuration.`
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
|
-
const port = parseInt(smtpPort, 10);
|
|
616
|
-
if (isNaN(port) || port < 1 || port > 65535) {
|
|
617
|
-
throw new Error(
|
|
618
|
-
`Invalid SMTP_PORT: "${smtpPort}". Must be a number between 1 and 65535.`
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
622
|
-
if (!emailRegex.test(emailFrom)) {
|
|
623
|
-
throw new Error(`Invalid EMAIL_FROM format: "${emailFrom}"`);
|
|
624
|
-
}
|
|
625
|
-
const recipients = emailTo.split(",").map((e) => e.trim());
|
|
626
|
-
for (const email of recipients) {
|
|
627
|
-
if (!emailRegex.test(email)) {
|
|
628
|
-
throw new Error(`Invalid email address in EMAIL_TO: "${email}"`);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
function validateEnvironment() {
|
|
633
|
-
const nodeEnv = process.env.NODE_ENV;
|
|
634
|
-
if (!nodeEnv) {
|
|
635
|
-
process.stderr.write(
|
|
636
|
-
"[Logger] Warning: NODE_ENV is not set. Defaulting to test environment.\n"
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
function validateConfig() {
|
|
641
|
-
try {
|
|
642
|
-
validateEnvironment();
|
|
643
|
-
validateFileConfig();
|
|
644
|
-
validateSlackConfig();
|
|
645
|
-
validateEmailConfig();
|
|
646
|
-
} catch (error) {
|
|
647
|
-
if (error instanceof Error) {
|
|
648
|
-
throw new Error(`[Logger] Configuration validation failed: ${error.message}`);
|
|
649
|
-
}
|
|
650
|
-
throw error;
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// src/logger/factory.ts
|
|
655
|
-
function initializeTransports() {
|
|
656
|
-
const transports = [];
|
|
657
|
-
const consoleConfig = getConsoleConfig();
|
|
658
|
-
transports.push(new ConsoleTransport(consoleConfig));
|
|
659
|
-
const fileConfig = getFileConfig();
|
|
660
|
-
if (fileConfig.enabled) {
|
|
661
|
-
transports.push(new FileTransport(fileConfig));
|
|
662
|
-
}
|
|
663
|
-
return transports;
|
|
664
|
-
}
|
|
665
|
-
function initializeLogger() {
|
|
666
|
-
validateConfig();
|
|
667
|
-
return new Logger({
|
|
668
|
-
level: getDefaultLogLevel(),
|
|
669
|
-
transports: initializeTransports()
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
var logger = initializeLogger();
|
|
1
|
+
import { logger } from '@spfn/core/logger';
|
|
673
2
|
|
|
674
3
|
// src/cache/cache-factory.ts
|
|
675
|
-
var cacheLogger = logger.child("cache");
|
|
4
|
+
var cacheLogger = logger.child("@spfn/core:cache");
|
|
676
5
|
function hasCacheConfig() {
|
|
677
|
-
return
|
|
678
|
-
(process.env.VALKEY_URL || process.env.CACHE_URL || process.env.VALKEY_WRITE_URL || process.env.VALKEY_READ_URL || process.env.CACHE_WRITE_URL || process.env.CACHE_READ_URL || process.env.VALKEY_SENTINEL_HOSTS || process.env.VALKEY_CLUSTER_NODES || // Legacy (Redis - backward compatibility)
|
|
679
|
-
process.env.REDIS_URL || process.env.REDIS_WRITE_URL || process.env.REDIS_READ_URL || process.env.REDIS_SENTINEL_HOSTS || process.env.REDIS_CLUSTER_NODES);
|
|
680
|
-
}
|
|
681
|
-
function getEnv(valkeyKey, cacheKey, redisKey) {
|
|
682
|
-
return process.env[valkeyKey] || process.env[cacheKey] || process.env[redisKey];
|
|
6
|
+
return !!(process.env.CACHE_URL || process.env.CACHE_WRITE_URL || process.env.CACHE_READ_URL || process.env.CACHE_SENTINEL_HOSTS || process.env.CACHE_CLUSTER_NODES);
|
|
683
7
|
}
|
|
684
8
|
function createClient(RedisClient, url) {
|
|
685
9
|
const options = {};
|
|
686
|
-
if (url.startsWith("rediss://")
|
|
687
|
-
const rejectUnauthorized = getEnv(
|
|
688
|
-
"VALKEY_TLS_REJECT_UNAUTHORIZED",
|
|
689
|
-
"CACHE_TLS_REJECT_UNAUTHORIZED",
|
|
690
|
-
"REDIS_TLS_REJECT_UNAUTHORIZED"
|
|
691
|
-
);
|
|
10
|
+
if (url.startsWith("rediss://")) {
|
|
692
11
|
options.tls = {
|
|
693
|
-
rejectUnauthorized:
|
|
12
|
+
rejectUnauthorized: process.env.CACHE_TLS_REJECT_UNAUTHORIZED !== "false"
|
|
694
13
|
};
|
|
695
14
|
}
|
|
696
15
|
return new RedisClient(url, options);
|
|
@@ -703,13 +22,13 @@ async function createCacheFromEnv() {
|
|
|
703
22
|
try {
|
|
704
23
|
const ioredis = await import('ioredis');
|
|
705
24
|
const RedisClient = ioredis.default;
|
|
706
|
-
const singleUrl =
|
|
707
|
-
const writeUrl =
|
|
708
|
-
const readUrl =
|
|
709
|
-
const clusterNodes =
|
|
710
|
-
const sentinelHosts =
|
|
711
|
-
const masterName =
|
|
712
|
-
const password =
|
|
25
|
+
const singleUrl = process.env.CACHE_URL;
|
|
26
|
+
const writeUrl = process.env.CACHE_WRITE_URL;
|
|
27
|
+
const readUrl = process.env.CACHE_READ_URL;
|
|
28
|
+
const clusterNodes = process.env.CACHE_CLUSTER_NODES;
|
|
29
|
+
const sentinelHosts = process.env.CACHE_SENTINEL_HOSTS;
|
|
30
|
+
const masterName = process.env.CACHE_MASTER_NAME;
|
|
31
|
+
const password = process.env.CACHE_PASSWORD;
|
|
713
32
|
if (singleUrl && !writeUrl && !readUrl && !clusterNodes) {
|
|
714
33
|
const client = createClient(RedisClient, singleUrl);
|
|
715
34
|
cacheLogger.debug("Created single cache instance", { url: singleUrl.replace(/:[^:@]+@/, ":***@") });
|
|
@@ -787,9 +106,7 @@ async function createSingleCacheFromEnv() {
|
|
|
787
106
|
const { write } = await createCacheFromEnv();
|
|
788
107
|
return write;
|
|
789
108
|
}
|
|
790
|
-
|
|
791
|
-
// src/cache/cache-manager.ts
|
|
792
|
-
var cacheLogger2 = logger.child("cache");
|
|
109
|
+
var cacheLogger2 = logger.child("@spfn/core:cache");
|
|
793
110
|
var writeInstance;
|
|
794
111
|
var readInstance;
|
|
795
112
|
var isDisabled = false;
|
|
@@ -882,13 +199,7 @@ function getCacheInfo() {
|
|
|
882
199
|
disabled: isDisabled
|
|
883
200
|
};
|
|
884
201
|
}
|
|
885
|
-
var getRedis = getCache;
|
|
886
|
-
var getRedisRead = getCacheRead;
|
|
887
|
-
var setRedis = setCache;
|
|
888
|
-
var initRedis = initCache;
|
|
889
|
-
var closeRedis = closeCache;
|
|
890
|
-
var getRedisInfo = getCacheInfo;
|
|
891
202
|
|
|
892
|
-
export { closeCache,
|
|
203
|
+
export { closeCache, createCacheFromEnv, createCacheFromEnv as createRedisFromEnv, createSingleCacheFromEnv, createSingleCacheFromEnv as createSingleRedisFromEnv, getCache, getCacheInfo, getCacheRead, initCache, isCacheDisabled, setCache };
|
|
893
204
|
//# sourceMappingURL=index.js.map
|
|
894
205
|
//# sourceMappingURL=index.js.map
|