@emdzej/bimmerz-logger 0.1.0

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.
Files changed (47) hide show
  1. package/LICENSE +131 -0
  2. package/README.md +235 -0
  3. package/dist/categories.d.ts +21 -0
  4. package/dist/categories.d.ts.map +1 -0
  5. package/dist/categories.js +36 -0
  6. package/dist/categories.js.map +1 -0
  7. package/dist/index.d.ts +20 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +25 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/levels.d.ts +21 -0
  12. package/dist/levels.d.ts.map +1 -0
  13. package/dist/levels.js +30 -0
  14. package/dist/levels.js.map +1 -0
  15. package/dist/logger.d.ts +49 -0
  16. package/dist/logger.d.ts.map +1 -0
  17. package/dist/logger.js +136 -0
  18. package/dist/logger.js.map +1 -0
  19. package/dist/logger.test.d.ts +2 -0
  20. package/dist/logger.test.d.ts.map +1 -0
  21. package/dist/logger.test.js +199 -0
  22. package/dist/logger.test.js.map +1 -0
  23. package/dist/sinks/buffer.d.ts +23 -0
  24. package/dist/sinks/buffer.d.ts.map +1 -0
  25. package/dist/sinks/buffer.js +44 -0
  26. package/dist/sinks/buffer.js.map +1 -0
  27. package/dist/sinks/console.d.ts +34 -0
  28. package/dist/sinks/console.d.ts.map +1 -0
  29. package/dist/sinks/console.js +106 -0
  30. package/dist/sinks/console.js.map +1 -0
  31. package/dist/sinks/multi.d.ts +13 -0
  32. package/dist/sinks/multi.d.ts.map +1 -0
  33. package/dist/sinks/multi.js +26 -0
  34. package/dist/sinks/multi.js.map +1 -0
  35. package/dist/sinks/null.d.ts +10 -0
  36. package/dist/sinks/null.d.ts.map +1 -0
  37. package/dist/sinks/null.js +15 -0
  38. package/dist/sinks/null.js.map +1 -0
  39. package/dist/sinks/pino.d.ts +37 -0
  40. package/dist/sinks/pino.d.ts.map +1 -0
  41. package/dist/sinks/pino.js +39 -0
  42. package/dist/sinks/pino.js.map +1 -0
  43. package/dist/types.d.ts +121 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +7 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +47 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"null.d.ts","sourceRoot":"","sources":["../../src/sinks/null.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,wBAAgB,QAAQ,IAAI,IAAI,CAM/B"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Null sink — drops every record. Use in tests so logging code
3
+ * doesn't pollute the test runner output, or in production code
4
+ * paths where you want to disable logging entirely without going
5
+ * through `silent` level (e.g. a "verbose" CLI flag that controls
6
+ * level but a separate "quiet" flag that controls the sink).
7
+ */
8
+ export function nullSink() {
9
+ return {
10
+ write() {
11
+ /* no-op */
12
+ },
13
+ };
14
+ }
15
+ //# sourceMappingURL=null.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"null.js","sourceRoot":"","sources":["../../src/sinks/null.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,UAAU,QAAQ;IACtB,OAAO;QACL,KAAK;YACH,WAAW;QACb,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * pino-backed sink — Node-only entry point. Exposed under the
3
+ * subpath `@emdzej/bimmerz-logger/sinks/pino` so web bundles
4
+ * tree-shake out pino entirely. Apps that want JSON logs, file
5
+ * destinations, or pino-pretty render through this.
6
+ *
7
+ * Maps each `LogRecord` onto a single pino call, preserving the
8
+ * level, bindings, message, and category (carried as the
9
+ * `category` field in the pino object). We re-create the pino
10
+ * instance on every config-applying call rather than caching one —
11
+ * cheap, and keeps the API simple ("here's pino's options, build
12
+ * me a sink").
13
+ */
14
+ import pino from 'pino';
15
+ import type { Sink } from '../types.js';
16
+ export interface PinoSinkOptions {
17
+ /**
18
+ * Render through pino-pretty when set. Node-only; ignored if
19
+ * `destination` is also set (pino-pretty + file requires its
20
+ * own transport stack).
21
+ */
22
+ pretty?: boolean;
23
+ /** Write to a file path instead of stdout. */
24
+ destination?: string;
25
+ /**
26
+ * Optional name attached to every record as `name` — pino's
27
+ * convention. Defaults to "bimmerz".
28
+ */
29
+ name?: string;
30
+ /**
31
+ * Any extra pino options. Merged AFTER the ones we set, so
32
+ * callers can override anything.
33
+ */
34
+ pinoOptions?: pino.LoggerOptions;
35
+ }
36
+ export declare function pinoSink(opts?: PinoSinkOptions): Sink;
37
+ //# sourceMappingURL=pino.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pino.d.ts","sourceRoot":"","sources":["../../src/sinks/pino.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,EAAa,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,WAAW,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC;CAClC;AAED,wBAAgB,QAAQ,CAAC,IAAI,GAAE,eAAoB,GAAG,IAAI,CAsBzD"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * pino-backed sink — Node-only entry point. Exposed under the
3
+ * subpath `@emdzej/bimmerz-logger/sinks/pino` so web bundles
4
+ * tree-shake out pino entirely. Apps that want JSON logs, file
5
+ * destinations, or pino-pretty render through this.
6
+ *
7
+ * Maps each `LogRecord` onto a single pino call, preserving the
8
+ * level, bindings, message, and category (carried as the
9
+ * `category` field in the pino object). We re-create the pino
10
+ * instance on every config-applying call rather than caching one —
11
+ * cheap, and keeps the API simple ("here's pino's options, build
12
+ * me a sink").
13
+ */
14
+ import pino from 'pino';
15
+ export function pinoSink(opts = {}) {
16
+ const transport = opts.destination
17
+ ? { target: 'pino/file', options: { destination: opts.destination } }
18
+ : opts.pretty
19
+ ? { target: 'pino-pretty', options: { colorize: true } }
20
+ : undefined;
21
+ const logger = pino({
22
+ name: opts.name ?? 'bimmerz',
23
+ // We do our own threshold filtering upstream; pino should
24
+ // accept everything and just format. Avoids double-checking
25
+ // the level threshold.
26
+ level: 'trace',
27
+ transport,
28
+ ...opts.pinoOptions,
29
+ });
30
+ return {
31
+ write(record) {
32
+ const bindings = { ...record.bindings };
33
+ if (record.category)
34
+ bindings.category = record.category;
35
+ logger[record.level](bindings, record.msg);
36
+ },
37
+ };
38
+ }
39
+ //# sourceMappingURL=pino.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pino.js","sourceRoot":"","sources":["../../src/sinks/pino.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AAwBxB,MAAM,UAAU,QAAQ,CAAC,OAAwB,EAAE;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW;QAChC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAoB,EAAE,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE;QAC9E,CAAC,CAAC,IAAI,CAAC,MAAM;YACX,CAAC,CAAC,EAAE,MAAM,EAAE,aAAsB,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;YACjE,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,MAAM,GAAG,IAAI,CAAC;QAClB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,SAAS;QAC5B,0DAA0D;QAC1D,4DAA4D;QAC5D,uBAAuB;QACvB,KAAK,EAAE,OAAO;QACd,SAAS;QACT,GAAG,IAAI,CAAC,WAAW;KACpB,CAAC,CAAC;IACH,OAAO;QACL,KAAK,CAAC,MAAiB;YACrB,MAAM,QAAQ,GAAG,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;YACxC,IAAI,MAAM,CAAC,QAAQ;gBAAE,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YACzD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7C,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Public types for `@emdzej/bimmerz-logger`. Apps and packages
3
+ * consume these — concrete implementations stay private to this
4
+ * package so we can swap them out without breaking call sites.
5
+ */
6
+ /**
7
+ * Severity ordering. `silent` blocks every line; `trace` lets
8
+ * everything through. Same names + ordering as pino so call-site
9
+ * idioms transfer 1:1.
10
+ */
11
+ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal' | 'silent';
12
+ /**
13
+ * Free-form key/value context that rides alongside the message.
14
+ * Each call passes its own bag; `Logger.child(bindings)` adds
15
+ * permanent bindings that merge with every subsequent call.
16
+ *
17
+ * Values are intentionally `unknown` — callers can pass primitives,
18
+ * objects, arrays, errors. Sinks decide how to render.
19
+ */
20
+ export type LogBindings = Record<string, unknown>;
21
+ /**
22
+ * One decoded log call, before any sink-side formatting.
23
+ *
24
+ * Sinks receive these and turn them into whatever the destination
25
+ * needs — JSON lines for pino/file, formatted strings for the
26
+ * browser console, structured objects for an in-memory ring.
27
+ */
28
+ export interface LogRecord {
29
+ /** Severity of this line (always above the effective threshold). */
30
+ level: LogLevel;
31
+ /**
32
+ * Dot-separated category the logger was created with, or `null`
33
+ * if the call came from the root logger (`getLogger()`).
34
+ */
35
+ category: string | null;
36
+ /**
37
+ * Merged bindings — `child()` permanent bindings plus any
38
+ * per-call bindings. May be `{}` but is never null.
39
+ */
40
+ bindings: LogBindings;
41
+ /** Human-readable message. */
42
+ msg: string;
43
+ /** `Date.now()` at the moment of the log call. */
44
+ time: number;
45
+ }
46
+ /**
47
+ * Destination for log records. Implementations are pluggable —
48
+ * console sink for browsers, pino sink for Node, ring-buffer sink
49
+ * for "Download log" UI features, etc. Compose multiple via
50
+ * `multiSink(a, b, …)`.
51
+ *
52
+ * `write` is sync because every consumer we have today writes
53
+ * synchronously (console.*, process.stdout, in-memory). If we
54
+ * ever need async sinks (network shipping, IndexedDB) we'll
55
+ * widen this.
56
+ */
57
+ export interface Sink {
58
+ write(record: LogRecord): void;
59
+ }
60
+ /**
61
+ * Logger handle handed out by `getLogger()`. Pino-shape API so
62
+ * existing call sites need no rewrites — but with `level` as a
63
+ * read-only getter (it reflects the live category-resolved
64
+ * threshold, not a per-instance mutable field). Use
65
+ * `configureLogger()` to change levels at runtime.
66
+ */
67
+ export interface Logger {
68
+ /** Currently-effective level for this logger's category. Read-only. */
69
+ readonly level: LogLevel;
70
+ trace(msg: string): void;
71
+ trace(bindings: LogBindings, msg: string): void;
72
+ debug(msg: string): void;
73
+ debug(bindings: LogBindings, msg: string): void;
74
+ info(msg: string): void;
75
+ info(bindings: LogBindings, msg: string): void;
76
+ warn(msg: string): void;
77
+ warn(bindings: LogBindings, msg: string): void;
78
+ error(msg: string): void;
79
+ error(bindings: LogBindings, msg: string): void;
80
+ fatal(msg: string): void;
81
+ fatal(bindings: LogBindings, msg: string): void;
82
+ /**
83
+ * Spawn a child logger that carries `bindings` on every call.
84
+ * Inherits the parent's category — the child's effective level
85
+ * still tracks the central config for that category.
86
+ */
87
+ child(bindings: LogBindings): Logger;
88
+ }
89
+ /**
90
+ * Central logger configuration. Apply via `configureLogger()`; the
91
+ * call is sticky and merges with the existing config. All loggers
92
+ * — including those handed out before the call — pick up the
93
+ * new settings immediately.
94
+ */
95
+ export interface LoggerConfig {
96
+ /**
97
+ * Default threshold when no `categories` entry matches. Applied
98
+ * to the root logger and to any category that has no rule.
99
+ */
100
+ level: LogLevel;
101
+ /**
102
+ * Per-category thresholds. Keys are dot-separated category
103
+ * paths; lookup walks up the path so a rule for `EDIABASX`
104
+ * applies to `EDIABASX.parser` and `EDIABASX.parser.lexer`
105
+ * unless a more specific rule wins.
106
+ *
107
+ * Example:
108
+ * ```
109
+ * { EDIABASX: 'debug', 'EDIABASX.parser': 'trace', INPAX: 'info' }
110
+ * ```
111
+ * — every `INPAX*` category at info; every `EDIABASX*` at debug
112
+ * except `EDIABASX.parser*` which is at trace.
113
+ */
114
+ categories?: Record<string, LogLevel>;
115
+ /**
116
+ * Where formatted records are written. Defaults to `consoleSink()`
117
+ * when omitted — works in both Node and browser, no dependencies.
118
+ */
119
+ sink?: Sink;
120
+ }
121
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAChB,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,OAAO,GACP,QAAQ,CAAC;AAEb;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,WAAW,SAAS;IACxB,oEAAoE;IACpE,KAAK,EAAE,QAAQ,CAAC;IAChB;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;;OAGG;IACH,QAAQ,EAAE,WAAW,CAAC;IACtB,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,IAAI;IACnB,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;CAChC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,MAAM;IACrB,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IAEzB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAEhD;;;;OAIG;IACH,KAAK,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,CAAC;CACtC;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,KAAK,EAAE,QAAQ,CAAC;IAEhB;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEtC;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;CACb"}
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Public types for `@emdzej/bimmerz-logger`. Apps and packages
3
+ * consume these — concrete implementations stay private to this
4
+ * package so we can swap them out without breaking call sites.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@emdzej/bimmerz-logger",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Shared structured logger for the bimmerz family — pino-shape API, hierarchical categories, runtime-mutable config, pluggable sinks. No env reads, isomorphic (Node + browser), tree-shake-friendly.",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./sinks/pino": {
14
+ "types": "./dist/sinks/pino.d.ts",
15
+ "import": "./dist/sinks/pino.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "dependencies": {
22
+ "pino": "^9.0.0",
23
+ "pino-pretty": "^11.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.13.1",
27
+ "typescript": "^5.7.3",
28
+ "vitest": "^2.1.8"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/emdzej/bimmerz.git",
36
+ "directory": "packages/logger"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc",
40
+ "dev": "tsc --watch",
41
+ "test": "vitest run --passWithNoTests",
42
+ "test:watch": "vitest",
43
+ "lint": "eslint .",
44
+ "typecheck": "tsc --noEmit",
45
+ "clean": "rm -rf dist .turbo *.tsbuildinfo"
46
+ }
47
+ }