@blamejs/core 0.4.11 → 0.4.12

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/CHANGELOG.md CHANGED
@@ -8,6 +8,7 @@ upgrading across more than a few patches at a time.
8
8
 
9
9
  ## v0.4.x
10
10
 
11
+ - **0.4.11** (2026-04-30) — b.cache: bytes-cap eviction, sliding TTL, tag invalidation
11
12
  - **0.4.10** (2026-04-30) — bodyParser multipart: fileFilter + per-field maxBytes/mimeTypes
12
13
  - **0.4.9** (2026-04-30) — Origin-Agent-Cluster + DNS-Prefetch-Control headers; b.auth.lockout primitive
13
14
  - **0.4.8** (2026-04-30) — wiki SEO surface: per-page OG / Twitter / JSON-LD + sitemap.xml + robots.txt
package/lib/log.js CHANGED
@@ -9,7 +9,8 @@
9
9
  *
10
10
  * Each line is one JSON object on a single line, terminated with `\n`.
11
11
  * Levels: debug (0) < info (1) < warn (2) < error (3) < fatal (4).
12
- * info-and-below routes to stdout; warn-and-up routes to stderr.
12
+ * Default routing: debug / info / warn stdout; error / fatal stderr.
13
+ * Multi-sink config (`sinks: [...]`) takes full control of routing.
13
14
  *
14
15
  * var log = b.log.create({
15
16
  * level: "info", // env LOG_LEVEL > opts.level > "info"
@@ -17,6 +18,19 @@
17
18
  * redact: true, // run extras through lib/redact
18
19
  * });
19
20
  *
21
+ * // Multi-sink: each sink gets every line at-or-above its own level.
22
+ * // Default (no `sinks` opt) splits info-and-below to stdout and
23
+ * // warn-and-up to stderr — same as before.
24
+ * var log = b.log.create({
25
+ * level: "debug",
26
+ * sinks: [
27
+ * { stream: process.stdout, level: "info" },
28
+ * { stream: fs.createWriteStream("./logs/debug.log"), level: "debug" },
29
+ * { stream: fs.createWriteStream("./logs/errors.log"), level: "error" },
30
+ * ],
31
+ * });
32
+ * // sinks: [...] is mutually exclusive with destination/errorDestination.
33
+ *
20
34
  * log.info("user logged in", { userId: "u-1" });
21
35
  * log.error("payment failed", { orderId, err: e.message });
22
36
  *
@@ -116,10 +130,56 @@ function _mergeExtras(into, extras, redactExtras) {
116
130
  return clobberAttempt;
117
131
  }
118
132
 
133
+ function _resolveSinks(opts) {
134
+ // Three input shapes — pick exactly one:
135
+ // (a) opts.sinks: [{ stream, level }, ...]
136
+ // (b) opts.destination + opts.errorDestination (legacy two-sink split)
137
+ // (c) neither — defaults to stdout for info-and-below, stderr for warn-and-up
138
+ if (Array.isArray(opts.sinks)) {
139
+ if (opts.destination !== undefined || opts.errorDestination !== undefined) {
140
+ throw new LogError("log/conflicting-sinks",
141
+ "log.create: pass either { sinks: [...] } OR { destination, errorDestination }, not both");
142
+ }
143
+ if (opts.sinks.length === 0) {
144
+ throw new LogError("log/no-sinks",
145
+ "log.create: sinks: [] would silently drop every line — pass at least one sink");
146
+ }
147
+ return opts.sinks.map(function (s, i) {
148
+ if (!s || typeof s !== "object") {
149
+ throw new LogError("log/bad-sink", "sinks[" + i + "]: expected object with { stream, level? }");
150
+ }
151
+ var allowed = ["stream", "level"];
152
+ var keys = Object.keys(s);
153
+ for (var j = 0; j < keys.length; j++) {
154
+ if (allowed.indexOf(keys[j]) === -1) {
155
+ throw new LogError("log/bad-sink",
156
+ "sinks[" + i + "]: unknown key '" + keys[j] + "' (allowed: " + allowed.join(", ") + ")");
157
+ }
158
+ }
159
+ var stream = _normalizeDestination(s.stream, null);
160
+ if (!stream) {
161
+ throw new LogError("log/bad-sink", "sinks[" + i + "]: stream is required");
162
+ }
163
+ // Per-sink level: missing → no filter beyond the global; present → must be valid.
164
+ var minLevel = (s.level === undefined) ? null : _normalizeLevel(s.level);
165
+ return { stream: stream, minLevel: minLevel };
166
+ });
167
+ }
168
+ // Legacy / default — synthesize the two-sink split.
169
+ var stdoutDest = _normalizeDestination(opts.destination, process.stdout);
170
+ var stderrDest = _normalizeDestination(opts.errorDestination, process.stderr);
171
+ return [
172
+ // Order matters for emit fan-out: stdout sink catches debug-info-warn;
173
+ // stderr catches error-and-up. Existing behavior — same boundary.
174
+ { stream: stdoutDest, minLevel: null, _maxLevelExclusive: LEVELS.error },
175
+ { stream: stderrDest, minLevel: LEVELS.error },
176
+ ];
177
+ }
178
+
119
179
  function create(opts) {
120
180
  opts = opts || {};
121
181
  validateOpts(opts, [
122
- "level", "destination", "errorDestination",
182
+ "level", "destination", "errorDestination", "sinks",
123
183
  "format", "redact", "base", "clock",
124
184
  ], "b.log");
125
185
 
@@ -134,8 +194,7 @@ function create(opts) {
134
194
  level = LEVELS.info;
135
195
  }
136
196
 
137
- var stdoutDest = _normalizeDestination(opts.destination, process.stdout);
138
- var stderrDest = _normalizeDestination(opts.errorDestination, process.stderr);
197
+ var sinks = _resolveSinks(opts);
139
198
 
140
199
  var format = opts.format || "json"; // reserved for future formats
141
200
  if (format !== "json") {
@@ -197,9 +256,16 @@ function create(opts) {
197
256
  }) + "\n";
198
257
  }
199
258
 
200
- var dest = (LEVELS[levelName] >= LEVELS.error) ? stderrDest : stdoutDest;
201
- try { dest.write(line); }
202
- catch (_e) { /* destination write best-effort — never throw out of a log call */ }
259
+ var lvlNum = LEVELS[levelName];
260
+ for (var s = 0; s < sinks.length; s++) {
261
+ var sink = sinks[s];
262
+ if (sink.minLevel !== null && lvlNum < sink.minLevel) continue;
263
+ // Legacy default-sinks split uses an exclusive upper bound so the
264
+ // stdout sink catches only info-and-below (warn+ goes to stderr).
265
+ if (sink._maxLevelExclusive !== undefined && lvlNum >= sink._maxLevelExclusive) continue;
266
+ try { sink.stream.write(line); }
267
+ catch (_e) { /* sink write best-effort — never throw out of a log call */ }
268
+ }
203
269
  }
204
270
 
205
271
  function _makeInstance(boundChain) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/core",
3
- "version": "0.4.11",
3
+ "version": "0.4.12",
4
4
  "description": "The Node framework that owns its stack.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "blamejs contributors",