@c9up/spectrum 0.1.4 → 0.1.6

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAY,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGhE,YAAY,EAAE,QAAQ,EAAE,CAAC;AAEzB,qBAAa,MAAM;IAClB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAC,CAAS;gBAEnB,MAAM,EAAE,SAAS,EAAE,MAAM,SAAQ,EAAE,aAAa,CAAC,EAAE,MAAM;IAMrE;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAQnE;;;OAGG;IACH,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIlC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,OAAO,CAAC,GAAG;CAgCX"}
1
+ {"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAY,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGhE,YAAY,EAAE,QAAQ,EAAE,CAAC;AAwBzB,qBAAa,MAAM;IAClB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAC,CAAS;gBAEnB,MAAM,EAAE,SAAS,EAAE,MAAM,SAAQ,EAAE,aAAa,CAAC,EAAE,MAAM;IAMrE;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM;IAQnE;;;OAGG;IACH,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIlC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI5D,OAAO,CAAC,GAAG;CAgCX"}
package/dist/Logger.js CHANGED
@@ -4,6 +4,26 @@
4
4
  * @implements FR54, FR55, FR56, FR58
5
5
  */
6
6
  import { LOG_LEVEL_ORDER } from "./types.js";
7
+ /**
8
+ * Serialize an `err`/`error` Error in the log data into a plain `{ name, message,
9
+ * stack }` (AdonisJS/pino `err` serializer parity) — Errors don't JSON-stringify
10
+ * usefully otherwise. Other data passes through untouched. Returns a new object;
11
+ * the caller's data is not mutated.
12
+ */
13
+ function serializeErr(data) {
14
+ if (!data)
15
+ return data;
16
+ for (const key of ["err", "error"]) {
17
+ const v = data[key];
18
+ if (v instanceof Error) {
19
+ return {
20
+ ...data,
21
+ [key]: { name: v.name, message: v.message, stack: v.stack },
22
+ };
23
+ }
24
+ }
25
+ return data;
26
+ }
7
27
  export class Logger {
8
28
  config;
9
29
  module;
@@ -57,7 +77,7 @@ export class Logger {
57
77
  module: this.module,
58
78
  correlationId: this.correlationId,
59
79
  timestamp: new Date().toISOString(),
60
- data,
80
+ data: serializeErr(data),
61
81
  };
62
82
  for (const channel of this.config.channels) {
63
83
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"Logger.js","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAI7C,MAAM,OAAO,MAAM;IACV,MAAM,CAAY;IAClB,MAAM,CAAS;IACf,aAAa,CAAU;IAE/B,YAAY,MAAiB,EAAE,MAAM,GAAG,KAAK,EAAE,aAAsB;QACpE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAoD;QACzD,OAAO,IAAI,MAAM,CAChB,IAAI,CAAC,MAAM,EACX,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,EAC7B,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,CAC3C,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,gBAAgB,CAAC,EAAU;QAC1B,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACpD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACpD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAA8B;QACnD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACpD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAA8B;QACpD,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAEO,GAAG,CACV,KAAe,EACf,OAAe,EACf,IAA8B;QAE9B,MAAM,YAAY,GACjB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;QACzD,MAAM,cAAc,GACnB,eAAe,CAAC,YAAY,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC;QACrE,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,eAAe,CAAC,cAAc,CAAC,EAAE,CAAC;YACrE,OAAO;QACR,CAAC;QAED,MAAM,KAAK,GAAa;YACvB,KAAK;YACL,OAAO;YACP,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,IAAI;SACJ,CAAC;QAEF,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC5C,IAAI,CAAC;gBACJ,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACtB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,uBAAuB,OAAO,CAAC,IAAI,iBAAiB,OAAO,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,CAChF,CAAC;YACH,CAAC;QACF,CAAC;IACF,CAAC;CACD"}
1
+ {"version":3,"file":"Logger.js","sourceRoot":"","sources":["../src/Logger.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RustLogBridge.d.ts","sourceRoot":"","sources":["../src/RustLogBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAY,MAAM,YAAY,CAAC;AAEjE;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAuB1D;AAcD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG;IAC5D,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;CACjB,CAmFA"}
1
+ {"version":3,"file":"RustLogBridge.d.ts","sourceRoot":"","sources":["../src/RustLogBridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAY,MAAM,YAAY,CAAC;AAEjE;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAuB1D;AAiBD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG;IAC5D,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;CACjB,CA6FA"}
@@ -39,8 +39,11 @@ export function parseRustLog(line) {
39
39
  /**
40
40
  * Create a bridge that captures stderr and routes Rust logs to Spectrum channels.
41
41
  *
42
- * Usage:
43
- * const bridge = createRustLogBridge(logger.config.channels)
42
+ * Usage — pass the same channels array you built the Logger with (Logger.config
43
+ * is private, so reuse the array rather than reaching into the instance):
44
+ * const channels = [new ConsoleChannel('pretty')]
45
+ * const logger = new Logger({ level: 'info', channels })
46
+ * const bridge = createRustLogBridge(channels)
44
47
  * bridge.start()
45
48
  * // ... Rust crates emit to stderr
46
49
  * bridge.stop()
@@ -72,13 +75,23 @@ export function createRustLogBridge(channels) {
72
75
  }
73
76
  const str = typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
74
77
  let hadRustLog = false;
75
- const passthroughLines = [];
76
- // Try to parse as Rust log if it matches, route to channels
77
- for (const line of str.split("\n")) {
78
- const trimmed = line.trim();
79
- if (!trimmed)
80
- continue;
81
- const entry = parseRustLog(trimmed);
78
+ // Rebuild passthrough as one byte-faithful string instead of
79
+ // re-emitting each line with an appended "\n". split("\n") drops
80
+ // the separator, so every segment except a trailing fragment (the
81
+ // chunk didn't end in "\n") was newline-terminated originally —
82
+ // reattach accordingly. This preserves blank lines and a partial
83
+ // no-newline trailing write verbatim, and keeps passthrough lines
84
+ // in their original order.
85
+ let passthrough = "";
86
+ const segments = str.split("\n");
87
+ for (let i = 0; i < segments.length; i++) {
88
+ const seg = segments[i] ?? "";
89
+ const isTrailingFragment = i === segments.length - 1;
90
+ const original = isTrailingFragment ? seg : `${seg}\n`;
91
+ const trimmed = seg.trim();
92
+ // Only a complete, non-empty line is a Rust-log candidate. A
93
+ // trailing fragment is a partial write — never parse or drop it.
94
+ const entry = trimmed && !isTrailingFragment ? parseRustLog(trimmed) : null;
82
95
  if (entry) {
83
96
  hadRustLog = true;
84
97
  for (const channel of channels) {
@@ -95,15 +108,15 @@ export function createRustLogBridge(channels) {
95
108
  }
96
109
  }
97
110
  else {
98
- passthroughLines.push(line);
111
+ passthrough += original;
99
112
  }
100
113
  }
101
114
  // Preserve original chunk when we didn't intercept any Rust logs.
102
115
  if (!hadRustLog) {
103
116
  return origWrite(chunk, ...args);
104
117
  }
105
- for (const line of passthroughLines) {
106
- origWrite(`${line}\n`);
118
+ if (passthrough) {
119
+ origWrite(passthrough);
107
120
  }
108
121
  return true;
109
122
  });
@@ -1 +1 @@
1
- {"version":3,"file":"RustLogBridge.js","sourceRoot":"","sources":["../src/RustLogBridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RustLogBridge.js","sourceRoot":"","sources":["../src/RustLogBridge.ts"],"names":[],"mappings":"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"}
@@ -10,10 +10,13 @@ export interface SpectrumAppContext {
10
10
  config: SpectrumConfigStore;
11
11
  }
12
12
  export default class SpectrumProvider {
13
+ #private;
13
14
  protected app: SpectrumAppContext;
14
15
  constructor(app: SpectrumAppContext);
15
16
  register(): void;
16
17
  boot(): Promise<void>;
18
+ /** Release channel resources (e.g. FileChannel WriteStreams) on shutdown. */
19
+ shutdown(): Promise<void>;
17
20
  }
18
21
  export {};
19
22
  //# sourceMappingURL=SpectrumProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpectrumProvider.d.ts","sourceRoot":"","sources":["../src/SpectrumProvider.ts"],"names":[],"mappings":"AAaA,UAAU,iBAAiB;IAC1B,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC;CACxC;AAED,UAAU,mBAAmB;IAC5B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,MAAM,EAAE,mBAAmB,CAAC;CAC5B;AAED,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACxB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;IAkBF,IAAI;CAGV"}
1
+ {"version":3,"file":"SpectrumProvider.d.ts","sourceRoot":"","sources":["../src/SpectrumProvider.ts"],"names":[],"mappings":"AAkBA,UAAU,iBAAiB;IAC1B,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,GAAG,IAAI,CAAC;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,CAAC;CACxC;AAED,UAAU,mBAAmB;IAC5B,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,MAAM,EAAE,mBAAmB,CAAC;CAC5B;AAED,MAAM,CAAC,OAAO,OAAO,gBAAgB;;IAGxB,SAAS,CAAC,GAAG,EAAE,kBAAkB;gBAAvB,GAAG,EAAE,kBAAkB;IAE7C,QAAQ;IA4BF,IAAI;IAIV,6EAA6E;IACvE,QAAQ;CAKd"}
@@ -8,22 +8,34 @@ function resolveLogLevel(raw) {
8
8
  return raw;
9
9
  return "info";
10
10
  }
11
+ /** A channel that owns a resource (e.g. a FileChannel WriteStream) to release on shutdown. */
12
+ function hasClose(ch) {
13
+ return "close" in ch && typeof ch.close === "function";
14
+ }
11
15
  export default class SpectrumProvider {
12
16
  app;
17
+ #channels = [];
13
18
  constructor(app) {
14
19
  this.app = app;
15
20
  }
16
21
  register() {
17
- this.app.container.singleton(Logger, () => {
18
- const config = this.app.config.get("logger");
19
- const level = config?.level && VALID_LEVELS.has(config.level)
20
- ? config.level
21
- : resolveLogLevel(process.env.LOG_LEVEL);
22
- return new Logger({
23
- level,
24
- channels: [new ConsoleChannel("pretty")],
25
- });
26
- });
22
+ const config = this.app.config.get("logger");
23
+ const level = config?.level && VALID_LEVELS.has(config.level)
24
+ ? config.level
25
+ : resolveLogLevel(process.env.LOG_LEVEL);
26
+ // Honour the configured channels (the whole point of LogConfig.channels);
27
+ // fall back to a pretty console channel only when none are supplied.
28
+ // Previously this was hardcoded and config.channels was silently ignored.
29
+ this.#channels =
30
+ config?.channels && config.channels.length > 0
31
+ ? config.channels
32
+ : [new ConsoleChannel("pretty")];
33
+ const channels = this.#channels;
34
+ // Forward per-module level overrides (config.logger.modules) — Logger.log
35
+ // gates each module's level off config.modules?.[module]; dropping it here
36
+ // silently disables every per-module override in production.
37
+ const modules = config?.modules;
38
+ this.app.container.singleton(Logger, () => new Logger({ level, channels, modules }));
27
39
  this.app.container.singleton("logger", () => {
28
40
  return this.app.container.resolve(Logger);
29
41
  });
@@ -31,5 +43,12 @@ export default class SpectrumProvider {
31
43
  async boot() {
32
44
  setLogger(this.app.container.resolve(Logger));
33
45
  }
46
+ /** Release channel resources (e.g. FileChannel WriteStreams) on shutdown. */
47
+ async shutdown() {
48
+ for (const channel of this.#channels) {
49
+ if (hasClose(channel))
50
+ channel.close();
51
+ }
52
+ }
34
53
  }
35
54
  //# sourceMappingURL=SpectrumProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpectrumProvider.js","sourceRoot":"","sources":["../src/SpectrumProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;AAEnE,SAAS,eAAe,CAAC,GAAuB;IAC/C,IAAI,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,GAAe,CAAC;IACzD,OAAO,MAAM,CAAC;AACf,CAAC;AAgBD,MAAM,CAAC,OAAO,OAAO,gBAAgB;IACd;IAAtB,YAAsB,GAAuB;QAAvB,QAAG,GAAH,GAAG,CAAoB;IAAG,CAAC;IAEjD,QAAQ;QACP,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE;YACzC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAuB,QAAQ,CAAC,CAAC;YACnE,MAAM,KAAK,GACV,MAAM,EAAE,KAAK,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;gBAC9C,CAAC,CAAC,MAAM,CAAC,KAAK;gBACd,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC3C,OAAO,IAAI,MAAM,CAAC;gBACjB,KAAK;gBACL,QAAQ,EAAE,CAAC,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;aACxC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAS,MAAM,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI;QACT,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAS,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;CACD"}
1
+ {"version":3,"file":"SpectrumProvider.js","sourceRoot":"","sources":["../src/SpectrumProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;AAEnE,SAAS,eAAe,CAAC,GAAuB;IAC/C,IAAI,GAAG,IAAI,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,GAAe,CAAC;IACzD,OAAO,MAAM,CAAC;AACf,CAAC;AAED,8FAA8F;AAC9F,SAAS,QAAQ,CAAC,EAAc;IAC/B,OAAO,OAAO,IAAI,EAAE,IAAI,OAAO,EAAE,CAAC,KAAK,KAAK,UAAU,CAAC;AACxD,CAAC;AAgBD,MAAM,CAAC,OAAO,OAAO,gBAAgB;IAGd;IAFtB,SAAS,GAAiB,EAAE,CAAC;IAE7B,YAAsB,GAAuB;QAAvB,QAAG,GAAH,GAAG,CAAoB;IAAG,CAAC;IAEjD,QAAQ;QACP,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAqB,QAAQ,CAAC,CAAC;QACjE,MAAM,KAAK,GACV,MAAM,EAAE,KAAK,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC;YAC9C,CAAC,CAAC,MAAM,CAAC,KAAK;YACd,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,0EAA0E;QAC1E,qEAAqE;QACrE,0EAA0E;QAC1E,IAAI,CAAC,SAAS;YACb,MAAM,EAAE,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;gBAC7C,CAAC,CAAC,MAAM,CAAC,QAAQ;gBACjB,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAEhC,0EAA0E;QAC1E,2EAA2E;QAC3E,6DAA6D;QAC7D,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAC3B,MAAM,EACN,GAAG,EAAE,CAAC,IAAI,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAC9C,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE,GAAG,EAAE;YAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAS,MAAM,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,IAAI;QACT,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAS,MAAM,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,6EAA6E;IAC7E,KAAK,CAAC,QAAQ;QACb,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,IAAI,QAAQ,CAAC,OAAO,CAAC;gBAAE,OAAO,CAAC,KAAK,EAAE,CAAC;QACxC,CAAC;IACF,CAAC;CACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"FileChannel.d.ts","sourceRoot":"","sources":["../../src/channels/FileChannel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,WAAY,YAAW,UAAU;;IAC7C,IAAI,SAAU;gBAUF,MAAM,EAAE,iBAAiB;IAMrC,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IA6H5B,KAAK,IAAI,IAAI;CAMb"}
1
+ {"version":3,"file":"FileChannel.d.ts","sourceRoot":"","sources":["../../src/channels/FileChannel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,WAAY,YAAW,UAAU;;IAC7C,IAAI,SAAU;gBAUF,MAAM,EAAE,iBAAiB;IAMrC,KAAK,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAwI5B,KAAK,IAAI,IAAI;CAMb"}
@@ -40,6 +40,17 @@ export class FileChannel {
40
40
  process.stderr.write(`[Spectrum] FileChannel: failed to open '${this.#filePath}'\n`);
41
41
  return;
42
42
  }
43
+ // A prior failed rotation strands buffered lines while #stream is
44
+ // null (the rotation's #flushPending early-returns on no stream).
45
+ // Now that we've reopened, drain them — in order, before the new
46
+ // line — instead of losing them until the next rotation happens to
47
+ // fire. #flushPending may itself trigger a rotation if a buffered
48
+ // line overflows, so re-check before writing the current line.
49
+ this.#flushPending();
50
+ if (this.#rotating) {
51
+ this.#pending.push(line);
52
+ return;
53
+ }
43
54
  }
44
55
  if (this.#currentSize + bytes > this.#maxSize) {
45
56
  this.#pending.push(line);
@@ -1 +1 @@
1
- {"version":3,"file":"FileChannel.js","sourceRoot":"","sources":["../../src/channels/FileChannel.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,GAAG,MAAM,kBAAkB,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AASlC,MAAM,OAAO,WAAW;IACvB,IAAI,GAAG,MAAM,CAAC;IACd,SAAS,CAAS;IAClB,QAAQ,CAAS;IACjB,SAAS,CAAS;IAClB,OAAO,GAA0B,IAAI,CAAC;IACtC,YAAY,GAAG,CAAC,CAAC;IACjB,SAAS,GAAG,KAAK,CAAC;IAClB,SAAS,GAAG,KAAK,CAAC;IAClB,QAAQ,GAAa,EAAE,CAAC;IAExB,YAAY,MAAyB;QACpC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,YAAY,IAAI,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;QACxD,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,KAAe;QACpB,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAE9C,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO;QACR,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACnB,IAAI,CAAC;gBACJ,IAAI,CAAC,WAAW,EAAE,CAAC;YACpB,CAAC;YAAC,MAAM,CAAC;gBACR,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,2CAA2C,IAAI,CAAC,SAAS,KAAK,CAC9D,CAAC;gBACF,OAAO;YACR,CAAC;QACF,CAAC;QAED,IAAI,IAAI,CAAC,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC/C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzB,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzB,OAAO;QACR,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzB,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC;QAC5B,CAAC;IACF,CAAC;IAED,WAAW;QACV,0EAA0E;QAC1E,iFAAiF;QACjF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACzC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACvC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACvB,CAAC;QACD,IAAI,CAAC;YACJ,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACR,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,wCAAwC,GAAG,CAAC,OAAO,IAAI,CACvD,CAAC;QACH,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,iBAAiB;QAChB,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,cAAc,CAAC,GAAG,EAAE;YACnB,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC3B,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;gBACvB,IAAI,CAAC,aAAa,EAAE,CAAC;YACtB,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,OAAO;QACZ,IAAI,CAAC;YACJ,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YAE1B,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;YACzD,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAEtC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;gBACjE,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxC,IAAI,CAAC;oBACJ,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC5B,CAAC;gBAAC,MAAM,CAAC;oBACR,4BAA4B;gBAC7B,CAAC;YACF,CAAC;YAED,IAAI,CAAC;gBACJ,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC;YACzD,CAAC;YAAC,MAAM,CAAC;gBACR,oBAAoB;YACrB,CAAC;YAED,IAAI,CAAC,WAAW,EAAE,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACR,IAAI,CAAC;gBACJ,IAAI,CAAC,WAAW,EAAE,CAAC;YACpB,CAAC;YAAC,MAAM,CAAC;gBACR,OAAO,CAAC,MAAM,CAAC,KAAK,CACnB,mEAAmE,IAAI,CAAC,SAAS,KAAK,CACtF,CAAC;gBACF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACrB,CAAC;QACF,CAAC;IACF,CAAC;IAED,aAAa;QACZ,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI;gBAAE,SAAS;YACpB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC9C,IAAI,IAAI,CAAC,YAAY,GAAG,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC/C,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAC5B,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBACzB,OAAO;YACR,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzB,IAAI,CAAC,YAAY,IAAI,KAAK,CAAC;QAC5B,CAAC;IACF,CAAC;IAED,KAAK,CAAC,YAAY;QACjB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC9B,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK;QACJ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACnB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACrB,CAAC;IACF,CAAC;CACD"}
1
+ {"version":3,"file":"FileChannel.js","sourceRoot":"","sources":["../../src/channels/FileChannel.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA,UAAU,QAAQ;IACjB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,SAAS,CACR,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;CACjB;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAYjE"}
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAAA,UAAU,QAAQ;IACjB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,SAAS,CACR,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAC3B,OAAO,CAAC,IAAI,CAAC,CAAC;CACjB;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBjE"}
package/dist/configure.js CHANGED
@@ -1,10 +1,14 @@
1
1
  export async function configure(codemods) {
2
2
  await codemods.addProvider("@c9up/spectrum/provider");
3
- await codemods.writeFile("config/logger.ts", `import { defineConfig } from '@c9up/spectrum'
3
+ await codemods.writeFile("config/logger.ts", `import { ConsoleChannel, defineConfig, type LogLevel } from '@c9up/spectrum'
4
4
 
5
5
  export default defineConfig({
6
- level: process.env.LOG_LEVEL ?? 'info',
7
- channels: ['console'],
6
+ // LOG_LEVEL is an untyped env string — narrow it to the LogLevel union so the
7
+ // generated config typechecks against defineConfig's strict \`level\`.
8
+ level: (process.env.LOG_LEVEL as LogLevel | undefined) ?? 'info',
9
+ // Channels are LogChannel instances. Add a FileChannel for disk logs:
10
+ // new FileChannel({ path: 'storage/logs/app.log' })
11
+ channels: [new ConsoleChannel('pretty')],
8
12
  })
9
13
  `);
10
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAkB;IACjD,MAAM,QAAQ,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC;IACtD,MAAM,QAAQ,CAAC,SAAS,CACvB,kBAAkB,EAClB;;;;;;CAMD,CACC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"configure.js","sourceRoot":"","sources":["../src/configure.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAkB;IACjD,MAAM,QAAQ,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC;IACtD,MAAM,QAAQ,CAAC,SAAS,CACvB,kBAAkB,EAClB;;;;;;;;;;CAUD,CACC,CAAC;AACH,CAAC"}
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * import logger from '@c9up/spectrum/services/main'
6
6
  *
7
- * logger.info({ userId }, 'user logged in')
7
+ * logger.info('user logged in', { userId }) // message-first; data + `err` serialized
8
8
  *
9
9
  * Populated by `SpectrumProvider.boot()`.
10
10
  */
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * import logger from '@c9up/spectrum/services/main'
6
6
  *
7
- * logger.info({ userId }, 'user logged in')
7
+ * logger.info('user logged in', { userId }) // message-first; data + `err` serialized
8
8
  *
9
9
  * Populated by `SpectrumProvider.boot()`.
10
10
  */
@@ -106,6 +106,22 @@ function safeStringify(value) {
106
106
  }
107
107
  }
108
108
  function deepClone(value) {
109
- return structuredClone(value);
109
+ try {
110
+ return structuredClone(value);
111
+ }
112
+ catch {
113
+ // `data` can carry functions / class instances that structuredClone
114
+ // rejects (DataCloneError). For a test capture a perfect clone isn't
115
+ // needed — round-trip through safeStringify (functions → "<function>",
116
+ // circular → "<circular>") so the entry is still CAPTURED and assertable
117
+ // instead of thrown away (the throw propagated into Logger.log's swallow,
118
+ // dropping the entry and making assertLogged fail with "no entry").
119
+ try {
120
+ return JSON.parse(safeStringify(value));
121
+ }
122
+ catch {
123
+ return value;
124
+ }
125
+ }
110
126
  }
111
127
  //# sourceMappingURL=FakeLogger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FakeLogger.js","sourceRoot":"","sources":["../../src/testing/FakeLogger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FakeLogger.js","sourceRoot":"","sources":["../../src/testing/FakeLogger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAeH,MAAM,OAAO,UAAU;IACb,IAAI,GAAG,MAAM,CAAC;IACvB,SAAS,GAAe,EAAE,CAAC;IAE3B,KAAK,CAAC,KAAe;QACpB,+DAA+D;QAC/D,0DAA0D;QAC1D,2DAA2D;QAC3D,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YACnB,GAAG,KAAK;YACR,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SACpD,CAAC,CAAC;IACJ,CAAC;IAED,kDAAkD;IAClD,SAAS;QACR,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,GAAG,CAAC;YACJ,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SAC5C,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK;QACJ,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,YAAY,CAAC,KAAe,EAAE,SAAkC;QAC/D,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAC5C,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO;QACvC,MAAM,IAAI,KAAK,CACd,wBAAwB,KAAK,IAAI,iBAAiB,CAAC,SAAS,CAAC,0CAA0C,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CACzI,CAAC;IACH,CAAC;IAED,eAAe,CAAC,KAAe,EAAE,SAAkC;QAClE,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO;QACxC,MAAM,IAAI,KAAK,CACd,2BAA2B,KAAK,IAAI,iBAAiB,CAAC,SAAS,CAAC,oDAAoD,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CACtJ,CAAC;IACH,CAAC;CACD;AAED,SAAS,WAAW,CACnB,KAAe,EACf,SAA6C;IAE7C,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;QACrC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC;IACjC,CAAC;IACD,mEAAmE;IACnE,iEAAiE;IACjE,6DAA6D;IAC7D,IAAI,SAAS,CAAC,UAAU,KAAK,EAAE,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACd,uGAAuG,CACvG,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC,EAAE,EAAE;QACZ,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK;YAAE,OAAO,KAAK,CAAC;QACpC,IACC,SAAS,CAAC,UAAU,KAAK,SAAS;YAClC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,CAAC,EACxC,CAAC;YACF,OAAO,KAAK,CAAC;QACd,CAAC;QACD,IAAI,SAAS,CAAC,WAAW,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7D,OAAO,KAAK,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CACzB,SAA6C;IAE7C,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACvC,IAAI,OAAO,SAAS,KAAK,UAAU;QAAE,OAAO,wBAAwB,CAAC;IACrE,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC;QACtC,OAAO,kCAAkC,CAAC;IAC3C,OAAO,KAAK,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;AACxC,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAoB;IAC7C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,kBAAkB,CAAC;IACrD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CACzB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,MAAM,cAAc,CAAC,CAAC,OAAO,GAAG,CAC3E,CAAC;IACF,OAAO,aAAa,QAAQ,CAAC,MAAM,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED;;;wDAGwD;AACxD,SAAS,aAAa,CAAC,KAAc;IACpC,MAAM,IAAI,GAAG,IAAI,OAAO,EAAU,CAAC;IACnC,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAU,EAAE,EAAE;YACjD,IAAI,OAAO,CAAC,KAAK,UAAU;gBAAE,OAAO,YAAY,CAAC;YACjD,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;gBACzC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAE,OAAO,YAAY,CAAC;gBACrC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACb,CAAC;YACD,OAAO,CAAC,CAAC;QACV,CAAC,CAAC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,mBAAmB,CAAC;IAC5B,CAAC;AACF,CAAC;AAED,SAAS,SAAS,CAAI,KAAQ;IAC7B,IAAI,CAAC;QACJ,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACR,oEAAoE;QACpE,qEAAqE;QACrE,uEAAuE;QACvE,yEAAyE;QACzE,0EAA0E;QAC1E,oEAAoE;QACpE,IAAI,CAAC;YACJ,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACR,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;AACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c9up/spectrum",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Spectrum — structured logging for the Ream framework",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/Logger.ts CHANGED
@@ -9,6 +9,28 @@ import { LOG_LEVEL_ORDER } from "./types.js";
9
9
 
10
10
  export type { LogLevel };
11
11
 
12
+ /**
13
+ * Serialize an `err`/`error` Error in the log data into a plain `{ name, message,
14
+ * stack }` (AdonisJS/pino `err` serializer parity) — Errors don't JSON-stringify
15
+ * usefully otherwise. Other data passes through untouched. Returns a new object;
16
+ * the caller's data is not mutated.
17
+ */
18
+ function serializeErr(
19
+ data?: Record<string, unknown>,
20
+ ): Record<string, unknown> | undefined {
21
+ if (!data) return data;
22
+ for (const key of ["err", "error"]) {
23
+ const v = data[key];
24
+ if (v instanceof Error) {
25
+ return {
26
+ ...data,
27
+ [key]: { name: v.name, message: v.message, stack: v.stack },
28
+ };
29
+ }
30
+ }
31
+ return data;
32
+ }
33
+
12
34
  export class Logger {
13
35
  private config: LogConfig;
14
36
  private module: string;
@@ -83,7 +105,7 @@ export class Logger {
83
105
  module: this.module,
84
106
  correlationId: this.correlationId,
85
107
  timestamp: new Date().toISOString(),
86
- data,
108
+ data: serializeErr(data),
87
109
  };
88
110
 
89
111
  for (const channel of this.config.channels) {
@@ -44,8 +44,11 @@ export function parseRustLog(line: string): LogEntry | null {
44
44
  /**
45
45
  * Create a bridge that captures stderr and routes Rust logs to Spectrum channels.
46
46
  *
47
- * Usage:
48
- * const bridge = createRustLogBridge(logger.config.channels)
47
+ * Usage — pass the same channels array you built the Logger with (Logger.config
48
+ * is private, so reuse the array rather than reaching into the instance):
49
+ * const channels = [new ConsoleChannel('pretty')]
50
+ * const logger = new Logger({ level: 'info', channels })
51
+ * const bridge = createRustLogBridge(channels)
49
52
  * bridge.start()
50
53
  * // ... Rust crates emit to stderr
51
54
  * bridge.stop()
@@ -89,14 +92,24 @@ export function createRustLogBridge(channels: LogChannel[]): {
89
92
  const str =
90
93
  typeof chunk === "string" ? chunk : new TextDecoder().decode(chunk);
91
94
  let hadRustLog = false;
92
- const passthroughLines: string[] = [];
93
-
94
- // Try to parse as Rust log if it matches, route to channels
95
- for (const line of str.split("\n")) {
96
- const trimmed = line.trim();
97
- if (!trimmed) continue;
98
-
99
- const entry = parseRustLog(trimmed);
95
+ // Rebuild passthrough as one byte-faithful string instead of
96
+ // re-emitting each line with an appended "\n". split("\n") drops
97
+ // the separator, so every segment except a trailing fragment (the
98
+ // chunk didn't end in "\n") was newline-terminated originally —
99
+ // reattach accordingly. This preserves blank lines and a partial
100
+ // no-newline trailing write verbatim, and keeps passthrough lines
101
+ // in their original order.
102
+ let passthrough = "";
103
+ const segments = str.split("\n");
104
+ for (let i = 0; i < segments.length; i++) {
105
+ const seg = segments[i] ?? "";
106
+ const isTrailingFragment = i === segments.length - 1;
107
+ const original = isTrailingFragment ? seg : `${seg}\n`;
108
+ const trimmed = seg.trim();
109
+ // Only a complete, non-empty line is a Rust-log candidate. A
110
+ // trailing fragment is a partial write — never parse or drop it.
111
+ const entry =
112
+ trimmed && !isTrailingFragment ? parseRustLog(trimmed) : null;
100
113
  if (entry) {
101
114
  hadRustLog = true;
102
115
  for (const channel of channels) {
@@ -110,7 +123,7 @@ export function createRustLogBridge(channels: LogChannel[]): {
110
123
  }
111
124
  }
112
125
  } else {
113
- passthroughLines.push(line);
126
+ passthrough += original;
114
127
  }
115
128
  }
116
129
 
@@ -119,8 +132,8 @@ export function createRustLogBridge(channels: LogChannel[]): {
119
132
  return origWrite(chunk as never, ...(args as []));
120
133
  }
121
134
 
122
- for (const line of passthroughLines) {
123
- origWrite(`${line}\n`);
135
+ if (passthrough) {
136
+ origWrite(passthrough);
124
137
  }
125
138
 
126
139
  return true;
@@ -1,7 +1,7 @@
1
1
  import { ConsoleChannel } from "./channels/ConsoleChannel.js";
2
2
  import { Logger } from "./Logger.js";
3
3
  import { setLogger } from "./services/main.js";
4
- import type { LogLevel } from "./types.js";
4
+ import type { LogChannel, LogConfig, LogLevel } from "./types.js";
5
5
  import { LOG_LEVEL_ORDER } from "./types.js";
6
6
 
7
7
  const VALID_LEVELS = new Set<string>(Object.keys(LOG_LEVEL_ORDER));
@@ -11,6 +11,11 @@ function resolveLogLevel(raw: string | undefined): LogLevel {
11
11
  return "info";
12
12
  }
13
13
 
14
+ /** A channel that owns a resource (e.g. a FileChannel WriteStream) to release on shutdown. */
15
+ function hasClose(ch: LogChannel): ch is LogChannel & { close(): void } {
16
+ return "close" in ch && typeof ch.close === "function";
17
+ }
18
+
14
19
  interface SpectrumContainer {
15
20
  singleton(token: unknown, factory: () => unknown): void;
16
21
  resolve<T = unknown>(token: unknown): T;
@@ -26,21 +31,33 @@ export interface SpectrumAppContext {
26
31
  }
27
32
 
28
33
  export default class SpectrumProvider {
34
+ #channels: LogChannel[] = [];
35
+
29
36
  constructor(protected app: SpectrumAppContext) {}
30
37
 
31
38
  register() {
32
- this.app.container.singleton(Logger, () => {
33
- const config = this.app.config.get<{ level?: LogLevel }>("logger");
34
- const level =
35
- config?.level && VALID_LEVELS.has(config.level)
36
- ? config.level
37
- : resolveLogLevel(process.env.LOG_LEVEL);
38
- return new Logger({
39
- level,
40
- channels: [new ConsoleChannel("pretty")],
41
- });
42
- });
39
+ const config = this.app.config.get<Partial<LogConfig>>("logger");
40
+ const level =
41
+ config?.level && VALID_LEVELS.has(config.level)
42
+ ? config.level
43
+ : resolveLogLevel(process.env.LOG_LEVEL);
44
+ // Honour the configured channels (the whole point of LogConfig.channels);
45
+ // fall back to a pretty console channel only when none are supplied.
46
+ // Previously this was hardcoded and config.channels was silently ignored.
47
+ this.#channels =
48
+ config?.channels && config.channels.length > 0
49
+ ? config.channels
50
+ : [new ConsoleChannel("pretty")];
51
+ const channels = this.#channels;
43
52
 
53
+ // Forward per-module level overrides (config.logger.modules) — Logger.log
54
+ // gates each module's level off config.modules?.[module]; dropping it here
55
+ // silently disables every per-module override in production.
56
+ const modules = config?.modules;
57
+ this.app.container.singleton(
58
+ Logger,
59
+ () => new Logger({ level, channels, modules }),
60
+ );
44
61
  this.app.container.singleton("logger", () => {
45
62
  return this.app.container.resolve<Logger>(Logger);
46
63
  });
@@ -49,4 +66,11 @@ export default class SpectrumProvider {
49
66
  async boot() {
50
67
  setLogger(this.app.container.resolve<Logger>(Logger));
51
68
  }
69
+
70
+ /** Release channel resources (e.g. FileChannel WriteStreams) on shutdown. */
71
+ async shutdown() {
72
+ for (const channel of this.#channels) {
73
+ if (hasClose(channel)) channel.close();
74
+ }
75
+ }
52
76
  }
@@ -54,6 +54,17 @@ export class FileChannel implements LogChannel {
54
54
  );
55
55
  return;
56
56
  }
57
+ // A prior failed rotation strands buffered lines while #stream is
58
+ // null (the rotation's #flushPending early-returns on no stream).
59
+ // Now that we've reopened, drain them — in order, before the new
60
+ // line — instead of losing them until the next rotation happens to
61
+ // fire. #flushPending may itself trigger a rotation if a buffered
62
+ // line overflows, so re-check before writing the current line.
63
+ this.#flushPending();
64
+ if (this.#rotating) {
65
+ this.#pending.push(line);
66
+ return;
67
+ }
57
68
  }
58
69
 
59
70
  if (this.#currentSize + bytes > this.#maxSize) {
package/src/configure.ts CHANGED
@@ -12,11 +12,15 @@ export async function configure(codemods: Codemods): Promise<void> {
12
12
  await codemods.addProvider("@c9up/spectrum/provider");
13
13
  await codemods.writeFile(
14
14
  "config/logger.ts",
15
- `import { defineConfig } from '@c9up/spectrum'
15
+ `import { ConsoleChannel, defineConfig, type LogLevel } from '@c9up/spectrum'
16
16
 
17
17
  export default defineConfig({
18
- level: process.env.LOG_LEVEL ?? 'info',
19
- channels: ['console'],
18
+ // LOG_LEVEL is an untyped env string — narrow it to the LogLevel union so the
19
+ // generated config typechecks against defineConfig's strict \`level\`.
20
+ level: (process.env.LOG_LEVEL as LogLevel | undefined) ?? 'info',
21
+ // Channels are LogChannel instances. Add a FileChannel for disk logs:
22
+ // new FileChannel({ path: 'storage/logs/app.log' })
23
+ channels: [new ConsoleChannel('pretty')],
20
24
  })
21
25
  `,
22
26
  );
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * import logger from '@c9up/spectrum/services/main'
6
6
  *
7
- * logger.info({ userId }, 'user logged in')
7
+ * logger.info('user logged in', { userId }) // message-first; data + `err` serialized
8
8
  *
9
9
  * Populated by `SpectrumProvider.boot()`.
10
10
  */
@@ -136,5 +136,19 @@ function safeStringify(value: unknown): string {
136
136
  }
137
137
 
138
138
  function deepClone<T>(value: T): T {
139
- return structuredClone(value);
139
+ try {
140
+ return structuredClone(value);
141
+ } catch {
142
+ // `data` can carry functions / class instances that structuredClone
143
+ // rejects (DataCloneError). For a test capture a perfect clone isn't
144
+ // needed — round-trip through safeStringify (functions → "<function>",
145
+ // circular → "<circular>") so the entry is still CAPTURED and assertable
146
+ // instead of thrown away (the throw propagated into Logger.log's swallow,
147
+ // dropping the entry and making assertLogged fail with "no entry").
148
+ try {
149
+ return JSON.parse(safeStringify(value));
150
+ } catch {
151
+ return value;
152
+ }
153
+ }
140
154
  }