@simplysm/core-node 14.0.15 → 14.0.17

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @simplysm/core-node
2
2
 
3
- Node.js-specific core utilities for the Simplysm framework. Provides enhanced file system operations, child process execution, path utilities, file watching, and a type-safe worker thread abstraction.
3
+ Node.js-specific core utilities for the Simplysm framework. Provides enhanced file system operations, child process execution, path utilities, file watching, consola logging configuration, and a type-safe worker thread abstraction.
4
4
 
5
5
  ## Installation
6
6
 
@@ -26,8 +26,8 @@ Namespace `fsx` -- Enhanced file system functions (sync and async pairs).
26
26
  | `copySync` | function | Copy file/directory with filter (sync) |
27
27
  | `read` | function | Read file as UTF-8 string (async) |
28
28
  | `readSync` | function | Read file as UTF-8 string (sync) |
29
- | `readBuffer` | function | Read file as Buffer (async) |
30
- | `readBufferSync` | function | Read file as Buffer (sync) |
29
+ | `readBytes` | function | Read file as Uint8Array (async) |
30
+ | `readBytesSync` | function | Read file as Uint8Array (sync) |
31
31
  | `readJson` | function | Read and parse JSON file (async) |
32
32
  | `readJsonSync` | function | Read and parse JSON file (sync) |
33
33
  | `write` | function | Write data to file (async) |
@@ -54,15 +54,14 @@ Namespace `cpx` -- Child process execution utilities.
54
54
 
55
55
  | API | Type | Description |
56
56
  |-----|------|-------------|
57
- | `ExecOptions` | interface | Options for `exec` |
58
- | `ExecSyncOptions` | type | Options for `execSync` (same as `ExecOptions` without `reject`) |
59
- | `ExecResult` | interface | Result of a child process execution |
60
- | `ExecProcess` | class | `PromiseLike<ExecResult>` wrapper with `kill()` support |
61
- | `exec` | function | Spawn a child process (async, returns `ExecProcess`) |
62
- | `execSync` | function | Spawn a child process (sync) |
57
+ | `SpawnResult` | interface | Result of a spawned child process |
58
+ | `SpawnProcess` | class | `PromiseLike<SpawnResult>` wrapper with `kill()` support |
59
+ | `spawn` | function | Spawn a child process (async, returns `SpawnProcess`) |
60
+ | `spawnSync` | function | Spawn a child process (sync) |
63
61
  | `codePageToEncoding` | function | Convert Windows code page number to encoding name |
64
62
  | `getSystemEncoding` | function | Detect system console encoding (cached) |
65
63
  | `resetEncodingCache` | function | Clear the cached system encoding |
64
+ | `resolveStdioPipe` | function | Determine which stdio channels are piped |
66
65
  | `decodeBytes` | function | Decode `Uint8Array` output with system encoding fallback |
67
66
 
68
67
  > See [docs/cpx.md](./docs/cpx.md) for details.
@@ -83,7 +82,7 @@ Namespace `pathx` -- Path manipulation utilities.
83
82
 
84
83
  > See [docs/pathx.md](./docs/pathx.md) for details.
85
84
 
86
- ### Features
85
+ ### Features / FsWatcher
87
86
 
88
87
  | API | Type | Description |
89
88
  |-----|------|-------------|
@@ -93,6 +92,19 @@ Namespace `pathx` -- Path manipulation utilities.
93
92
 
94
93
  > See [docs/fs-watcher.md](./docs/fs-watcher.md) for details.
95
94
 
95
+ ### Features / Consola
96
+
97
+ | API | Type | Description |
98
+ |-----|------|-------------|
99
+ | `PrettyReporter` | class | Terminal consola reporter with icons, colors, and error stack formatting |
100
+ | `FileReporterOptions` | interface | Options for `createFileReporter` (maxSize, maxDays) |
101
+ | `createFileReporter` | function | Create a file-based consola reporter (JSON lines, date rotation) |
102
+ | `withMaxLevel` | function | Wrap a reporter to filter out log entries above a max level |
103
+ | `SetupConsolaOptions` | interface | Options for `setupConsola` |
104
+ | `setupConsola` | function | Configure consola reporters based on environment (prod/dev/debug) |
105
+
106
+ > See [docs/consola.md](./docs/consola.md) for details.
107
+
96
108
  ### Worker
97
109
 
98
110
  | API | Type | Description |
@@ -118,6 +130,9 @@ import { fsx } from "@simplysm/core-node";
118
130
  const content = await fsx.read("/path/to/file.txt");
119
131
  await fsx.write("/path/to/output.txt", "hello");
120
132
 
133
+ // Binary read
134
+ const rawBytes = await fsx.readBytes("/path/to/image.png");
135
+
121
136
  // JSON
122
137
  const data = await fsx.readJson<{ name: string }>("/path/to/config.json");
123
138
  await fsx.writeJson("/path/to/out.json", data, { space: 2 });
@@ -135,15 +150,18 @@ const tsFiles = await fsx.glob("/project/src/**/*.ts");
135
150
  import { cpx } from "@simplysm/core-node";
136
151
 
137
152
  // Await result
138
- const result = await cpx.exec("git", ["status"], { cwd: "/project" });
153
+ const result = await cpx.spawn("git", ["status"], { cwd: "/project" });
139
154
  // result: { stdout, stderr, exitCode }
140
155
 
141
156
  // Kill a running process
142
- const proc = cpx.exec("long-running-cmd", []);
157
+ const proc = cpx.spawn("long-running-cmd", []);
143
158
  proc.kill();
144
159
 
145
160
  // Inherit stdio, don't reject on non-zero exit
146
- await cpx.exec("make", ["build"], { stdio: "inherit", reject: false });
161
+ await cpx.spawn("make", ["build"], { stdio: "inherit", reject: false });
162
+
163
+ // Synchronous execution
164
+ const syncResult = cpx.spawnSync("node", ["--version"]);
147
165
  ```
148
166
 
149
167
  ### Path utilities
@@ -157,6 +175,18 @@ const name = pathx.basenameWithoutExt("file.spec.ts"); // "file.spec"
157
175
  const isChild = pathx.isChildPath("/a/b/c", "/a/b"); // true
158
176
  ```
159
177
 
178
+ ### Consola logging
179
+
180
+ ```typescript
181
+ import { setupConsola } from "@simplysm/core-node";
182
+
183
+ // Auto-configure based on environment (prod → file only, dev → file + terminal)
184
+ setupConsola();
185
+
186
+ // CLI mode — always use terminal output regardless of environment
187
+ setupConsola({ cli: true });
188
+ ```
189
+
160
190
  ### File watcher
161
191
 
162
192
  ```typescript
@@ -0,0 +1,9 @@
1
+ import type { ConsolaReporter } from "consola";
2
+ export interface FileReporterOptions {
3
+ /** @default 20MB */
4
+ maxSize?: number;
5
+ /** @default 14 */
6
+ maxDays?: number;
7
+ }
8
+ export declare function createFileReporter(options?: FileReporterOptions): ConsolaReporter;
9
+ //# sourceMappingURL=file-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-reporter.d.ts","sourceRoot":"","sources":["../../../src/features/consola/file-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,eAAe,EAAa,MAAM,SAAS,CAAC;AAI1E,MAAM,WAAW,mBAAmB;IAClC,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAKD,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,eAAe,CAyEjF"}
@@ -0,0 +1,108 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ const DEFAULT_MAX_SIZE = 20 * 1024 * 1024;
4
+ const DEFAULT_MAX_DAYS = 14;
5
+ export function createFileReporter(options) {
6
+ const maxSize = options?.maxSize ?? DEFAULT_MAX_SIZE;
7
+ const maxDays = options?.maxDays ?? DEFAULT_MAX_DAYS;
8
+ const outDir = path.resolve(process.cwd(), ".logs");
9
+ let dirEnsured = false;
10
+ let stream = null;
11
+ let currentDate = "";
12
+ let currentSize = 0;
13
+ let lastCleanDate = "";
14
+ function rotate(dateStr) {
15
+ if (stream != null) {
16
+ stream.end();
17
+ stream = null;
18
+ }
19
+ if (!dirEnsured) {
20
+ fs.mkdirSync(outDir, { recursive: true });
21
+ dirEnsured = true;
22
+ }
23
+ const filePath = resolveLogFilePath(outDir, dateStr, maxSize);
24
+ try {
25
+ currentSize = fs.statSync(filePath).size;
26
+ }
27
+ catch {
28
+ currentSize = 0;
29
+ }
30
+ stream = fs.createWriteStream(filePath, { flags: "a" });
31
+ currentDate = dateStr;
32
+ }
33
+ return {
34
+ log(logObj, _ctx) {
35
+ const entry = {
36
+ time: logObj.date.toISOString(),
37
+ level: logObj.type.toUpperCase(),
38
+ };
39
+ if (logObj.tag) {
40
+ entry["tag"] = logObj.tag;
41
+ }
42
+ const msgs = [];
43
+ for (const a of logObj.args) {
44
+ if (a instanceof Error) {
45
+ entry["err"] = { message: a.message, stack: a.stack };
46
+ }
47
+ else {
48
+ msgs.push(typeof a === "string" ? a : String(a));
49
+ }
50
+ }
51
+ if (msgs.length > 0) {
52
+ entry["msg"] = msgs.join(" ");
53
+ }
54
+ const line = JSON.stringify(entry) + "\n";
55
+ const date = logObj.date;
56
+ const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
57
+ if (dateStr !== currentDate || currentSize + line.length >= maxSize) {
58
+ rotate(dateStr);
59
+ }
60
+ stream.write(line);
61
+ currentSize += line.length;
62
+ if (lastCleanDate !== dateStr) {
63
+ lastCleanDate = dateStr;
64
+ cleanOldFiles(outDir, maxDays);
65
+ }
66
+ },
67
+ };
68
+ }
69
+ function resolveLogFilePath(outDir, dateStr, maxSize) {
70
+ const basePath = path.join(outDir, `app.${dateStr}.log`);
71
+ if (!fs.existsSync(basePath))
72
+ return basePath;
73
+ if (fs.statSync(basePath).size < maxSize)
74
+ return basePath;
75
+ let seq = 1;
76
+ while (true) {
77
+ const seqPath = path.join(outDir, `app.${dateStr}.${seq}.log`);
78
+ if (!fs.existsSync(seqPath))
79
+ return seqPath;
80
+ if (fs.statSync(seqPath).size < maxSize)
81
+ return seqPath;
82
+ seq++;
83
+ }
84
+ }
85
+ function cleanOldFiles(outDir, maxDays) {
86
+ let entries;
87
+ try {
88
+ entries = fs.readdirSync(outDir);
89
+ }
90
+ catch {
91
+ return;
92
+ }
93
+ const cutoff = new Date();
94
+ cutoff.setDate(cutoff.getDate() - maxDays);
95
+ const cutoffStr = `${cutoff.getFullYear()}-${String(cutoff.getMonth() + 1).padStart(2, "0")}-${String(cutoff.getDate()).padStart(2, "0")}`;
96
+ for (const entry of entries) {
97
+ const match = /^app\.(\d{4}-\d{2}-\d{2})(?:\.\d+)?\.log$/.exec(entry);
98
+ if (match != null && match[1] < cutoffStr) {
99
+ try {
100
+ fs.unlinkSync(path.join(outDir, entry));
101
+ }
102
+ catch {
103
+ // ignore
104
+ }
105
+ }
106
+ }
107
+ }
108
+ //# sourceMappingURL=file-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-reporter.js","sourceRoot":"","sources":["../../../src/features/consola/file-reporter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AASxB,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAC1C,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,MAAM,UAAU,kBAAkB,CAAC,OAA6B;IAC9D,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,gBAAgB,CAAC;IACrD,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,gBAAgB,CAAC;IACrD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;IAEpD,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,MAAM,GAA0B,IAAI,CAAC;IACzC,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,aAAa,GAAG,EAAE,CAAC;IAEvB,SAAS,MAAM,CAAC,OAAe;QAC7B,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;YACnB,MAAM,CAAC,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QAED,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1C,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QAED,MAAM,QAAQ,GAAG,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC9D,IAAI,CAAC;YACH,WAAW,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,WAAW,GAAG,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACxD,WAAW,GAAG,OAAO,CAAC;IACxB,CAAC;IAED,OAAO;QACL,GAAG,CAAC,MAAiB,EAAE,IAAiC;YACtD,MAAM,KAAK,GAA4B;gBACrC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;gBAC/B,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE;aACjC,CAAC;YACF,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;gBACf,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC;YAC5B,CAAC;YAED,MAAM,IAAI,GAAa,EAAE,CAAC;YAC1B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAC5B,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;oBACvB,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;gBACxD,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBACnD,CAAC;YACH,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpB,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChC,CAAC;YAED,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;YAE1C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;YACzB,MAAM,OAAO,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;YAEnI,IAAI,OAAO,KAAK,WAAW,IAAI,WAAW,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,EAAE,CAAC;gBACpE,MAAM,CAAC,OAAO,CAAC,CAAC;YAClB,CAAC;YAED,MAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACpB,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC;YAE3B,IAAI,aAAa,KAAK,OAAO,EAAE,CAAC;gBAC9B,aAAa,GAAG,OAAO,CAAC;gBACxB,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAc,EAAE,OAAe,EAAE,OAAe;IAC1E,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,OAAO,MAAM,CAAC,CAAC;IAEzD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC9C,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,GAAG,OAAO;QAAE,OAAO,QAAQ,CAAC;IAE1D,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,OAAO,IAAI,GAAG,MAAM,CAAC,CAAC;QAC/D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,OAAO,CAAC;QAC5C,IAAI,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,GAAG,OAAO;YAAE,OAAO,OAAO,CAAC;QACxD,GAAG,EAAE,CAAC;IACR,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,MAAc,EAAE,OAAe;IACpD,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;IAC1B,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAE3I,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,2CAA2C,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtE,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,CAAC;YAC1C,IAAI,CAAC;gBACH,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;YAC1C,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { ConsolaReporter, LogObject, ConsolaOptions } from "consola";
2
+ export declare class PrettyReporter implements ConsolaReporter {
3
+ log(logObj: LogObject, ctx: {
4
+ options: ConsolaOptions;
5
+ }): void;
6
+ private _formatLogObj;
7
+ private _formatArgs;
8
+ private _formatError;
9
+ private _formatStack;
10
+ private _formatIcon;
11
+ private _formatDate;
12
+ private _formatBox;
13
+ }
14
+ //# sourceMappingURL=pretty-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pretty-reporter.d.ts","sourceRoot":"","sources":["../../../src/features/consola/pretty-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AA4E1E,qBAAa,cAAe,YAAW,eAAe;IACpD,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE;QAAE,OAAO,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI;IAe9D,OAAO,CAAC,aAAa;IA4BrB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,UAAU;CAMnB"}
@@ -0,0 +1,139 @@
1
+ import { formatWithOptions } from "node:util";
2
+ import { sep } from "node:path";
3
+ import { env } from "@simplysm/core-common";
4
+ // -- Constants ----------------------------------------------------------------
5
+ const TYPE_ICONS = {
6
+ error: "\u2716",
7
+ fatal: "\u2716",
8
+ ready: "\u2714",
9
+ warn: "\u26A0",
10
+ info: "\u2139",
11
+ success: "\u2714",
12
+ debug: "\u2699",
13
+ trace: "\u2192",
14
+ fail: "\u2716",
15
+ start: "\u25D0",
16
+ log: "",
17
+ };
18
+ const TYPE_COLORS = {
19
+ info: "cyan",
20
+ fail: "red",
21
+ success: "green",
22
+ ready: "green",
23
+ start: "magenta",
24
+ };
25
+ const LEVEL_COLORS = {
26
+ 0: "red",
27
+ 1: "yellow",
28
+ };
29
+ const ANSI_CODES = {
30
+ gray: "\x1b[90m",
31
+ red: "\x1b[31m",
32
+ green: "\x1b[32m",
33
+ yellow: "\x1b[33m",
34
+ cyan: "\x1b[36m",
35
+ magenta: "\x1b[35m",
36
+ };
37
+ const ANSI_RESET = "\x1b[0m";
38
+ // -- Helpers ------------------------------------------------------------------
39
+ function colorize(color, text, enabled) {
40
+ if (!enabled)
41
+ return text;
42
+ return `${ANSI_CODES[color]}${text}${ANSI_RESET}`;
43
+ }
44
+ function writeStream(data, stream) {
45
+ const s = stream;
46
+ const write = s.__write ?? s.write;
47
+ write.call(stream, data);
48
+ }
49
+ function detectColorSupport() {
50
+ if (env("NO_COLOR") != null)
51
+ return false;
52
+ if (env("FORCE_COLOR") != null)
53
+ return true;
54
+ if (process.stdout.isTTY === true)
55
+ return true;
56
+ return process.platform === "win32";
57
+ }
58
+ export class PrettyReporter {
59
+ log(logObj, ctx) {
60
+ const opts = {
61
+ ...ctx.options.formatOptions,
62
+ colors: detectColorSupport(),
63
+ };
64
+ const line = this._formatLogObj(logObj, opts);
65
+ const stream = logObj.level < 2
66
+ ? ctx.options.stderr ?? process.stderr
67
+ : ctx.options.stdout ?? process.stdout;
68
+ writeStream(line + "\n", stream);
69
+ }
70
+ _formatLogObj(logObj, opts) {
71
+ const formattedArgs = this._formatArgs(logObj.args, opts);
72
+ const [message, ...additional] = formattedArgs.split("\n");
73
+ if (logObj.type === "box") {
74
+ return this._formatBox(logObj, formattedArgs);
75
+ }
76
+ const tag = logObj.tag !== "" ? colorize("gray", `[${logObj.tag}]`, opts.colors) : "";
77
+ const icon = this._formatIcon(logObj, opts.colors);
78
+ const date = this._formatDate(logObj.date, opts);
79
+ const coloredDate = date !== "" ? colorize("gray", date, opts.colors) : "";
80
+ let fullLine = [tag, icon, message, coloredDate].filter(Boolean).join(" ");
81
+ if (additional.length > 0) {
82
+ fullLine += "\n" + additional.join("\n");
83
+ }
84
+ if (logObj.type === "trace") {
85
+ const err = new Error("Trace: " + logObj.message);
86
+ fullLine += this._formatStack(err.stack ?? "", err.message);
87
+ }
88
+ const isBadge = logObj.badge ?? logObj.level < 2;
89
+ return isBadge ? "\n" + fullLine + "\n" : fullLine;
90
+ }
91
+ _formatArgs(args, opts) {
92
+ const processed = args.map((arg) => {
93
+ if (arg != null && typeof arg === "object" && typeof arg.stack === "string") {
94
+ return this._formatError(arg, opts);
95
+ }
96
+ return arg;
97
+ });
98
+ return formatWithOptions({ colors: opts.colors, compact: opts.compact }, ...processed);
99
+ }
100
+ _formatError(err, opts) {
101
+ const message = err.message;
102
+ const stack = err.stack != null ? this._formatStack(err.stack, message, opts) : "";
103
+ const level = opts.errorLevel ?? 0;
104
+ const prefix = level > 0 ? `${" ".repeat(level)}[cause]: ` : "";
105
+ const cause = err.cause instanceof Error
106
+ ? "\n\n" + this._formatError(err.cause, { ...opts, errorLevel: level + 1 })
107
+ : "";
108
+ return prefix + message + "\n" + stack + cause;
109
+ }
110
+ _formatStack(stack, message, opts) {
111
+ const cwd = process.cwd() + sep;
112
+ const indent = " ".repeat((opts?.errorLevel ?? 0) + 1);
113
+ const lines = stack
114
+ .split("\n")
115
+ .splice(message.split("\n").length)
116
+ .map((l) => l.trim().replace("file://", "").replace(cwd, ""));
117
+ return `\n${indent}` + lines.map((l) => ` ${l}`).join(`\n${indent}`);
118
+ }
119
+ _formatIcon(logObj, useColors) {
120
+ const icon = TYPE_ICONS[logObj.type] ?? "";
121
+ if (icon === "")
122
+ return "";
123
+ const color = TYPE_COLORS[logObj.type] ?? LEVEL_COLORS[logObj.level] ?? "gray";
124
+ return colorize(color, icon, useColors);
125
+ }
126
+ _formatDate(date, opts) {
127
+ if (!opts.date)
128
+ return "";
129
+ const base = date.toLocaleTimeString();
130
+ return `${base}.${String(date.getMilliseconds()).padStart(3, "0")}`;
131
+ }
132
+ _formatBox(logObj, message) {
133
+ const tag = logObj.tag !== "" ? `[${logObj.tag}]` : "";
134
+ const title = logObj.title;
135
+ const lines = [tag, title, ...message.split("\n")].filter(Boolean);
136
+ return "\n" + lines.map((l) => ` > ${l}`).join("\n") + "\n";
137
+ }
138
+ }
139
+ //# sourceMappingURL=pretty-reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pretty-reporter.js","sourceRoot":"","sources":["../../../src/features/consola/pretty-reporter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAE5C,gFAAgF;AAEhF,MAAM,UAAU,GAA2B;IACzC,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,QAAQ;IACjB,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,QAAQ;IACf,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,QAAQ;IACf,GAAG,EAAE,EAAE;CACR,CAAC;AAIF,MAAM,WAAW,GAA0C;IACzD,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,SAAS;CACjB,CAAC;AAEF,MAAM,YAAY,GAA0C;IAC1D,CAAC,EAAE,KAAK;IACR,CAAC,EAAE,QAAQ;CACZ,CAAC;AAEF,MAAM,UAAU,GAA8B;IAC5C,IAAI,EAAE,UAAU;IAChB,GAAG,EAAE,UAAU;IACf,KAAK,EAAE,UAAU;IACjB,MAAM,EAAE,UAAU;IAClB,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,UAAU;CACpB,CAAC;AAEF,MAAM,UAAU,GAAG,SAAS,CAAC;AAE7B,gFAAgF;AAEhF,SAAS,QAAQ,CAAC,KAAgB,EAAE,IAAY,EAAE,OAAgB;IAChE,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,UAAU,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,MAA6B;IAC9D,MAAM,CAAC,GAAG,MAAmE,CAAC;IAC9E,MAAM,KAAK,GAAG,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,CAAC;IACnC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,kBAAkB;IACzB,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1C,IAAI,GAAG,CAAC,aAAa,CAAC,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC5C,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AACtC,CAAC;AAWD,MAAM,OAAO,cAAc;IACzB,GAAG,CAAC,MAAiB,EAAE,GAAgC;QACrD,MAAM,IAAI,GAAe;YACvB,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa;YAC5B,MAAM,EAAE,kBAAkB,EAAE;SAC7B,CAAC;QAEF,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC9C,MAAM,MAAM,GACV,MAAM,CAAC,KAAK,GAAG,CAAC;YACd,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM;YACtC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;QAE3C,WAAW,CAAC,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAEO,aAAa,CAAC,MAAiB,EAAE,IAAgB;QACvD,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1D,MAAM,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE3D,IAAI,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACjD,MAAM,WAAW,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAE3E,IAAI,QAAQ,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE3E,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,QAAQ,IAAI,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAClD,QAAQ,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,OAAO,GAAI,MAA0C,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;QACtF,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;IACrD,CAAC;IAEO,WAAW,CAAC,IAAe,EAAE,IAAgB;QACnD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YACjC,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAQ,GAAa,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACvF,OAAO,IAAI,CAAC,YAAY,CAAC,GAAY,EAAE,IAAI,CAAC,CAAC;YAC/C,CAAC;YACD,OAAO,GAAG,CAAC;QACb,CAAC,CAAC,CAAC;QACH,OAAO,iBAAiB,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,SAAS,CAAC,CAAC;IACzF,CAAC;IAEO,YAAY,CAAC,GAAU,EAAE,IAAgB;QAC/C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QAC5B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,MAAM,KAAK,GACT,GAAG,CAAC,KAAK,YAAY,KAAK;YACxB,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC;YAC3E,CAAC,CAAC,EAAE,CAAC;QACT,OAAO,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC;IACjD,CAAC;IAEO,YAAY,CAAC,KAAa,EAAE,OAAe,EAAE,IAAiB;QACpE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,UAAU,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,KAAK;aAChB,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;aAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;QAChE,OAAO,KAAK,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;IACxE,CAAC;IAEO,WAAW,CAAC,MAAiB,EAAE,SAAkB;QACvD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,IAAI,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAc,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC;QAC1F,OAAO,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IAC1C,CAAC;IAEO,WAAW,CAAC,IAAU,EAAE,IAAgB;QAC9C,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACvC,OAAO,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IACtE,CAAC;IAEO,UAAU,CAAC,MAAiB,EAAE,OAAe;QACnD,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,MAAM,KAAK,GAAI,MAAyC,CAAC,KAAK,CAAC;QAC/D,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnE,OAAO,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9D,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ import type { ConsolaReporter } from "consola";
2
+ export declare function withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter;
3
+ export interface SetupConsolaOptions {
4
+ cli?: boolean;
5
+ }
6
+ export declare function setupConsola(opts?: SetupConsolaOptions): void;
7
+ //# sourceMappingURL=setup-consola.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-consola.d.ts","sourceRoot":"","sources":["../../../src/features/consola/setup-consola.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,eAAe,EAAa,MAAM,SAAS,CAAC;AAM1E,wBAAgB,YAAY,CAAC,QAAQ,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,eAAe,CAOzF;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAoB7D"}
@@ -0,0 +1,35 @@
1
+ import consola, { LogLevels } from "consola";
2
+ import { env, parseBoolEnv } from "@simplysm/core-common";
3
+ import { PrettyReporter } from "./pretty-reporter.js";
4
+ import { createFileReporter } from "./file-reporter.js";
5
+ export function withMaxLevel(reporter, maxLevel) {
6
+ return {
7
+ log(logObj, ctx) {
8
+ if (logObj.level > maxLevel)
9
+ return;
10
+ reporter.log(logObj, ctx);
11
+ },
12
+ };
13
+ }
14
+ export function setupConsola(opts) {
15
+ if (!opts?.cli && !parseBoolEnv(env("DEV"))) {
16
+ // prod: debug 포함 FileReporter
17
+ consola.level = LogLevels.debug;
18
+ consola.options.reporters = [createFileReporter()];
19
+ return;
20
+ }
21
+ if (parseBoolEnv(env("SD_DEBUG"))) {
22
+ // dev + SD_DEBUG: debug 포함 PrettyReporter
23
+ consola.level = LogLevels.debug;
24
+ consola.options.reporters = [new PrettyReporter()];
25
+ }
26
+ else {
27
+ // dev: debug 포함 FileReporter + debug 비포함 PrettyReporter
28
+ consola.level = LogLevels.debug;
29
+ consola.options.reporters = [
30
+ createFileReporter(),
31
+ withMaxLevel(new PrettyReporter(), LogLevels.info),
32
+ ];
33
+ }
34
+ }
35
+ //# sourceMappingURL=setup-consola.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-consola.js","sourceRoot":"","sources":["../../../src/features/consola/setup-consola.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,UAAU,YAAY,CAAC,QAAyB,EAAE,QAAgB;IACtE,OAAO;QACL,GAAG,CAAC,MAAiB,EAAE,GAAgC;YACrD,IAAI,MAAM,CAAC,KAAK,GAAG,QAAQ;gBAAE,OAAO;YACpC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC5B,CAAC;KACF,CAAC;AACJ,CAAC;AAMD,MAAM,UAAU,YAAY,CAAC,IAA0B;IACrD,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QAC5C,8BAA8B;QAC9B,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;QAChC,OAAO,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IAED,IAAI,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QAClC,0CAA0C;QAC1C,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;QAChC,OAAO,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,IAAI,cAAc,EAAE,CAAC,CAAC;IACrD,CAAC;SAAM,CAAC;QACN,wDAAwD;QACxD,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC;QAChC,OAAO,CAAC,OAAO,CAAC,SAAS,GAAG;YAC1B,kBAAkB,EAAE;YACpB,YAAY,CAAC,IAAI,cAAc,EAAE,EAAE,SAAS,CAAC,IAAI,CAAC;SACnD,CAAC;IACJ,CAAC;AACH,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,9 @@ export * as cpx from "./utils/cp";
2
2
  export * as fsx from "./utils/fs";
3
3
  export * as pathx from "./utils/path";
4
4
  export * from "./features/fs-watcher";
5
+ export * from "./features/consola/pretty-reporter";
6
+ export * from "./features/consola/file-reporter";
7
+ export * from "./features/consola/setup-consola";
5
8
  export * from "./worker/types";
6
9
  export * from "./worker/worker";
7
10
  export * from "./worker/create-worker";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,cAAc,uBAAuB,CAAC;AAGtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oCAAoC,CAAC;AACnD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AAGjD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,9 @@ export * as fsx from "./utils/fs.js";
4
4
  export * as pathx from "./utils/path.js";
5
5
  // 기능
6
6
  export * from "./features/fs-watcher.js";
7
+ export * from "./features/consola/pretty-reporter.js";
8
+ export * from "./features/consola/file-reporter.js";
9
+ export * from "./features/consola/setup-consola.js";
7
10
  // 워커
8
11
  export * from "./worker/types.js";
9
12
  export * from "./worker/worker.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AACP,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,KAAK;AACL,cAAc,uBAAuB,CAAC;AAEtC,KAAK;AACL,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO;AACP,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,GAAG,MAAM,YAAY,CAAC;AAClC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,KAAK;AACL,cAAc,uBAAuB,CAAC;AACtC,cAAc,oCAAoC,CAAC;AACnD,cAAc,kCAAkC,CAAC;AACjD,cAAc,kCAAkC,CAAC;AAEjD,KAAK;AACL,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC"}
package/dist/utils/cp.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { execSync as cpExecSync, spawn as cpSpawn, spawnSync as cpSpawnSync } from "child_process";
2
- import { bytes } from "@simplysm/core-common";
2
+ import { bytes, env } from "@simplysm/core-common";
3
3
  const CODE_PAGE_MAP = {
4
4
  65001: "utf-8",
5
5
  949: "euc-kr",
@@ -31,7 +31,7 @@ export function getSystemEncoding() {
31
31
  }
32
32
  }
33
33
  else {
34
- const lang = process.env["LANG"] ?? process.env["LC_ALL"] ?? "";
34
+ const lang = env("LANG") ?? env("LC_ALL") ?? "";
35
35
  const dotIndex = lang.indexOf(".");
36
36
  if (dotIndex >= 0) {
37
37
  let encoding = lang.slice(dotIndex + 1).split("@")[0].toLowerCase();
@@ -1 +1 @@
1
- {"version":3,"file":"cp.js","sourceRoot":"","sources":["../../src/utils/cp.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cp.js","sourceRoot":"","sources":["../../src/utils/cp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,141 @@
1
+ # Consola
2
+
3
+ Consola logging utilities for environment-aware reporter configuration. Provides a terminal pretty-printer, a file-based JSON logger with rotation, and a one-call setup function.
4
+
5
+ ```ts
6
+ import {
7
+ setupConsola,
8
+ withMaxLevel,
9
+ PrettyReporter,
10
+ createFileReporter,
11
+ } from "@simplysm/core-node";
12
+ import type { SetupConsolaOptions, FileReporterOptions } from "@simplysm/core-node";
13
+ ```
14
+
15
+ ## Types
16
+
17
+ ### SetupConsolaOptions
18
+
19
+ ```ts
20
+ interface SetupConsolaOptions {
21
+ cli?: boolean;
22
+ }
23
+ ```
24
+
25
+ | Field | Type | Description |
26
+ |-------|------|-------------|
27
+ | `cli` | `boolean` | When `true`, always use `PrettyReporter` regardless of environment (for CLI tools) |
28
+
29
+ ### FileReporterOptions
30
+
31
+ ```ts
32
+ interface FileReporterOptions {
33
+ maxSize?: number;
34
+ maxDays?: number;
35
+ }
36
+ ```
37
+
38
+ | Field | Type | Description |
39
+ |-------|------|-------------|
40
+ | `maxSize` | `number` | Maximum size per log file in bytes. Default: `20 * 1024 * 1024` (20 MB). When exceeded, a new sequenced file is created (e.g., `app.2026-04-03.1.log`). |
41
+ | `maxDays` | `number` | Number of days to retain log files. Default: `14`. Files older than this are deleted on the next log write. |
42
+
43
+ ## PrettyReporter
44
+
45
+ ```ts
46
+ class PrettyReporter implements ConsolaReporter {
47
+ log(logObj: LogObject, ctx: { options: ConsolaOptions }): void;
48
+ }
49
+ ```
50
+
51
+ A terminal-oriented consola reporter. Formats log output with:
52
+ - Type-specific icons (e.g., checkmark for success, cross for error, gear for debug)
53
+ - ANSI color coding based on log type and level (respects `NO_COLOR`/`FORCE_COLOR` env vars)
54
+ - Error stack trace formatting with relative paths and cause chain support
55
+ - Badge-style formatting (extra newlines) for error/fatal level messages
56
+ - Box formatting for `box` type log entries
57
+ - Timestamp display when `formatOptions.date` is enabled
58
+
59
+ Errors at level < 2 are written to stderr; all others to stdout.
60
+
61
+ ## Functions
62
+
63
+ ### setupConsola
64
+
65
+ ```ts
66
+ function setupConsola(opts?: SetupConsolaOptions): void
67
+ ```
68
+
69
+ Configure the global `consola` instance with appropriate reporters based on the runtime environment. Sets the log level to `debug` in all cases.
70
+
71
+ | Environment | Behavior |
72
+ |-------------|----------|
73
+ | Production (`env.DEV` is falsy, `cli` not set) | `FileReporter` only -- all logs (including debug) go to `.logs/` |
74
+ | Development + `SD_DEBUG` env var | `PrettyReporter` only -- all logs (including debug) to terminal |
75
+ | Development (default) | `FileReporter` (all levels) + `PrettyReporter` (info and below only) |
76
+ | `cli: true` | Same as `SD_DEBUG` mode -- always uses `PrettyReporter` |
77
+
78
+ | Parameter | Type | Description |
79
+ |-----------|------|-------------|
80
+ | `opts` | `SetupConsolaOptions` | Optional configuration |
81
+
82
+ ### createFileReporter
83
+
84
+ ```ts
85
+ function createFileReporter(options?: FileReporterOptions): ConsolaReporter
86
+ ```
87
+
88
+ Create a file-based consola reporter that writes JSON-line entries to `.logs/` directory.
89
+
90
+ | Parameter | Type | Description |
91
+ |-----------|------|-------------|
92
+ | `options` | `FileReporterOptions` | Optional size and retention settings |
93
+
94
+ File naming: `app.YYYY-MM-DD.log`, with sequential suffix (`app.YYYY-MM-DD.1.log`) when size limit is reached.
95
+
96
+ Each log entry is a single JSON line with fields:
97
+
98
+ | Field | Type | Description |
99
+ |-------|------|-------------|
100
+ | `time` | `string` | ISO 8601 timestamp |
101
+ | `level` | `string` | Uppercase log type (e.g., `"INFO"`, `"ERROR"`) |
102
+ | `tag` | `string` | Log tag (omitted if empty) |
103
+ | `msg` | `string` | Concatenated message arguments (omitted if none) |
104
+ | `err` | `{ message, stack }` | Error details (present when an Error argument is logged) |
105
+
106
+ Log rotation: when the date changes or file size exceeds `maxSize`, a new file is opened. Files older than `maxDays` are automatically cleaned up.
107
+
108
+ ### withMaxLevel
109
+
110
+ ```ts
111
+ function withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter
112
+ ```
113
+
114
+ Wrap a consola reporter to suppress log entries above a specified level. Log entries with `logObj.level > maxLevel` are silently dropped.
115
+
116
+ | Parameter | Type | Description |
117
+ |-----------|------|-------------|
118
+ | `reporter` | `ConsolaReporter` | The reporter to wrap |
119
+ | `maxLevel` | `number` | Maximum log level to pass through (e.g., `LogLevels.info` for info and below) |
120
+
121
+ ## Usage
122
+
123
+ ```ts
124
+ import { setupConsola, createFileReporter, PrettyReporter, withMaxLevel } from "@simplysm/core-node";
125
+ import consola, { LogLevels } from "consola";
126
+
127
+ // One-call setup (recommended)
128
+ setupConsola();
129
+
130
+ // Manual configuration example
131
+ consola.level = LogLevels.debug;
132
+ consola.options.reporters = [
133
+ createFileReporter({ maxSize: 10 * 1024 * 1024, maxDays: 7 }),
134
+ withMaxLevel(new PrettyReporter(), LogLevels.info),
135
+ ];
136
+
137
+ // Then use consola as usual
138
+ consola.info("Server started on port 3000");
139
+ consola.debug("Connection pool initialized");
140
+ consola.error(new Error("Database connection failed"));
141
+ ```
package/docs/cpx.md CHANGED
@@ -9,38 +9,10 @@ import { cpx } from "@simplysm/core-node";
9
9
 
10
10
  ## Types
11
11
 
12
- ### ExecOptions
12
+ ### SpawnResult
13
13
 
14
14
  ```ts
15
- interface ExecOptions {
16
- cwd?: string;
17
- env?: Record<string, string>;
18
- stdio?: "pipe" | "inherit";
19
- shell?: boolean;
20
- reject?: boolean;
21
- }
22
- ```
23
-
24
- | Field | Type | Default | Description |
25
- |-------|------|---------|-------------|
26
- | `cwd` | `string` | `undefined` | Working directory for the child process |
27
- | `env` | `Record<string, string>` | `undefined` | Extra environment variables (merged with `process.env`) |
28
- | `stdio` | `"pipe" \| "inherit"` | `"pipe"` | `"pipe"` captures stdout/stderr; `"inherit"` passes them through |
29
- | `shell` | `boolean` | `false` | Run the command in a shell |
30
- | `reject` | `boolean` | `true` | If `false`, the promise resolves even on non-zero exit codes |
31
-
32
- ### ExecSyncOptions
33
-
34
- ```ts
35
- type ExecSyncOptions = Omit<ExecOptions, "reject">
36
- ```
37
-
38
- Same as `ExecOptions` without the `reject` field. `execSync` always returns a result regardless of exit code.
39
-
40
- ### ExecResult
41
-
42
- ```ts
43
- interface ExecResult {
15
+ interface SpawnResult {
44
16
  stdout: string;
45
17
  stderr: string;
46
18
  exitCode: number;
@@ -49,27 +21,27 @@ interface ExecResult {
49
21
 
50
22
  | Field | Type | Description |
51
23
  |-------|------|-------------|
52
- | `stdout` | `string` | Captured standard output (empty when `stdio: "inherit"`) |
53
- | `stderr` | `string` | Captured standard error (empty when `stdio: "inherit"`) |
24
+ | `stdout` | `string` | Captured standard output (empty when stdio is not `"pipe"`) |
25
+ | `stderr` | `string` | Captured standard error (empty when stdio is not `"pipe"`) |
54
26
  | `exitCode` | `number` | Process exit code |
55
27
 
56
- ## ExecProcess
28
+ ## SpawnProcess
57
29
 
58
30
  ```ts
59
- class ExecProcess implements PromiseLike<ExecResult> {
31
+ class SpawnProcess implements PromiseLike<SpawnResult> {
60
32
  get pid(): number | undefined;
61
33
  then<TResult1, TResult2>(
62
- onfulfilled?: ((value: ExecResult) => TResult1 | PromiseLike<TResult1>) | null,
34
+ onfulfilled?: ((value: SpawnResult) => TResult1 | PromiseLike<TResult1>) | null,
63
35
  onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
64
36
  ): Promise<TResult1 | TResult2>;
65
37
  catch<TResult>(
66
38
  onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
67
- ): Promise<ExecResult | TResult>;
39
+ ): Promise<SpawnResult | TResult>;
68
40
  kill(signal?: NodeJS.Signals | number): boolean;
69
41
  }
70
42
  ```
71
43
 
72
- A `PromiseLike<ExecResult>` wrapper around `ChildProcess`. Can be `await`ed for the result or `kill()`ed to terminate the process.
44
+ A `PromiseLike<SpawnResult>` wrapper around `ChildProcess`. Can be `await`ed for the result or `kill()`ed to terminate the process.
73
45
 
74
46
  | Property/Method | Description |
75
47
  |-----------------|-------------|
@@ -80,35 +52,61 @@ A `PromiseLike<ExecResult>` wrapper around `ChildProcess`. Can be `await`ed for
80
52
 
81
53
  ## Functions
82
54
 
83
- ### exec
55
+ ### spawn
84
56
 
85
57
  ```ts
86
- function exec(cmd: string, args: string[], options?: ExecOptions): ExecProcess
58
+ function spawn(
59
+ cmd: string,
60
+ args: string[],
61
+ options?: SpawnOptions & { reject?: boolean },
62
+ ): SpawnProcess
87
63
  ```
88
64
 
89
- Spawn a child process asynchronously. Returns an `ExecProcess` that can be awaited or killed.
65
+ Spawn a child process asynchronously. Returns a `SpawnProcess` that can be awaited or killed. Options extend Node.js `SpawnOptions` with an additional `reject` field.
90
66
 
91
67
  | Parameter | Type | Description |
92
68
  |-----------|------|-------------|
93
69
  | `cmd` | `string` | Command to execute |
94
70
  | `args` | `string[]` | Command arguments |
95
- | `options` | `ExecOptions` | Execution options |
71
+ | `options` | `SpawnOptions & { reject?: boolean }` | Node.js spawn options plus `reject` (default `true`) |
96
72
 
97
- By default, rejects if exit code is non-zero. Set `options.reject: false` to always resolve. Output is decoded using system encoding detection (UTF-8 with fallback).
73
+ By default, rejects if exit code is non-zero. Set `options.reject: false` to always resolve. The `env` option is merged with `process.env`. Output is decoded using system encoding detection (UTF-8 with fallback).
98
74
 
99
- ### execSync
75
+ ### spawnSync
100
76
 
101
77
  ```ts
102
- function execSync(cmd: string, args: string[], options?: ExecSyncOptions): ExecResult
78
+ function spawnSync(
79
+ cmd: string,
80
+ args: string[],
81
+ options?: SpawnSyncOptions & { reject?: boolean },
82
+ ): SpawnResult
103
83
  ```
104
84
 
105
- Spawn a child process synchronously. Always returns an `ExecResult` regardless of exit code.
85
+ Spawn a child process synchronously. Options extend Node.js `SpawnSyncOptions` with an additional `reject` field.
106
86
 
107
87
  | Parameter | Type | Description |
108
88
  |-----------|------|-------------|
109
89
  | `cmd` | `string` | Command to execute |
110
90
  | `args` | `string[]` | Command arguments |
111
- | `options` | `ExecSyncOptions` | Execution options |
91
+ | `options` | `SpawnSyncOptions & { reject?: boolean }` | Node.js spawn sync options plus `reject` (default `true`) |
92
+
93
+ By default, throws if exit code is non-zero. Set `options.reject: false` to always return the result.
94
+
95
+ ### resolveStdioPipe
96
+
97
+ ```ts
98
+ function resolveStdioPipe(
99
+ stdio: SpawnOptions["stdio"],
100
+ ): { stdout: boolean; stderr: boolean }
101
+ ```
102
+
103
+ Determine whether stdout and stderr are piped based on the `stdio` option value.
104
+
105
+ | Parameter | Type | Description |
106
+ |-----------|------|-------------|
107
+ | `stdio` | `SpawnOptions["stdio"]` | The stdio configuration (string or array) |
108
+
109
+ Returns `{ stdout: true, stderr: true }` when `stdio` is `"pipe"` or `undefined`. For array form, checks `stdio[1]` and `stdio[2]` individually.
112
110
 
113
111
  ### codePageToEncoding
114
112
 
@@ -155,19 +153,19 @@ Decode a `Uint8Array` to a string. First tries UTF-8 (with `fatal: true`); if th
155
153
  import { cpx } from "@simplysm/core-node";
156
154
 
157
155
  // Basic execution
158
- const result = await cpx.exec("git", ["status"], { cwd: "/project" });
156
+ const result = await cpx.spawn("git", ["status"], { cwd: "/project" });
159
157
  // result.stdout, result.stderr, result.exitCode
160
158
 
161
159
  // Inherit stdio
162
- await cpx.exec("make", ["build"], { stdio: "inherit" });
160
+ await cpx.spawn("make", ["build"], { stdio: "inherit" });
163
161
 
164
162
  // Don't reject on failure
165
- const { exitCode } = await cpx.exec("test-cmd", [], { reject: false });
163
+ const { exitCode } = await cpx.spawn("test-cmd", [], { reject: false });
166
164
 
167
165
  // Kill a long-running process
168
- const proc = cpx.exec("long-cmd", []);
166
+ const proc = cpx.spawn("long-cmd", []);
169
167
  proc.kill();
170
168
 
171
169
  // Synchronous execution
172
- const syncResult = cpx.execSync("node", ["--version"]);
170
+ const syncResult = cpx.spawnSync("node", ["--version"]);
173
171
  ```
package/docs/fsx.md CHANGED
@@ -111,21 +111,21 @@ async function read(targetPath: string): Promise<string>
111
111
 
112
112
  Read a file as a UTF-8 string (async).
113
113
 
114
- ### readBufferSync
114
+ ### readBytesSync
115
115
 
116
116
  ```ts
117
- function readBufferSync(targetPath: string): Buffer
117
+ function readBytesSync(targetPath: string): Uint8Array
118
118
  ```
119
119
 
120
- Read a file as a Buffer (sync).
120
+ Read a file as a Uint8Array (sync).
121
121
 
122
- ### readBuffer
122
+ ### readBytes
123
123
 
124
124
  ```ts
125
- async function readBuffer(targetPath: string): Promise<Buffer>
125
+ async function readBytes(targetPath: string): Promise<Uint8Array>
126
126
  ```
127
127
 
128
- Read a file as a Buffer (async).
128
+ Read a file as a Uint8Array (async).
129
129
 
130
130
  ### readJsonSync
131
131
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/core-node",
3
- "version": "14.0.15",
3
+ "version": "14.0.17",
4
4
  "description": "심플리즘 패키지 - 코어 (node)",
5
5
  "author": "심플리즘",
6
6
  "license": "Apache-2.0",
@@ -25,9 +25,9 @@
25
25
  "glob": "^13.0.6",
26
26
  "minimatch": "^10.2.5",
27
27
  "tsx": "^4.21.0",
28
- "@simplysm/core-common": "14.0.15"
28
+ "@simplysm/core-common": "14.0.17"
29
29
  },
30
30
  "devDependencies": {
31
- "@types/node": "^20.19.37"
31
+ "@types/node": "^20.19.39"
32
32
  }
33
33
  }
@@ -0,0 +1,127 @@
1
+ import type { ConsolaOptions, ConsolaReporter, LogObject } from "consola";
2
+ import fs from "fs";
3
+ import path from "path";
4
+
5
+ export interface FileReporterOptions {
6
+ /** @default 20MB */
7
+ maxSize?: number;
8
+ /** @default 14 */
9
+ maxDays?: number;
10
+ }
11
+
12
+ const DEFAULT_MAX_SIZE = 20 * 1024 * 1024;
13
+ const DEFAULT_MAX_DAYS = 14;
14
+
15
+ export function createFileReporter(options?: FileReporterOptions): ConsolaReporter {
16
+ const maxSize = options?.maxSize ?? DEFAULT_MAX_SIZE;
17
+ const maxDays = options?.maxDays ?? DEFAULT_MAX_DAYS;
18
+ const outDir = path.resolve(process.cwd(), ".logs");
19
+
20
+ let dirEnsured = false;
21
+ let stream: fs.WriteStream | null = null;
22
+ let currentDate = "";
23
+ let currentSize = 0;
24
+ let lastCleanDate = "";
25
+
26
+ function rotate(dateStr: string): void {
27
+ if (stream != null) {
28
+ stream.end();
29
+ stream = null;
30
+ }
31
+
32
+ if (!dirEnsured) {
33
+ fs.mkdirSync(outDir, { recursive: true });
34
+ dirEnsured = true;
35
+ }
36
+
37
+ const filePath = resolveLogFilePath(outDir, dateStr, maxSize);
38
+ try {
39
+ currentSize = fs.statSync(filePath).size;
40
+ } catch {
41
+ currentSize = 0;
42
+ }
43
+
44
+ stream = fs.createWriteStream(filePath, { flags: "a" });
45
+ currentDate = dateStr;
46
+ }
47
+
48
+ return {
49
+ log(logObj: LogObject, _ctx: { options: ConsolaOptions }) {
50
+ const entry: Record<string, unknown> = {
51
+ time: logObj.date.toISOString(),
52
+ level: logObj.type.toUpperCase(),
53
+ };
54
+ if (logObj.tag) {
55
+ entry["tag"] = logObj.tag;
56
+ }
57
+
58
+ const msgs: string[] = [];
59
+ for (const a of logObj.args) {
60
+ if (a instanceof Error) {
61
+ entry["err"] = { message: a.message, stack: a.stack };
62
+ } else {
63
+ msgs.push(typeof a === "string" ? a : String(a));
64
+ }
65
+ }
66
+ if (msgs.length > 0) {
67
+ entry["msg"] = msgs.join(" ");
68
+ }
69
+
70
+ const line = JSON.stringify(entry) + "\n";
71
+
72
+ const date = logObj.date;
73
+ const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
74
+
75
+ if (dateStr !== currentDate || currentSize + line.length >= maxSize) {
76
+ rotate(dateStr);
77
+ }
78
+
79
+ stream!.write(line);
80
+ currentSize += line.length;
81
+
82
+ if (lastCleanDate !== dateStr) {
83
+ lastCleanDate = dateStr;
84
+ cleanOldFiles(outDir, maxDays);
85
+ }
86
+ },
87
+ };
88
+ }
89
+
90
+ function resolveLogFilePath(outDir: string, dateStr: string, maxSize: number): string {
91
+ const basePath = path.join(outDir, `app.${dateStr}.log`);
92
+
93
+ if (!fs.existsSync(basePath)) return basePath;
94
+ if (fs.statSync(basePath).size < maxSize) return basePath;
95
+
96
+ let seq = 1;
97
+ while (true) {
98
+ const seqPath = path.join(outDir, `app.${dateStr}.${seq}.log`);
99
+ if (!fs.existsSync(seqPath)) return seqPath;
100
+ if (fs.statSync(seqPath).size < maxSize) return seqPath;
101
+ seq++;
102
+ }
103
+ }
104
+
105
+ function cleanOldFiles(outDir: string, maxDays: number): void {
106
+ let entries: string[];
107
+ try {
108
+ entries = fs.readdirSync(outDir);
109
+ } catch {
110
+ return;
111
+ }
112
+
113
+ const cutoff = new Date();
114
+ cutoff.setDate(cutoff.getDate() - maxDays);
115
+ const cutoffStr = `${cutoff.getFullYear()}-${String(cutoff.getMonth() + 1).padStart(2, "0")}-${String(cutoff.getDate()).padStart(2, "0")}`;
116
+
117
+ for (const entry of entries) {
118
+ const match = /^app\.(\d{4}-\d{2}-\d{2})(?:\.\d+)?\.log$/.exec(entry);
119
+ if (match != null && match[1] < cutoffStr) {
120
+ try {
121
+ fs.unlinkSync(path.join(outDir, entry));
122
+ } catch {
123
+ // ignore
124
+ }
125
+ }
126
+ }
127
+ }
@@ -0,0 +1,172 @@
1
+ import type { ConsolaReporter, LogObject, ConsolaOptions } from "consola";
2
+ import { formatWithOptions } from "node:util";
3
+ import { sep } from "node:path";
4
+ import { env } from "@simplysm/core-common";
5
+
6
+ // -- Constants ----------------------------------------------------------------
7
+
8
+ const TYPE_ICONS: Record<string, string> = {
9
+ error: "\u2716",
10
+ fatal: "\u2716",
11
+ ready: "\u2714",
12
+ warn: "\u26A0",
13
+ info: "\u2139",
14
+ success: "\u2714",
15
+ debug: "\u2699",
16
+ trace: "\u2192",
17
+ fail: "\u2716",
18
+ start: "\u25D0",
19
+ log: "",
20
+ };
21
+
22
+ type AnsiColor = "gray" | "red" | "green" | "yellow" | "cyan" | "magenta";
23
+
24
+ const TYPE_COLORS: Record<string, AnsiColor | undefined> = {
25
+ info: "cyan",
26
+ fail: "red",
27
+ success: "green",
28
+ ready: "green",
29
+ start: "magenta",
30
+ };
31
+
32
+ const LEVEL_COLORS: Record<number, AnsiColor | undefined> = {
33
+ 0: "red",
34
+ 1: "yellow",
35
+ };
36
+
37
+ const ANSI_CODES: Record<AnsiColor, string> = {
38
+ gray: "\x1b[90m",
39
+ red: "\x1b[31m",
40
+ green: "\x1b[32m",
41
+ yellow: "\x1b[33m",
42
+ cyan: "\x1b[36m",
43
+ magenta: "\x1b[35m",
44
+ };
45
+
46
+ const ANSI_RESET = "\x1b[0m";
47
+
48
+ // -- Helpers ------------------------------------------------------------------
49
+
50
+ function colorize(color: AnsiColor, text: string, enabled: boolean): string {
51
+ if (!enabled) return text;
52
+ return `${ANSI_CODES[color]}${text}${ANSI_RESET}`;
53
+ }
54
+
55
+ function writeStream(data: string, stream: NodeJS.WritableStream): void {
56
+ const s = stream as NodeJS.WritableStream & { __write?: typeof stream.write };
57
+ const write = s.__write ?? s.write;
58
+ write.call(stream, data);
59
+ }
60
+
61
+ function detectColorSupport(): boolean {
62
+ if (env("NO_COLOR") != null) return false;
63
+ if (env("FORCE_COLOR") != null) return true;
64
+ if (process.stdout.isTTY === true) return true;
65
+ return process.platform === "win32";
66
+ }
67
+
68
+ // -- Reporter -----------------------------------------------------------------
69
+
70
+ interface FormatOpts {
71
+ date?: boolean;
72
+ colors: boolean;
73
+ compact?: boolean | number;
74
+ errorLevel?: number;
75
+ }
76
+
77
+ export class PrettyReporter implements ConsolaReporter {
78
+ log(logObj: LogObject, ctx: { options: ConsolaOptions }): void {
79
+ const opts: FormatOpts = {
80
+ ...ctx.options.formatOptions,
81
+ colors: detectColorSupport(),
82
+ };
83
+
84
+ const line = this._formatLogObj(logObj, opts);
85
+ const stream =
86
+ logObj.level < 2
87
+ ? ctx.options.stderr ?? process.stderr
88
+ : ctx.options.stdout ?? process.stdout;
89
+
90
+ writeStream(line + "\n", stream);
91
+ }
92
+
93
+ private _formatLogObj(logObj: LogObject, opts: FormatOpts): string {
94
+ const formattedArgs = this._formatArgs(logObj.args, opts);
95
+ const [message, ...additional] = formattedArgs.split("\n");
96
+
97
+ if (logObj.type === "box") {
98
+ return this._formatBox(logObj, formattedArgs);
99
+ }
100
+
101
+ const tag = logObj.tag !== "" ? colorize("gray", `[${logObj.tag}]`, opts.colors) : "";
102
+ const icon = this._formatIcon(logObj, opts.colors);
103
+ const date = this._formatDate(logObj.date, opts);
104
+ const coloredDate = date !== "" ? colorize("gray", date, opts.colors) : "";
105
+
106
+ let fullLine = [tag, icon, message, coloredDate].filter(Boolean).join(" ");
107
+
108
+ if (additional.length > 0) {
109
+ fullLine += "\n" + additional.join("\n");
110
+ }
111
+
112
+ if (logObj.type === "trace") {
113
+ const err = new Error("Trace: " + logObj.message);
114
+ fullLine += this._formatStack(err.stack ?? "", err.message);
115
+ }
116
+
117
+ const isBadge = (logObj as LogObject & { badge?: boolean }).badge ?? logObj.level < 2;
118
+ return isBadge ? "\n" + fullLine + "\n" : fullLine;
119
+ }
120
+
121
+ private _formatArgs(args: unknown[], opts: FormatOpts): string {
122
+ const processed = args.map((arg) => {
123
+ if (arg != null && typeof arg === "object" && typeof (arg as Error).stack === "string") {
124
+ return this._formatError(arg as Error, opts);
125
+ }
126
+ return arg;
127
+ });
128
+ return formatWithOptions({ colors: opts.colors, compact: opts.compact }, ...processed);
129
+ }
130
+
131
+ private _formatError(err: Error, opts: FormatOpts): string {
132
+ const message = err.message;
133
+ const stack = err.stack != null ? this._formatStack(err.stack, message, opts) : "";
134
+ const level = opts.errorLevel ?? 0;
135
+ const prefix = level > 0 ? `${" ".repeat(level)}[cause]: ` : "";
136
+ const cause =
137
+ err.cause instanceof Error
138
+ ? "\n\n" + this._formatError(err.cause, { ...opts, errorLevel: level + 1 })
139
+ : "";
140
+ return prefix + message + "\n" + stack + cause;
141
+ }
142
+
143
+ private _formatStack(stack: string, message: string, opts?: FormatOpts): string {
144
+ const cwd = process.cwd() + sep;
145
+ const indent = " ".repeat((opts?.errorLevel ?? 0) + 1);
146
+ const lines = stack
147
+ .split("\n")
148
+ .splice(message.split("\n").length)
149
+ .map((l) => l.trim().replace("file://", "").replace(cwd, ""));
150
+ return `\n${indent}` + lines.map((l) => ` ${l}`).join(`\n${indent}`);
151
+ }
152
+
153
+ private _formatIcon(logObj: LogObject, useColors: boolean): string {
154
+ const icon = TYPE_ICONS[logObj.type] ?? "";
155
+ if (icon === "") return "";
156
+ const color: AnsiColor = TYPE_COLORS[logObj.type] ?? LEVEL_COLORS[logObj.level] ?? "gray";
157
+ return colorize(color, icon, useColors);
158
+ }
159
+
160
+ private _formatDate(date: Date, opts: FormatOpts): string {
161
+ if (!opts.date) return "";
162
+ const base = date.toLocaleTimeString();
163
+ return `${base}.${String(date.getMilliseconds()).padStart(3, "0")}`;
164
+ }
165
+
166
+ private _formatBox(logObj: LogObject, message: string): string {
167
+ const tag = logObj.tag !== "" ? `[${logObj.tag}]` : "";
168
+ const title = (logObj as LogObject & { title?: string }).title;
169
+ const lines = [tag, title, ...message.split("\n")].filter(Boolean);
170
+ return "\n" + lines.map((l) => ` > ${l}`).join("\n") + "\n";
171
+ }
172
+ }
@@ -0,0 +1,40 @@
1
+ import type { ConsolaOptions, ConsolaReporter, LogObject } from "consola";
2
+ import consola, { LogLevels } from "consola";
3
+ import { env, parseBoolEnv } from "@simplysm/core-common";
4
+ import { PrettyReporter } from "./pretty-reporter";
5
+ import { createFileReporter } from "./file-reporter";
6
+
7
+ export function withMaxLevel(reporter: ConsolaReporter, maxLevel: number): ConsolaReporter {
8
+ return {
9
+ log(logObj: LogObject, ctx: { options: ConsolaOptions }) {
10
+ if (logObj.level > maxLevel) return;
11
+ reporter.log(logObj, ctx);
12
+ },
13
+ };
14
+ }
15
+
16
+ export interface SetupConsolaOptions {
17
+ cli?: boolean;
18
+ }
19
+
20
+ export function setupConsola(opts?: SetupConsolaOptions): void {
21
+ if (!opts?.cli && !parseBoolEnv(env("DEV"))) {
22
+ // prod: debug 포함 FileReporter
23
+ consola.level = LogLevels.debug;
24
+ consola.options.reporters = [createFileReporter()];
25
+ return;
26
+ }
27
+
28
+ if (parseBoolEnv(env("SD_DEBUG"))) {
29
+ // dev + SD_DEBUG: debug 포함 PrettyReporter
30
+ consola.level = LogLevels.debug;
31
+ consola.options.reporters = [new PrettyReporter()];
32
+ } else {
33
+ // dev: debug 포함 FileReporter + debug 비포함 PrettyReporter
34
+ consola.level = LogLevels.debug;
35
+ consola.options.reporters = [
36
+ createFileReporter(),
37
+ withMaxLevel(new PrettyReporter(), LogLevels.info),
38
+ ];
39
+ }
40
+ }
package/src/index.ts CHANGED
@@ -5,6 +5,9 @@ export * as pathx from "./utils/path";
5
5
 
6
6
  // 기능
7
7
  export * from "./features/fs-watcher";
8
+ export * from "./features/consola/pretty-reporter";
9
+ export * from "./features/consola/file-reporter";
10
+ export * from "./features/consola/setup-consola";
8
11
 
9
12
  // 워커
10
13
  export * from "./worker/types";
package/src/utils/cp.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ChildProcess, SpawnOptions, SpawnSyncOptions } from "child_process";
2
2
  import { execSync as cpExecSync, spawn as cpSpawn, spawnSync as cpSpawnSync } from "child_process";
3
- import { bytes } from "@simplysm/core-common";
3
+ import { bytes, env } from "@simplysm/core-common";
4
4
 
5
5
  const CODE_PAGE_MAP: Record<number, string> = {
6
6
  65001: "utf-8",
@@ -36,7 +36,7 @@ export function getSystemEncoding(): string {
36
36
  return _cachedEncoding;
37
37
  }
38
38
  } else {
39
- const lang = process.env["LANG"] ?? process.env["LC_ALL"] ?? "";
39
+ const lang = env("LANG") ?? env("LC_ALL") ?? "";
40
40
  const dotIndex = lang.indexOf(".");
41
41
  if (dotIndex >= 0) {
42
42
  let encoding = lang.slice(dotIndex + 1).split("@")[0].toLowerCase();