@done-coding/output-node 0.1.0 → 0.1.1

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
@@ -16,7 +16,8 @@ Node.js 环境下的同构输出工具包,基于 @done-coding/output-core 核
16
16
  - ⚡ **高性能** - 异步日志写入,可配置缓冲区大小
17
17
  - 🔧 **完整的错误处理** - 输入验证、配置验证、文件权限检查
18
18
  - 📦 **完整类型支持** - 100% TypeScript 类型定义和类型推导
19
- - 🎯 **输出模式切换** - 支持控制台和日志文件之间的智能切换
19
+ - 🔇 **动态静默控制** - 支持运行时动态控制输出静默,基于类型的条件静默
20
+ - 🎯 **智能切换同步** - 支持控制台和日志文件之间的智能切换和同步
20
21
  - 📋 **TABLE 类型特殊处理** - 智能表格数据展示和 JSON 序列化
21
22
 
22
23
  ## 快速开始
@@ -76,6 +77,22 @@ logger.error("发生错误");
76
77
  5. **框架兼容** - 与现代框架(NestJS、Express 等)完美兼容
77
78
  6. **极简 API** - 最少化的配置参数,易于使用
78
79
 
80
+ ### 内置实现
81
+
82
+ #### 控制台输出驱动
83
+
84
+ - **基础实现**: `console.log` + `chalk` 颜色支持
85
+ - **颜色配置**: 支持自定义颜色映射和禁用颜色
86
+ - **TABLE 类型**: 自动调用 `console.table` 进行表格展示
87
+ - **错误处理**: 颜色格式化失败时自动降级到无颜色输出
88
+
89
+ #### 日志文件输出驱动
90
+
91
+ - **基础实现**: `pino` + `sonic-boom` 高性能日志写入
92
+ - **缓冲机制**: 可配置缓冲区大小,支持同步/异步写入
93
+ - **临终保护**: 使用 `signal-exit` 实现非侵入式进程退出监听
94
+ - **内置控制台**: 自动提供 `outputConsoleFn`,基于 pino 输出到控制台
95
+
79
96
  ## API 文档
80
97
 
81
98
  ### createOutputConsole
@@ -94,12 +111,12 @@ function createOutputConsole(
94
111
 
95
112
  | 参数 | 类型 | 默认值 | 说明 |
96
113
  | ----------------------- | ------------------------------------- | ------ | -------------------- |
97
- | options.silent | boolean | false | 是否静默模式 |
114
+ | options.isSilent | (type) => boolean | - | 动态静默控制函数 |
98
115
  | options.enableColor | boolean | true | 是否启用颜色输出 |
99
116
  | options.colorMap | Record<OutputConsoleTypeEnum, string> | - | 自定义颜色映射 |
100
117
  | options.isSwitchLogFile | (type) => boolean | - | 切换到日志文件的条件 |
101
118
  | options.isSyncToLogFile | (type) => boolean | - | 同步到日志文件的条件 |
102
- | options.outputFileFn | OutputLogFile | - | 日志文件输出函数 |
119
+ | options.outputFileFn | OutputConsoleRaw | - | 日志文件输出函数 |
103
120
 
104
121
  **返回值:** OutputConsole 混合类型实例
105
122
 
@@ -108,7 +125,8 @@ function createOutputConsole(
108
125
  ```typescript
109
126
  const output = createOutputConsole({
110
127
  enableColor: true,
111
- silent: false,
128
+ isSilent: (type) =>
129
+ type === OutputConsoleTypeEnum.DEBUG && !process.env.DEBUG,
112
130
  });
113
131
 
114
132
  output.info("信息");
@@ -133,11 +151,10 @@ function createOutputLogFile(
133
151
  | 参数 | 类型 | 默认值 | 说明 |
134
152
  | ----------------------- | ----------------- | ------ | -------------------- |
135
153
  | options.logFilePath | string | - | 日志文件路径(必传) |
136
- | options.silent | boolean | false | 是否静默模式 |
154
+ | options.isSilent | (type) => boolean | - | 动态静默控制函数 |
137
155
  | options.sync | boolean | false | 是否同步写入 |
138
156
  | options.bufferSize | number | 4096 | 缓冲区大小(字节) |
139
157
  | options.isSwitchConsole | (type) => boolean | - | 切换到控制台的条件 |
140
- | options.outputConsoleFn | OutputConsole | - | 控制台输出函数 |
141
158
 
142
159
  **返回值:** OutputLogFile 混合类型实例
143
160
 
@@ -182,6 +199,33 @@ logger.error("数据库连接失败");
182
199
 
183
200
  ## 进阶使用
184
201
 
202
+ ### 动态静默控制
203
+
204
+ ```typescript
205
+ import {
206
+ createOutputConsole,
207
+ OutputConsoleTypeEnum,
208
+ } from "@done-coding/output-node";
209
+
210
+ const output = createOutputConsole({
211
+ // 动态控制静默:只在调试模式下显示 DEBUG 信息
212
+ isSilent: (type) => {
213
+ if (type === OutputConsoleTypeEnum.DEBUG) {
214
+ return !process.env.DEBUG;
215
+ }
216
+ // 生产环境下静默所有 SKIP 类型
217
+ if (type === OutputConsoleTypeEnum.SKIP) {
218
+ return process.env.NODE_ENV === "production";
219
+ }
220
+ return false;
221
+ },
222
+ });
223
+
224
+ output.debug("调试信息"); // 只在 DEBUG=true 时显示
225
+ output.skip("跳过信息"); // 生产环境下不显示
226
+ output.info("普通信息"); // 总是显示
227
+ ```
228
+
185
229
  ### 切换和同步逻辑
186
230
 
187
231
  ```typescript
@@ -201,7 +245,10 @@ const output = createOutputConsole({
201
245
  isSwitchLogFile: (type) => type === OutputConsoleTypeEnum.ERROR,
202
246
  // 警告级别同步到日志文件(控制台和文件都显示)
203
247
  isSyncToLogFile: (type) => type === OutputConsoleTypeEnum.WARN,
204
- outputFileFn: fileLogger,
248
+ outputFileFn: (type, ...messages) => {
249
+ // 将控制台类型映射到日志文件类型并输出
250
+ fileLogger(type, ...messages);
251
+ },
205
252
  });
206
253
 
207
254
  output.info("普通信息"); // 只在控制台显示
@@ -283,11 +330,11 @@ logger.info("应用启动");
283
330
 
284
331
  ### 测试覆盖率
285
332
 
286
- - **语句覆盖率**: 99.22%
287
- - **分支覆盖率**: 96.27%
288
- - **函数覆盖率**: 97.72%
289
- - **行覆盖率**: 99.22%
290
- - **测试数量**: 211 个测试,全部通过
333
+ - **语句覆盖率**: 99.35%
334
+ - **分支覆盖率**: 97.63%
335
+ - **函数覆盖率**: 97.5%
336
+ - **行覆盖率**: 99.35%
337
+ - **测试数量**: 181 个测试,全部通过
291
338
 
292
339
  ### 本地开发
293
340
 
@@ -319,6 +366,14 @@ pnpm build
319
366
 
320
367
  ## 常见问题
321
368
 
369
+ **Q: 为什么 Node 包不需要传入 `outputImpl` 参数?**
370
+
371
+ A: Node 包自动提供了基于 `console.log + chalk` 和 `pino` 的输出实现,用户无需手动创建和传入 `outputImpl`。这是 Node 包相比 Core 包的主要优势。
372
+
373
+ **Q: 如何在生产环境中优化日志性能?**
374
+
375
+ A: 使用异步写入模式(`sync: false`),适当增加缓冲区大小(如 8KB 或 16KB),并合理配置 `isSilent` 函数来过滤不必要的日志输出。
376
+
322
377
  **Q: 如何在 NestJS 中使用?**
323
378
 
324
379
  A: 可以在 NestJS 的 OnModuleInit 和 OnApplicationShutdown 钩子中使用,临终落盘保护会自动处理进程退出时的日志刷新。
package/es/index.mjs CHANGED
@@ -1,95 +1,93 @@
1
1
  #!/usr/bin/env node
2
- import { OutputConsoleTypeEnum as c, OutputLogFileTypeEnum as E, handleTableTypeConsole as Z, generateTimestamp as x, createOutputConsole as z, createOutputLogFile as G } from "@done-coding/output-core";
3
- import { OutputConsoleTypeEnum as pe, OutputLogFileTypeEnum as Ee } from "@done-coding/output-core";
4
- import p from "chalk";
5
- import h from "pino";
6
- import F from "node:fs";
7
- import { onExit as j } from "signal-exit";
8
- class N extends Error {
9
- constructor(t, r) {
10
- super(`无效的输出类型: ${t}。有效的类型包括: ${r.join(", ")}`), this.name = "InvalidOutputTypeError";
2
+ import { OutputConsoleTypeEnum as u, OutputLogFileTypeEnum as a, handleTableTypeConsole as w, createOutputConsole as D, createOutputLogFile as A } from "@done-coding/output-core";
3
+ import { OutputConsoleTypeEnum as le, OutputLogFileTypeEnum as fe } from "@done-coding/output-core";
4
+ import d from "chalk";
5
+ import O from "pino";
6
+ import h from "node:fs";
7
+ import { onExit as P } from "signal-exit";
8
+ class L extends Error {
9
+ constructor(r, t) {
10
+ super(`无效的输出类型: ${r}。有效的类型包括: ${t.join(", ")}`), this.name = "InvalidOutputTypeError";
11
11
  }
12
12
  }
13
- class S extends Error {
14
- constructor(t) {
15
- super(`配置错误: ${t}`), this.name = "InvalidConfigurationError";
13
+ class g extends Error {
14
+ constructor(r) {
15
+ super(`配置错误: ${r}`), this.name = "InvalidConfigurationError";
16
16
  }
17
17
  }
18
- class le extends Error {
19
- constructor(t, r) {
20
- super(`文件权限不足: 无法${r}文件 "${t}"`), this.name = "FilePermissionError";
18
+ class re extends Error {
19
+ constructor(r, t) {
20
+ super(`文件权限不足: 无法${t}文件 "${r}"`), this.name = "FilePermissionError";
21
21
  }
22
22
  }
23
- class _ extends Error {
24
- constructor(t, r) {
25
- super(`驱动初始化失败: ${t}${r ? ` - ${r.message}` : ""}`), this.name = "DriverInitializationError", r && (this.cause = r);
23
+ class C extends Error {
24
+ constructor(r, t) {
25
+ super(`驱动初始化失败: ${r}${t ? ` - ${t.message}` : ""}`), this.name = "DriverInitializationError", t && (this.cause = t);
26
26
  }
27
27
  }
28
- function C(e) {
29
- const t = Object.values(c).filter(
30
- (r) => typeof r == "number"
28
+ function _(e) {
29
+ const r = Object.values(u).filter(
30
+ (t) => typeof t == "number"
31
31
  );
32
- if (typeof e != "number" || !t.includes(e)) {
33
- const r = Object.keys(c).filter(
34
- (i) => isNaN(Number(i))
32
+ if (typeof e != "number" || !r.includes(e)) {
33
+ const t = Object.keys(u).filter(
34
+ (o) => isNaN(Number(o))
35
35
  );
36
- throw new N(e, r);
36
+ throw new L(e, t);
37
37
  }
38
38
  }
39
- function w(e) {
40
- const t = Object.values(E).filter(
41
- (r) => typeof r == "number"
39
+ function U(e) {
40
+ const r = Object.values(a).filter(
41
+ (t) => typeof t == "number"
42
42
  );
43
- if (typeof e != "number" || !t.includes(e)) {
44
- const r = Object.keys(E).filter(
45
- (i) => isNaN(Number(i))
43
+ if (typeof e != "number" || !r.includes(e)) {
44
+ const t = Object.keys(a).filter(
45
+ (o) => isNaN(Number(o))
46
46
  );
47
- throw new N(e, r);
47
+ throw new L(e, t);
48
48
  }
49
49
  }
50
- function m(e, t) {
51
- for (const r of t)
52
- if (!(r in e) || e[r] === void 0)
53
- throw new S(`缺少必需字段: ${r}`);
50
+ function T(e, r) {
51
+ for (const t of r)
52
+ if (!(t in e) || e[t] === void 0)
53
+ throw new g(`缺少必需字段: ${t}`);
54
54
  }
55
- function b(e) {
55
+ function M(e) {
56
56
  if (typeof e != "string" || e.trim() === "")
57
- throw new S("文件路径必须是非空字符串");
57
+ throw new g("文件路径必须是非空字符串");
58
58
  if (/[<>:"|?*]/.test(e))
59
- throw new S(`文件路径包含非法字符: ${e}`);
59
+ throw new g(`文件路径包含非法字符: ${e}`);
60
60
  }
61
- function a(e, t) {
61
+ function c(e, r) {
62
62
  try {
63
63
  return e();
64
- } catch (r) {
65
- return t ? t(r) : void 0;
64
+ } catch (t) {
65
+ return r ? r(t) : void 0;
66
66
  }
67
67
  }
68
- const I = {
69
- /** 默认静默模式:关闭 */
70
- silent: !1,
68
+ const R = {
69
+ // isSilent 是可选的,不需要默认值
71
70
  /** 默认启用颜色输出:是 */
72
71
  enableColor: !0
73
- }, B = {
74
- [c.DEBUG]: "gray",
75
- [c.SKIP]: "dim",
76
- [c.INFO]: "white",
77
- [c.TABLE]: "cyan",
78
- [c.STAGE]: "blue",
79
- [c.SUCCESS]: "greenBright",
80
- [c.WARN]: "yellow",
81
- [c.ERROR]: "redBright"
82
- }, k = {
72
+ }, $ = {
73
+ [u.DEBUG]: "gray",
74
+ [u.SKIP]: "dim",
75
+ [u.INFO]: "white",
76
+ [u.TABLE]: "cyan",
77
+ [u.STAGE]: "blue",
78
+ [u.SUCCESS]: "greenBright",
79
+ [u.WARN]: "yellow",
80
+ [u.ERROR]: "redBright"
81
+ }, x = {
83
82
  /** 默认颜色格式化失败时的回退颜色 */
84
83
  fallbackColor: "white"
85
- }, g = {
86
- /** 默认静默模式:关闭 */
87
- silent: !1,
84
+ }, m = {
85
+ // isSilent 是可选的,不需要默认值
88
86
  /** 默认同步写入:关闭(异步写入) */
89
87
  sync: !1,
90
88
  /** 默认缓冲区大小:4KB */
91
89
  bufferSize: 4096
92
- }, v = {
90
+ }, b = {
93
91
  /** 默认日志级别:trace */
94
92
  level: "trace",
95
93
  /** 默认时间格式:yyyy-mm-dd HH:MM:ss */
@@ -100,7 +98,7 @@ const I = {
100
98
  messageFormat: "{msg}",
101
99
  /** 默认文件输出颜色:关闭 */
102
100
  colorize: !1
103
- }, d = {
101
+ }, E = {
104
102
  /** 最小缓冲区大小:1KB */
105
103
  MIN_SIZE: 1024,
106
104
  /** 默认缓冲区大小:4KB */
@@ -109,26 +107,24 @@ const I = {
109
107
  RECOMMENDED_SIZE: 8192,
110
108
  /** 最大缓冲区大小:64KB */
111
109
  MAX_SIZE: 65536
112
- }, L = {
113
- /** 默认静默模式:关闭 */
114
- silent: !1,
110
+ }, z = {
111
+ // isSilent 是可选的,不需要默认值
115
112
  /** 默认启用颜色输出:是 */
116
113
  enableColor: !0
117
- }, O = {
118
- /** 默认静默模式:关闭 */
119
- silent: !1,
114
+ }, F = {
115
+ // isSilent 是可选的,不需要默认值
120
116
  /** 默认同步写入:关闭(异步写入) */
121
117
  sync: !1,
122
118
  /** 默认缓冲区大小:4KB */
123
119
  bufferSize: 4096
124
- }, se = {
120
+ }, te = {
125
121
  /** 默认静默模式:是(避免控制台输出) */
126
122
  silentMode: !0,
127
123
  /** 默认最大重试次数:3次 */
128
124
  maxRetries: 3,
129
125
  /** 默认重试间隔:100ms */
130
126
  retryInterval: 100
131
- }, ue = {
127
+ }, oe = {
132
128
  /** 默认错误处理模式:静默处理 */
133
129
  silentMode: !0,
134
130
  /** 默认错误重试次数:2次 */
@@ -136,277 +132,210 @@ const I = {
136
132
  /** 默认错误重试延迟:50ms */
137
133
  retryDelay: 50
138
134
  };
139
- function X(e) {
135
+ function G(e) {
140
136
  const {
141
- silent: t = I.silent,
142
- enableColor: r = I.enableColor,
143
- colorMap: i
144
- } = e, o = { ...B, ...i };
137
+ enableColor: r = R.enableColor,
138
+ colorMap: t
139
+ } = e, o = { ...$, ...t };
145
140
  try {
146
- if (m(e, []), i) {
147
- for (const [s, l] of Object.entries(i))
148
- if (C(Number(s)), typeof l != "string")
149
- throw new Error(`颜色值必须是字符串: ${l}`);
141
+ if (T(e, []), t) {
142
+ for (const [n, i] of Object.entries(t))
143
+ if (_(Number(n)), typeof i != "string")
144
+ throw new Error(`颜色值必须是字符串: ${i}`);
150
145
  }
151
- } catch (s) {
152
- throw new _("控制台驱动", s);
146
+ } catch (n) {
147
+ throw new C("控制台驱动", n);
153
148
  }
154
- return (s, ...l) => {
155
- if (a(() => C(s)), t)
156
- return;
157
- if (s === c.TABLE) {
158
- a(() => Z(...l));
149
+ return (n, ...i) => {
150
+ if (c(() => _(n)), n === u.TABLE) {
151
+ c(() => w(...i));
159
152
  return;
160
153
  }
161
- const f = o[s] || k.fallbackColor, n = l.map((u) => typeof u == "string" && r ? a(
162
- () => f in p && typeof p[f] == "function" ? p[f](u) : p.white(u),
163
- () => u
164
- ) : u);
165
- a(() => console.log(...n));
154
+ const l = o[n] || x.fallbackColor, f = i.map((s) => typeof s == "string" && r ? c(
155
+ () => l in d && typeof d[l] == "function" ? d[l](s) : d.white(s),
156
+ () => s
157
+ ) : s);
158
+ c(() => console.log(...f));
166
159
  };
167
160
  }
168
- const R = /* @__PURE__ */ new Set();
169
- let T = !1;
170
- function H() {
171
- for (const { logger: e, destination: t } of R)
161
+ const N = /* @__PURE__ */ new Set();
162
+ let I = !1;
163
+ function Z() {
164
+ for (const { logger: e, destination: r } of N)
172
165
  try {
173
- e.info("进程退出,日志刷新完成"), V(t);
166
+ e.info("进程退出,日志刷新完成"), B(r);
174
167
  } catch {
175
168
  }
176
- q();
169
+ H();
177
170
  }
178
- function V(e) {
179
- if (M(e))
171
+ function B(e) {
172
+ if (v(e))
180
173
  try {
181
- e.flushSync(), W(e);
182
- } catch (t) {
183
- if (t instanceof Error && t.message.includes("sonic boom is not ready yet"))
174
+ e.flushSync(), j(e);
175
+ } catch (r) {
176
+ if (r instanceof Error && r.message.includes("sonic boom is not ready yet"))
184
177
  return;
185
178
  }
186
179
  }
187
- function W(e) {
188
- const t = e.fd;
189
- if (typeof t == "number" && t > 0)
180
+ function j(e) {
181
+ const r = e.fd;
182
+ if (typeof r == "number" && r > 0)
190
183
  try {
191
- F.fsyncSync(t);
184
+ h.fsyncSync(r);
192
185
  } catch {
193
186
  }
194
187
  }
195
- function M(e) {
188
+ function v(e) {
196
189
  if (!e || !("flushSync" in e) || typeof e.flushSync != "function")
197
190
  return !1;
198
- const t = e;
199
- if (t.constructor && t.constructor.name === "SonicBoom")
200
- return K(t);
201
- const r = t.fd;
202
- return typeof r == "number" ? r > 0 || r === 1 || r === 2 : !(t.destroyed || t.closed || t.writable === !1);
191
+ const r = e;
192
+ if (r.constructor && r.constructor.name === "SonicBoom")
193
+ return k(r);
194
+ const t = r.fd;
195
+ return typeof t == "number" ? t > 0 || t === 1 || t === 2 : !(r.destroyed || r.closed || r.writable === !1);
203
196
  }
204
- function K(e) {
197
+ function k(e) {
205
198
  return !(!e.fd || e.fd === null || e.fd === void 0 || e.fd < 0 || e._writing === !0 || e.destroyed === !0 || e._reopening === !0 || e._ending === !0 || e._asyncDrainScheduled === !0 || e._buf && e._buf.length > 0 && e._writing);
206
199
  }
207
- function q() {
200
+ function H() {
208
201
  try {
209
- process.stdout.fd !== void 0 && typeof process.stdout.fd == "number" && F.fsyncSync(process.stdout.fd);
202
+ process.stdout.fd !== void 0 && typeof process.stdout.fd == "number" && h.fsyncSync(process.stdout.fd);
210
203
  } catch {
211
204
  }
212
205
  try {
213
- process.stderr.fd !== void 0 && typeof process.stderr.fd == "number" && F.fsyncSync(process.stderr.fd);
206
+ process.stderr.fd !== void 0 && typeof process.stderr.fd == "number" && h.fsyncSync(process.stderr.fd);
214
207
  } catch {
215
208
  }
216
209
  }
217
- function J() {
218
- T || (j(() => {
219
- H();
220
- }), T = !0);
210
+ function X() {
211
+ I || (P(() => {
212
+ Z();
213
+ }), I = !0);
221
214
  }
222
- function P(e, t) {
223
- R.add({ logger: e, destination: t }), J();
215
+ function V({
216
+ logger: e,
217
+ destination: r
218
+ }) {
219
+ N.add({ logger: e, destination: r }), X();
224
220
  }
225
- function Q(e) {
221
+ function W(e) {
226
222
  const {
227
- silent: t = g.silent,
228
223
  logFilePath: r,
229
- sync: i = g.sync,
230
- bufferSize: o = g.bufferSize
224
+ sync: t = m.sync,
225
+ bufferSize: o = m.bufferSize
231
226
  } = e;
232
227
  try {
233
- if (m(e, []), r !== void 0 && b(r), o <= 0)
228
+ if (T(e, []), r !== void 0 && M(r), o <= 0)
234
229
  throw new Error(`缓冲区大小必须大于0: ${o}`);
235
- if (o < d.MIN_SIZE || o > d.MAX_SIZE)
230
+ if (o < E.MIN_SIZE || o > E.MAX_SIZE)
236
231
  throw new Error(
237
- `缓冲区大小必须在 ${d.MIN_SIZE} - ${d.MAX_SIZE} 字节范围内: ${o}`
232
+ `缓冲区大小必须在 ${E.MIN_SIZE} - ${E.MAX_SIZE} 字节范围内: ${o}`
238
233
  );
239
- } catch (n) {
240
- throw new _("日志文件驱动", n);
234
+ } catch (f) {
235
+ throw new C("日志文件驱动", f);
241
236
  }
242
- const s = r || D();
243
- let l = null, f = null;
244
- if (!t) {
245
- const n = $({
246
- logFilePath: s,
247
- sync: i,
248
- bufferSize: o
249
- });
250
- l = n.logger, f = n.destination, l && f && P(l, f);
251
- }
252
- return (n, ...u) => {
253
- if (a(() => w(n)), t || !l)
254
- return;
255
- const y = u.length === 1 ? u[0] : u.join(" ");
256
- a(() => {
257
- const U = (E[n] || "INFO").toLowerCase();
258
- l[U](y);
259
- });
260
- };
261
- }
262
- function Y(e) {
263
- const {
264
- silent: t = g.silent,
237
+ const i = K({
265
238
  logFilePath: r,
266
- sync: i = g.sync,
267
- bufferSize: o = g.bufferSize
268
- } = e;
269
- try {
270
- if (m(e, []), r !== void 0 && b(r), o <= 0)
271
- throw new Error(`缓冲区大小必须大于0: ${o}`);
272
- if (o < d.MIN_SIZE || o > d.MAX_SIZE)
273
- throw new Error(
274
- `缓冲区大小必须在 ${d.MIN_SIZE} - ${d.MAX_SIZE} 字节范围内: ${o}`
275
- );
276
- } catch (n) {
277
- throw new _("日志文件驱动", n);
278
- }
279
- const s = r || D();
280
- let l = null, f = null;
281
- if (!t) {
282
- const n = $({
283
- logFilePath: s,
284
- sync: i,
285
- bufferSize: o
286
- });
287
- l = n.logger, f = n.destination, l && f && P(l, f);
288
- }
289
- return (n, ...u) => {
290
- if (t || !l)
291
- return;
292
- const y = u.length === 1 ? u[0] : u.join(" ");
293
- a(() => {
294
- l.info(y);
295
- });
296
- };
297
- }
298
- function A() {
299
- let e = null;
300
- return e = ee().logger, (r, ...i) => {
301
- if (a(() => w(r)), !e)
302
- return;
303
- const o = i.length === 1 ? i[0] : i.join(" ");
304
- a(() => {
305
- const s = (E[r] || "INFO").toLowerCase();
306
- e[s](o);
239
+ sync: t,
240
+ bufferSize: o
241
+ });
242
+ V(i);
243
+ const { logger: l } = i;
244
+ return (f, ...s) => {
245
+ c(() => U(f));
246
+ const y = s.length === 1 ? s[0] : s.join(" ");
247
+ c(() => {
248
+ var S;
249
+ const p = (a[f] || a[a.INFO]).toLowerCase();
250
+ (S = l[p]) == null || S.call(l, y);
307
251
  });
308
252
  };
309
253
  }
310
- function D() {
311
- const e = x(), t = process.pid;
312
- return `${e}-${t}.log`;
313
- }
314
- function $(e) {
315
- const { logFilePath: t, sync: r, bufferSize: i } = e, o = h.destination({
316
- dest: t,
317
- sync: r,
254
+ function K(e) {
255
+ const { logFilePath: r, sync: t, bufferSize: o } = e, n = O.destination({
256
+ dest: r,
257
+ sync: t,
318
258
  // 使用用户配置的同步/异步模式
319
- bufferSize: i,
259
+ bufferSize: o,
320
260
  // 使用用户配置的缓冲区大小
321
261
  autoEnd: !1
322
262
  // 始终禁用自动结束,使用我们的防御性退出处理
323
- }), s = o.flushSync;
324
- return o.flushSync = function() {
325
- if (M(o))
326
- return s.call(this);
327
- }, { logger: h(
263
+ }), i = n.flushSync;
264
+ return n.flushSync = function() {
265
+ if (v(n))
266
+ return i.call(this);
267
+ }, { logger: O(
328
268
  {
329
- level: v.level
269
+ level: b.level
330
270
  },
331
- o
332
- ), destination: o };
271
+ n
272
+ ), destination: n };
333
273
  }
334
- function ee() {
335
- return { logger: h({
336
- level: v.level
337
- }), destination: process.stdout };
338
- }
339
- function fe(e = {}) {
274
+ function ne(e = {}) {
340
275
  const {
341
- silent: t = L.silent,
342
- enableColor: r = L.enableColor,
343
- colorMap: i,
344
- isSwitchLogFile: o,
345
- isSyncToLogFile: s,
346
- outputFileFn: l
347
- } = e, f = X({
348
- silent: t,
276
+ enableColor: r = z.enableColor,
277
+ isSilent: t,
278
+ colorMap: o,
279
+ ...n
280
+ } = e, i = G({
349
281
  enableColor: r,
350
- colorMap: i
282
+ colorMap: o
351
283
  });
352
- let n = l;
353
- return !n && (o || s) && (n = Y({
354
- silent: t
355
- })), z(f, {
356
- silent: t,
357
- colorMap: i,
284
+ return D({
285
+ outputImpl: i,
286
+ isSilent: t,
287
+ colorMap: o,
358
288
  enableColor: r,
359
- isSwitchLogFile: o,
360
- isSyncToLogFile: s,
361
- outputFileFn: n
289
+ ...n
362
290
  });
363
291
  }
364
- function ce(e) {
292
+ function ie(e) {
365
293
  const {
366
- silent: t = O.silent,
367
- logFilePath: r,
294
+ isSilent: r,
295
+ // 不设置默认值,保持可选
296
+ logFilePath: t,
368
297
  // 现在是必传的绝对路径参数
369
- sync: i = O.sync,
298
+ sync: o = F.sync,
370
299
  // 默认异步写入
371
- bufferSize: o = O.bufferSize,
300
+ bufferSize: n = F.bufferSize,
372
301
  // 默认缓冲区大小 4KB
373
- isSwitchConsole: s,
374
- outputConsoleFn: l
375
- } = e, f = Q({
376
- silent: t,
377
- logFilePath: r,
378
- sync: i,
379
- bufferSize: o
302
+ ...i
303
+ } = e, l = W({
304
+ logFilePath: t,
305
+ sync: o,
306
+ bufferSize: n
380
307
  });
381
- let n = l;
382
- return !n && s && (n = A()), G(f, {
383
- silent: t,
384
- logFilePath: r,
385
- isSwitchConsole: s,
386
- outputConsoleFn: n
308
+ let f;
309
+ return A({
310
+ outputImpl: l,
311
+ isSilent: r,
312
+ // 内部使用pino 直接输出到控制台
313
+ outputConsoleFn: (s, ...y) => {
314
+ f || (f = O({
315
+ level: b.level
316
+ }));
317
+ const p = { type: s, messages: y };
318
+ f.info(p);
319
+ },
320
+ ...i
387
321
  });
388
322
  }
389
- function ae() {
390
- return A();
391
- }
392
323
  export {
393
- d as BUFFER_SIZE_CONFIG,
394
- B as DEFAULT_CONSOLE_COLOR_MAP,
395
- I as DEFAULT_CONSOLE_OUTPUT_IMPL_OPTIONS,
396
- ue as DEFAULT_ERROR_HANDLING_CONFIG,
397
- g as DEFAULT_LOG_FILE_OUTPUT_IMPL_OPTIONS,
398
- L as DEFAULT_NODE_CREATE_OUTPUT_CONSOLE_OPTIONS,
399
- O as DEFAULT_NODE_CREATE_OUTPUT_LOG_FILE_OPTIONS,
400
- v as DEFAULT_PINO_CONFIG,
401
- se as DEFAULT_PROCESS_HANDLER_CONFIG,
402
- _ as DriverInitializationError,
403
- le as FilePermissionError,
404
- S as InvalidConfigurationError,
405
- N as InvalidOutputTypeError,
406
- pe as OutputConsoleTypeEnum,
407
- Ee as OutputLogFileTypeEnum,
408
- fe as createOutputConsole,
409
- ce as createOutputLogFile,
410
- ae as createPinoConsoleOutput,
411
- A as createPinoConsoleOutputImpl
324
+ E as BUFFER_SIZE_CONFIG,
325
+ $ as DEFAULT_CONSOLE_COLOR_MAP,
326
+ R as DEFAULT_CONSOLE_OUTPUT_IMPL_OPTIONS,
327
+ oe as DEFAULT_ERROR_HANDLING_CONFIG,
328
+ m as DEFAULT_LOG_FILE_OUTPUT_IMPL_OPTIONS,
329
+ z as DEFAULT_NODE_CREATE_OUTPUT_CONSOLE_OPTIONS,
330
+ F as DEFAULT_NODE_CREATE_OUTPUT_LOG_FILE_OPTIONS,
331
+ b as DEFAULT_PINO_CONFIG,
332
+ te as DEFAULT_PROCESS_HANDLER_CONFIG,
333
+ C as DriverInitializationError,
334
+ re as FilePermissionError,
335
+ g as InvalidConfigurationError,
336
+ L as InvalidOutputTypeError,
337
+ le as OutputConsoleTypeEnum,
338
+ fe as OutputLogFileTypeEnum,
339
+ ne as createOutputConsole,
340
+ ie as createOutputLogFile
412
341
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@done-coding/output-node",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "node相关输出",
5
5
  "private": false,
6
6
  "module": "es/index.mjs",
@@ -43,6 +43,9 @@
43
43
  "@types/node": "^18.0.0",
44
44
  "@types/signal-exit": "^4.0.0",
45
45
  "@vitest/coverage-v8": "^1.6.1",
46
+ "chalk": "^5.3.0",
47
+ "pino": "^8.17.2",
48
+ "signal-exit": "^4.1.0",
46
49
  "typescript": "^5.8.3",
47
50
  "vite": "^5.0.10",
48
51
  "vite-plugin-dts": "^3.7.0",
@@ -54,10 +57,10 @@
54
57
  "signal-exit": "^4.1.0"
55
58
  },
56
59
  "dependencies": {
57
- "@done-coding/output-core": "0.1.0"
60
+ "@done-coding/output-core": "0.1.1"
58
61
  },
59
62
  "engines": {
60
63
  "node": ">=18.0.0"
61
64
  },
62
- "gitHead": "fe3595bcf072441befe269e1b1e47c807ae7e0e5"
65
+ "gitHead": "87831fe924a30d5f398a6687e92f5db72d6ea71b"
63
66
  }
package/types/index.d.ts CHANGED
@@ -32,12 +32,9 @@ export declare function createOutputConsole(options?: CreateOutputConsoleOptions
32
32
 
33
33
  /**
34
34
  * Node.js 环境下的控制台输出选项
35
- * 继承核心包选项,但不需要手动传入 outputImpl,并且 silent 是可选的
35
+ * 继承核心包选项,但不需要手动传入 outputImpl
36
36
  */
37
- export declare interface CreateOutputConsoleOptions extends Omit<CreateOutputConsoleOptions_2, "outputFileFn"> {
38
- /** 日志文件输出函数 */
39
- outputFileFn?: OutputConsoleRaw;
40
- }
37
+ export declare type CreateOutputConsoleOptions = Omit<CreateOutputConsoleOptions_2, "outputImpl">;
41
38
 
42
39
  /**
43
40
  * 创建日志文件输出实例 (Node.js 适配版本)
@@ -50,31 +47,18 @@ export declare function createOutputLogFile(options: CreateOutputLogFileOptions)
50
47
 
51
48
  /**
52
49
  * Node.js 环境下的日志文件输出选项
53
- * 继承核心包选项,但不需要手动传入 outputImpl,并且 silent 是可选的
50
+ * 继承核心包选项,但不需要手动传入 outputImpl,并且增加 Node.js 特定参数
54
51
  * logFilePath 为必传参数,必须提供绝对路径
55
52
  */
56
- export declare interface CreateOutputLogFileOptions extends Omit<CreateOutputLogFileOptions_2, "logFilePath" | "outputConsoleFn">, Pick<LogFileOutputImplOptions, "sync" | "bufferSize"> {
53
+ export declare interface CreateOutputLogFileOptions extends Omit<CreateOutputLogFileOptions_2, "outputImpl" | "logFilePath" | "outputConsoleFn"> {
54
+ /** 日志文件是否同步写入 (默认: false,异步写入) */
55
+ sync?: boolean;
56
+ /** 异步写入是的缓冲区大小 (字节,默认: 4096) */
57
+ bufferSize?: number;
57
58
  /** 日志文件绝对路径 (必传) */
58
59
  logFilePath: string;
59
- /** 输出到控制台对应的方法 */
60
- outputConsoleFn?: OutputLogFileRaw;
61
60
  }
62
61
 
63
- /**
64
- * 创建基于 pino 的控制台输出实现
65
- * 用于 outputLogFile 切换到控制台输出时使用
66
- *
67
- * @param options 配置选项
68
- * @returns 控制台输出函数(保持日志级别类型)
69
- */
70
- export declare function createPinoConsoleOutput(): OutputLogFileRaw;
71
-
72
- /**
73
- * 创建控制台输出实现(基于 pino)
74
- * @returns 日志文件输出函数(输出到控制台)
75
- */
76
- export declare function createPinoConsoleOutputImpl(): OutputLogFileRaw;
77
-
78
62
  /**
79
63
  * 默认控制台输出颜色映射
80
64
  * 根据需求 4.2 定义的颜色方案
@@ -85,8 +69,6 @@ export declare const DEFAULT_CONSOLE_COLOR_MAP: Record<OutputConsoleTypeEnum, st
85
69
  * 控制台输出实现默认配置
86
70
  */
87
71
  export declare const DEFAULT_CONSOLE_OUTPUT_IMPL_OPTIONS: {
88
- /** 默认静默模式:关闭 */
89
- readonly silent: false;
90
72
  /** 默认启用颜色输出:是 */
91
73
  readonly enableColor: true;
92
74
  };
@@ -107,8 +89,6 @@ export declare const DEFAULT_ERROR_HANDLING_CONFIG: {
107
89
  * 日志文件输出实现默认配置
108
90
  */
109
91
  export declare const DEFAULT_LOG_FILE_OUTPUT_IMPL_OPTIONS: {
110
- /** 默认静默模式:关闭 */
111
- readonly silent: false;
112
92
  /** 默认同步写入:关闭(异步写入) */
113
93
  readonly sync: false;
114
94
  /** 默认缓冲区大小:4KB */
@@ -119,8 +99,6 @@ export declare const DEFAULT_LOG_FILE_OUTPUT_IMPL_OPTIONS: {
119
99
  * Node.js 控制台输出创建函数默认配置
120
100
  */
121
101
  export declare const DEFAULT_NODE_CREATE_OUTPUT_CONSOLE_OPTIONS: {
122
- /** 默认静默模式:关闭 */
123
- readonly silent: false;
124
102
  /** 默认启用颜色输出:是 */
125
103
  readonly enableColor: true;
126
104
  };
@@ -129,8 +107,6 @@ export declare const DEFAULT_NODE_CREATE_OUTPUT_CONSOLE_OPTIONS: {
129
107
  * Node.js 日志文件输出创建函数默认配置
130
108
  */
131
109
  export declare const DEFAULT_NODE_CREATE_OUTPUT_LOG_FILE_OPTIONS: {
132
- /** 默认静默模式:关闭 */
133
- readonly silent: false;
134
110
  /** 默认同步写入:关闭(异步写入) */
135
111
  readonly sync: false;
136
112
  /** 默认缓冲区大小:4KB */
@@ -194,20 +170,6 @@ export declare class InvalidOutputTypeError extends Error {
194
170
  constructor(type: unknown, validTypes: string[]);
195
171
  }
196
172
 
197
- /**
198
- * 日志文件输出实现配置选项
199
- */
200
- declare interface LogFileOutputImplOptions {
201
- /** 是否静默模式 */
202
- silent?: boolean;
203
- /** 日志文件路径 (可选,相对路径) */
204
- logFilePath?: string;
205
- /** 是否同步写入 (默认: false,异步写入) */
206
- sync?: boolean;
207
- /** 缓冲区大小 (字节,默认: 4096) */
208
- bufferSize?: number;
209
- }
210
-
211
173
  export { OutputConsole }
212
174
 
213
175
  export { OutputConsoleRaw }