@vielzeug/rune 1.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.
Files changed (60) hide show
  1. package/README.md +95 -0
  2. package/dist/_dev.cjs +2 -0
  3. package/dist/_dev.cjs.map +1 -0
  4. package/dist/_dev.d.ts +2 -0
  5. package/dist/_dev.d.ts.map +1 -0
  6. package/dist/_dev.js +9 -0
  7. package/dist/_dev.js.map +1 -0
  8. package/dist/_prototype.cjs +2 -0
  9. package/dist/_prototype.cjs.map +1 -0
  10. package/dist/_prototype.d.ts +2 -0
  11. package/dist/_prototype.d.ts.map +1 -0
  12. package/dist/_prototype.js +13 -0
  13. package/dist/_prototype.js.map +1 -0
  14. package/dist/console.cjs +2 -0
  15. package/dist/console.cjs.map +1 -0
  16. package/dist/console.d.ts +65 -0
  17. package/dist/console.d.ts.map +1 -0
  18. package/dist/console.js +132 -0
  19. package/dist/console.js.map +1 -0
  20. package/dist/errors.cjs +2 -0
  21. package/dist/errors.cjs.map +1 -0
  22. package/dist/errors.d.ts +15 -0
  23. package/dist/errors.d.ts.map +1 -0
  24. package/dist/errors.js +17 -0
  25. package/dist/errors.js.map +1 -0
  26. package/dist/index.cjs +1 -0
  27. package/dist/index.d.ts +10 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +7 -0
  30. package/dist/lazy.cjs +2 -0
  31. package/dist/lazy.cjs.map +1 -0
  32. package/dist/lazy.d.ts +28 -0
  33. package/dist/lazy.d.ts.map +1 -0
  34. package/dist/lazy.js +21 -0
  35. package/dist/lazy.js.map +1 -0
  36. package/dist/logger.cjs +2 -0
  37. package/dist/logger.cjs.map +1 -0
  38. package/dist/logger.d.ts +15 -0
  39. package/dist/logger.d.ts.map +1 -0
  40. package/dist/logger.js +158 -0
  41. package/dist/logger.js.map +1 -0
  42. package/dist/rune.cjs +3 -0
  43. package/dist/rune.cjs.map +1 -0
  44. package/dist/rune.iife.js +3 -0
  45. package/dist/rune.iife.js.map +1 -0
  46. package/dist/rune.js +3 -0
  47. package/dist/rune.js.map +1 -0
  48. package/dist/transports.cjs +3 -0
  49. package/dist/transports.cjs.map +1 -0
  50. package/dist/transports.d.ts +88 -0
  51. package/dist/transports.d.ts.map +1 -0
  52. package/dist/transports.js +111 -0
  53. package/dist/transports.js.map +1 -0
  54. package/dist/types.cjs +2 -0
  55. package/dist/types.cjs.map +1 -0
  56. package/dist/types.d.ts +274 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +16 -0
  59. package/dist/types.js.map +1 -0
  60. package/package.json +43 -0
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @vielzeug/rune
2
+
3
+ > Structured browser/Node logger with levels, namespaces, pluggable transports, lazy bindings, and timing helpers.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@vielzeug/rune)](https://www.npmjs.com/package/@vielzeug/rune) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ <details>
8
+ <summary>Quick Reference</summary>
9
+
10
+ **Package:** `@vielzeug/rune` &nbsp;·&nbsp; **Category:** Logging
11
+
12
+ **Key exports:** `createLogger`, `defaultLogger`, `lazy`, `consoleTransport`, `remoteTransport`, `jsonTransport`, `batchTransport`, `sampleTransport`, `redactTransport`, `pipe`, `isLevelEnabled`
13
+
14
+ **When to use:** Structured browser/Node logging with log levels, namespaced scopes, lazy bindings, and a pluggable transport pipeline.
15
+
16
+ **Related:** [@vielzeug/courier](https://vielzeug.dev/courier/) · [@vielzeug/herald](https://vielzeug.dev/herald/) · [@vielzeug/familiar](https://vielzeug.dev/familiar/)
17
+
18
+ </details>
19
+
20
+ `@vielzeug/rune` is part of Vielzeug and ships as a zero-dependency TypeScript package with ESM+CJS output.
21
+
22
+ ## Installation
23
+
24
+ ```sh
25
+ pnpm add @vielzeug/rune
26
+ npm install @vielzeug/rune
27
+ yarn add @vielzeug/rune
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ```ts
33
+ import { createLogger, defaultLogger, lazy } from '@vielzeug/rune';
34
+ import { consoleTransport, jsonTransport, remoteTransport, sampleTransport } from '@vielzeug/rune';
35
+
36
+ // Default singleton — uses consoleTransport() automatically
37
+ defaultLogger.info({ port: 3000 }, 'server started');
38
+ defaultLogger.warn('cache stale');
39
+ defaultLogger.error(new Error('connection lost')); // auto-serializes Error
40
+
41
+ // Namespaced child loggers
42
+ const api = createLogger('api');
43
+ api.info({ method: 'GET', path: '/users' }, 'request');
44
+
45
+ // Pinned bindings — lazy() evaluates only when the level passes
46
+ const reqLog = api.withBindings({
47
+ requestId: 'abc-123',
48
+ diagnostics: lazy(() => buildDiagnostics()),
49
+ });
50
+ reqLog.debug('processing'); // diagnostics() called only here
51
+
52
+ // Structured timing — label is the message; emits { duration_ms } in context
53
+ const users = await reqLog.time('db.query', () => db.query('SELECT * FROM users'));
54
+
55
+ // Custom transport pipeline
56
+ const log = createLogger({
57
+ logLevel: 'info',
58
+ namespace: 'server',
59
+ transports: [
60
+ consoleTransport({ timestamp: true }),
61
+ remoteTransport({
62
+ handler: async (type, data) => {
63
+ await fetch('/api/logs', { body: JSON.stringify(data), method: 'POST' });
64
+ },
65
+ level: 'error',
66
+ }),
67
+ ],
68
+ });
69
+
70
+ // Node.js: NDJSON for log aggregation (ELK, Datadog, etc.)
71
+ const nodeLog = createLogger({
72
+ transports: [jsonTransport({ level: 'warn' })],
73
+ });
74
+
75
+ // Fan-out to multiple transports independently (errors in one don't block others)
76
+ import { pipe } from '@vielzeug/rune';
77
+ const fanout = pipe(consoleTransport(), remoteTransport({ handler, level: 'error' }));
78
+
79
+ // Sampling — drop 90 % of debug entries before they reach the downstream transport
80
+ const sampledLog = createLogger({
81
+ logLevel: 'debug',
82
+ transports: [sampleTransport({ rate: 0.1, transport: consoleTransport() })],
83
+ });
84
+ ```
85
+
86
+ ## Documentation
87
+
88
+ - [Overview](https://vielzeug.dev/rune/)
89
+ - [Usage Guide](https://vielzeug.dev/rune/usage)
90
+ - [API Reference](https://vielzeug.dev/rune/api)
91
+ - [Examples](https://vielzeug.dev/rune/examples)
92
+
93
+ ## License
94
+
95
+ MIT © [Helmuth Saatkamp](https://github.com/helmuthdu) — part of the [Vielzeug](https://github.com/helmuthdu/vielzeug) monorepo.
package/dist/_dev.cjs ADDED
@@ -0,0 +1,2 @@
1
+ var e=!globalThis.__RUNE_PROD__;function t(t){e&&console.warn(`[@vielzeug/rune] ${t}`)}exports.warn=t;
2
+ //# sourceMappingURL=_dev.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_dev.cjs","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __RUNE_PROD__?: boolean }).__RUNE_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/rune] ${msg}`);\n}\n"],"mappings":"AAAA,IAAM,EAAQ,CAAE,WAA2C,cAG3D,SAAgB,EAAK,EAAmB,CAClC,GAAO,QAAQ,KAAK,oBAAoB,GAAK,CACnD"}
package/dist/_dev.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=_dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_dev.d.ts","sourceRoot":"","sources":["../src/_dev.ts"],"names":[],"mappings":""}
package/dist/_dev.js ADDED
@@ -0,0 +1,9 @@
1
+ //#region src/_dev.ts
2
+ var e = !globalThis.__RUNE_PROD__;
3
+ function t(t) {
4
+ e && console.warn(`[@vielzeug/rune] ${t}`);
5
+ }
6
+ //#endregion
7
+ export { t as warn };
8
+
9
+ //# sourceMappingURL=_dev.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_dev.js","names":[],"sources":["../src/_dev.ts"],"sourcesContent":["const isDev = !(globalThis as { __RUNE_PROD__?: boolean }).__RUNE_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/rune] ${msg}`);\n}\n"],"mappings":";AAAA,IAAM,IAAQ,CAAE,WAA2C;AAG3D,SAAgB,EAAK,GAAmB;CACtC,AAAI,KAAO,QAAQ,KAAK,oBAAoB,GAAK;AACnD"}
@@ -0,0 +1,2 @@
1
+ var e=new Set([`__proto__`,`constructor`,`prototype`]);function t(t){return e.has(t)}exports.isUnsafeObjectKey=t;
2
+ //# sourceMappingURL=_prototype.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_prototype.cjs","names":[],"sources":["../src/_prototype.ts"],"sourcesContent":["/**\n * Property names that must never be used as a bracket-assignment key on a plain object literal —\n * `obj[key] = value` for `key === '__proto__'` invokes `Object.prototype`'s `__proto__` accessor\n * and reassigns `obj`'s own prototype instead of setting an own property. `constructor` and\n * `prototype` are excluded defensively for the same class of risk.\n *\n * Used when rebuilding a caller-supplied `Bindings` object key-by-key (`logger.ts`'s\n * `serializeErrors()`, `transports.ts`'s `redactObject()`) — bindings/context ultimately come\n * from application code, but nothing prevents an app from passing through attacker-controlled\n * data (e.g. `log.info(JSON.parse(untrustedInput))`).\n *\n * @internal\n */\nconst UNSAFE_OBJECT_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/** @internal */\nexport function isUnsafeObjectKey(key: string): boolean {\n return UNSAFE_OBJECT_KEYS.has(key);\n}\n"],"mappings":"AAaA,IAAM,EAAqB,IAAI,IAAI,CAAC,YAAa,cAAe,WAAW,CAAC,EAG5E,SAAgB,EAAkB,EAAsB,CACtD,OAAO,EAAmB,IAAI,CAAG,CACnC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=_prototype.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_prototype.d.ts","sourceRoot":"","sources":["../src/_prototype.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ //#region src/_prototype.ts
2
+ var e = /* @__PURE__ */ new Set([
3
+ "__proto__",
4
+ "constructor",
5
+ "prototype"
6
+ ]);
7
+ function t(t) {
8
+ return e.has(t);
9
+ }
10
+ //#endregion
11
+ export { t as isUnsafeObjectKey };
12
+
13
+ //# sourceMappingURL=_prototype.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_prototype.js","names":[],"sources":["../src/_prototype.ts"],"sourcesContent":["/**\n * Property names that must never be used as a bracket-assignment key on a plain object literal —\n * `obj[key] = value` for `key === '__proto__'` invokes `Object.prototype`'s `__proto__` accessor\n * and reassigns `obj`'s own prototype instead of setting an own property. `constructor` and\n * `prototype` are excluded defensively for the same class of risk.\n *\n * Used when rebuilding a caller-supplied `Bindings` object key-by-key (`logger.ts`'s\n * `serializeErrors()`, `transports.ts`'s `redactObject()`) — bindings/context ultimately come\n * from application code, but nothing prevents an app from passing through attacker-controlled\n * data (e.g. `log.info(JSON.parse(untrustedInput))`).\n *\n * @internal\n */\nconst UNSAFE_OBJECT_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\n/** @internal */\nexport function isUnsafeObjectKey(key: string): boolean {\n return UNSAFE_OBJECT_KEYS.has(key);\n}\n"],"mappings":";AAaA,IAAM,oBAAqB,IAAI,IAAI;CAAC;CAAa;CAAe;AAAW,CAAC;AAG5E,SAAgB,EAAkB,GAAsB;CACtD,OAAO,EAAmB,IAAI,CAAG;AACnC"}
@@ -0,0 +1,2 @@
1
+ const e=require("./types.cjs");function t(e,t,n){let r=Object.keys(e).length>0,i=n&&r?n(e):r?e:void 0;return i!==void 0&&t!==void 0?[t,i]:i===void 0?t===void 0?[]:[t]:[i]}var n={debug:{badge:`🅳`,bg:`#616161`,border:`#424242`,color:`#e0e0e0`},error:{badge:`🅴`,bg:`#d32f2f`,border:`#c62828`,color:`#fff`},fatal:{badge:`🅵`,bg:`#4a148c`,border:`#38006b`,color:`#fff`},group:{badge:`🅶`,bg:`#546e7a`,border:`#455a64`,color:`#fff`},info:{badge:`🅸`,bg:`#1976d2`,border:`#1565c0`,color:`#fff`},ns:{badge:``,bg:`#424242`,border:`#212121`,color:`#fff`},warn:{badge:`🆆`,bg:`#ffb300`,border:`#ffa000`,color:`#212121`}},r=`border-radius: 8px; font: italic small-caps bold 12px; font-weight: lighter; padding: 0 4px;`,i={debug:`log`,error:`error`,fatal:`error`,info:`info`,warn:`warn`};function a(e){if(!e)return n;let t={...n};for(let r of Object.keys(e)){let i=e[r];i&&(t[r]={...n[r],...i})}return t}function o(e){return[parseInt(e.slice(1,3),16),parseInt(e.slice(3,5),16),parseInt(e.slice(5,7),16)]}function s(e,t){let[n,r,i]=o(e);return`\x1b[38;2;${n};${r};${i}m${t}\x1b[0m`}function c(e){return`\x1b[90m${e}\x1b[0m`}function l(){return typeof window<`u`?!1:globalThis.process?.stdout?.isTTY===!0}function u(e,t=``){return`background: ${e.bg}; color: ${e.color}; border: 1px solid ${e.border}; border-radius: 4px; padding: 0 1px${t}`}function d(e){return e.replace(/%/g,`%%`)}function f(e,t,n,r,i){let a=e[t],o=d(a.badge),l=[i?s(a.bg,o):o];if(n){let e=d(n);l.push(i?c(`[${e}]`):`[${e}]`)}return r&&l.push(i?c(r):r),`${l.join(` | `)} |`}function p(e,t,n,i){let a=`%c${d(e[t].badge)}%c`,o=[u(e[t]),``];return n&&(a+=` %c${d(n)}%c`,o.push(u(e.ns,`; ${r}`),``)),i&&(a+=` %c${i}%c`,o.push(`color: gray`,``)),{fmt:a,parts:o}}function m(n={}){let r=n.level??`debug`,o=n.timestamp??!0,s=n.format??`raw`,c=typeof window>`u`,u=n.ansi??(c?l():!1),d=a(n.theme),m=s===`json`?e=>JSON.stringify(e):n.inspectFn;return n=>{if(!e.isLevelEnabled(r,n.level))return;let a=o?n.timestamp.toISOString().slice(11,23):``,s=t(n.data,n.message,m),l=console[i[n.level]];if(c)l(f(d,n.level,n.namespace,a,u),...s);else{let{fmt:e,parts:t}=p(d,n.level,n.namespace,a);l(e,...t,...s)}}}function h(e,t,n,i){let a=e?console.groupCollapsed:console.group;if(typeof window>`u`){let e=[i.group.badge,t];n&&e.push(`[${n}]`),a(e.join(` | `));return}let o=`${i.group.badge} %c${d(t)}%c`,s=[u(i.group,`; margin-right: 6px; padding: 1px 3px 0`),``];n&&(o+=` %c${d(n)}%c`,s.push(u(i.ns,`; ${r}; margin-right: 6px`),``)),a(o,...s)}exports.DEFAULT_THEME=n,exports.consoleTransport=m,exports.renderGroup=h,exports.resolveTheme=a;
2
+ //# sourceMappingURL=console.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.cjs","names":[],"sources":["../src/console.ts"],"sourcesContent":["import type { Bindings, LogEntry, LogLevel, LogType, Transport } from './types';\n\nimport { isLevelEnabled } from './types';\n\n/* ─── Console theme types ─── */\n\n/**\n * Per-level style definition for the console transport.\n * All fields are optional when providing a level override — unspecified fields fall back to the default theme.\n */\nexport type ConsoleThemeEntry = {\n badge: string;\n bg: string;\n border: string;\n color: string;\n};\n\n/**\n * Partial theme overrides merged on top of the default theme.\n * Each level entry is also partial — only specify the fields you want to change.\n *\n * @example\n * consoleTransport({ theme: { error: { badge: '✖' } } })\n */\nexport type ConsoleTheme = Partial<Record<LogType | 'group' | 'ns', Partial<ConsoleThemeEntry>>>;\n\n/**\n * Fully-resolved console theme where every level and every field is populated.\n */\nexport type ResolvedTheme = Record<LogType | 'group' | 'ns', ConsoleThemeEntry>;\n\n/* ─── ConsoleTransportOptions ─── */\n\nexport type ConsoleTransportOptions = {\n /**\n * Enable ANSI 24-bit color output in Node.js.\n * Default: true when process.stdout.isTTY is true, false otherwise.\n */\n ansi?: boolean;\n /**\n * Object serialization format for Node.js output.\n * - 'json' — JSON.stringify (machine-readable, fails on circular refs)\n * - 'raw' — pass the object directly to the console method (default)\n * Default: 'raw'.\n */\n format?: 'json' | 'raw';\n /**\n * Custom object inspector function. When provided, the `data` object is\n * passed through this function before being written to the console.\n */\n inspectFn?: (value: unknown) => string;\n /** Minimum level to output. Default: 'debug'. */\n level?: LogLevel;\n /** Partial theme overrides merged on top of the default theme. */\n theme?: ConsoleTheme;\n /** Whether to include timestamp in console output. Default: true. */\n timestamp?: boolean;\n};\n\n/* ─── Payload builder ─── */\n\nfunction buildPayload(\n data: Readonly<Bindings>,\n message: string | undefined,\n inspectFn: ((v: unknown) => string) | undefined,\n): unknown[] {\n const hasData = Object.keys(data).length > 0;\n const formatted: unknown = inspectFn && hasData ? inspectFn(data) : hasData ? data : undefined;\n\n if (formatted !== undefined && message !== undefined) return [message, formatted];\n\n if (formatted !== undefined) return [formatted];\n\n if (message !== undefined) return [message];\n\n return [];\n}\n\n/* ─── Console theme ─── */\n\nexport const DEFAULT_THEME: ResolvedTheme = {\n debug: { badge: '🅳', bg: '#616161', border: '#424242', color: '#e0e0e0' },\n error: { badge: '🅴', bg: '#d32f2f', border: '#c62828', color: '#fff' },\n fatal: { badge: '🅵', bg: '#4a148c', border: '#38006b', color: '#fff' },\n group: { badge: '🅶', bg: '#546e7a', border: '#455a64', color: '#fff' },\n info: { badge: '🅸', bg: '#1976d2', border: '#1565c0', color: '#fff' },\n ns: { badge: '', bg: '#424242', border: '#212121', color: '#fff' },\n warn: { badge: '🆆', bg: '#ffb300', border: '#ffa000', color: '#212121' },\n};\n\nconst NS_STYLE = 'border-radius: 8px; font: italic small-caps bold 12px; font-weight: lighter; padding: 0 4px;';\n\nconst LOG_METHOD: Record<LogType, 'error' | 'info' | 'log' | 'warn'> = {\n debug: 'log',\n error: 'error',\n fatal: 'error',\n info: 'info',\n warn: 'warn',\n};\n\n/** Deep-merges per-level overrides onto the default theme. Only specified fields within each level entry are replaced. */\nexport function resolveTheme(override: ConsoleTheme | undefined): ResolvedTheme {\n if (!override) return DEFAULT_THEME;\n\n const result: ResolvedTheme = { ...DEFAULT_THEME };\n\n for (const key of Object.keys(override) as Array<LogType | 'group' | 'ns'>) {\n const entry = override[key];\n\n if (entry) result[key] = { ...DEFAULT_THEME[key], ...entry };\n }\n\n return result;\n}\n\n/* ─── ANSI helpers (Node) ─── */\n\nfunction hexToRgb(hex: string): [number, number, number] {\n return [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];\n}\n\nfunction ansiColor(hex: string, text: string): string {\n const [r, g, b] = hexToRgb(hex);\n\n return `\\x1b[38;2;${r};${g};${b}m${text}\\x1b[0m`;\n}\n\nfunction ansiMuted(text: string): string {\n return `\\x1b[90m${text}\\x1b[0m`;\n}\n\nfunction supportsAnsi(): boolean {\n if (typeof window !== 'undefined') return false;\n\n return (\n (globalThis as Record<string, unknown> & { process?: { stdout?: { isTTY?: boolean } } }).process?.stdout?.isTTY ===\n true\n );\n}\n\nfunction badgeStyle(entry: ConsoleThemeEntry, extra = ''): string {\n return `background: ${entry.bg}; color: ${entry.color}; border: 1px solid ${entry.border}; border-radius: 4px; padding: 0 1px${extra}`;\n}\n\nfunction escapeConsoleFormat(s: string): string {\n return s.replace(/%/g, '%%');\n}\n\n/**\n * Builds the Node console prefix string, passed as the first argument to `console.log()`/etc.\n * alongside the payload. `namespace` is escaped because Node's `console.*` methods run the first\n * string argument through `util.format` — an unescaped `%s`/`%d`/`%o`/etc. in a caller-controlled\n * namespace would consume and hide the actual payload arguments that follow (log forging).\n */\nfunction buildNodePrefix(\n theme: ResolvedTheme,\n type: LogType,\n namespace: string,\n timestamp: string,\n useAnsi: boolean,\n): string {\n const t = theme[type];\n const safeBadge = escapeConsoleFormat(t.badge);\n const badgeText = useAnsi ? ansiColor(t.bg, safeBadge) : safeBadge;\n const meta = [badgeText];\n\n if (namespace) {\n const safeNamespace = escapeConsoleFormat(namespace);\n\n meta.push(useAnsi ? ansiMuted(`[${safeNamespace}]`) : `[${safeNamespace}]`);\n }\n\n if (timestamp) meta.push(useAnsi ? ansiMuted(timestamp) : timestamp);\n\n return `${meta.join(' | ')} |`;\n}\n\nfunction buildBrowserPrefix(\n theme: ResolvedTheme,\n type: LogType,\n namespace: string,\n timestamp: string,\n): { fmt: string; parts: string[] } {\n let fmt = `%c${escapeConsoleFormat(theme[type].badge)}%c`;\n const parts: string[] = [badgeStyle(theme[type]), ''];\n\n if (namespace) {\n fmt += ` %c${escapeConsoleFormat(namespace)}%c`;\n parts.push(badgeStyle(theme.ns, `; ${NS_STYLE}`), '');\n }\n\n if (timestamp) {\n fmt += ` %c${timestamp}%c`;\n parts.push('color: gray', '');\n }\n\n return { fmt, parts };\n}\n\n/* ─── consoleTransport ─── */\n\n/**\n * Formats and writes log entries to the browser or Node.js console.\n * Uses CSS-styled badges in browsers and plain text in Node.\n * Accepts an optional partial `theme` to override colors and badges per level.\n *\n * @example\n * consoleTransport()\n * consoleTransport({ level: 'warn', timestamp: false })\n * consoleTransport({ theme: { error: { badge: '✖' } } })\n * consoleTransport({ inspectFn: (v) => require('util').inspect(v, { colors: true, depth: 4 }) })\n */\nexport function consoleTransport(options: ConsoleTransportOptions = {}): Transport {\n const level = options.level ?? 'debug';\n const showTimestamp = options.timestamp ?? true;\n const format = options.format ?? 'raw';\n const isNode = typeof window === 'undefined';\n const useAnsi = options.ansi ?? (isNode ? supportsAnsi() : false);\n const resolved = resolveTheme(options.theme);\n const inspectFn: ((v: unknown) => string) | undefined =\n format === 'json' ? (v) => JSON.stringify(v) : options.inspectFn;\n\n return (entry: LogEntry): void => {\n if (!isLevelEnabled(level, entry.level)) return;\n\n const timestamp = showTimestamp ? entry.timestamp.toISOString().slice(11, 23) : '';\n const payload = buildPayload(entry.data, entry.message, inspectFn);\n const method = console[LOG_METHOD[entry.level]] as (...args: unknown[]) => void;\n\n if (isNode) {\n method(buildNodePrefix(resolved, entry.level, entry.namespace, timestamp, useAnsi), ...payload);\n } else {\n const { fmt, parts } = buildBrowserPrefix(resolved, entry.level, entry.namespace, timestamp);\n\n method(fmt, ...parts, ...payload);\n }\n };\n}\n\n/* ─── Group rendering (used by logger.ts wrapGroup) ─── */\n\nexport function renderGroup(collapsed: boolean, label: string, namespace: string, theme: ResolvedTheme): void {\n const fn = collapsed ? console.groupCollapsed : console.group;\n const isNode = typeof window === 'undefined';\n\n if (isNode) {\n const meta = [theme.group.badge, label];\n\n if (namespace) meta.push(`[${namespace}]`);\n\n fn(meta.join(' | '));\n\n return;\n }\n\n let fmt = `${theme.group.badge} %c${escapeConsoleFormat(label)}%c`;\n const parts: string[] = [badgeStyle(theme.group, '; margin-right: 6px; padding: 1px 3px 0'), ''];\n\n if (namespace) {\n fmt += ` %c${escapeConsoleFormat(namespace)}%c`;\n parts.push(badgeStyle(theme.ns, `; ${NS_STYLE}; margin-right: 6px`), '');\n }\n\n fn(fmt, ...parts);\n}\n"],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { LogLevel, LogType, Transport } from './types';
2
+ /**
3
+ * Per-level style definition for the console transport.
4
+ * All fields are optional when providing a level override — unspecified fields fall back to the default theme.
5
+ */
6
+ export type ConsoleThemeEntry = {
7
+ badge: string;
8
+ bg: string;
9
+ border: string;
10
+ color: string;
11
+ };
12
+ /**
13
+ * Partial theme overrides merged on top of the default theme.
14
+ * Each level entry is also partial — only specify the fields you want to change.
15
+ *
16
+ * @example
17
+ * consoleTransport({ theme: { error: { badge: '✖' } } })
18
+ */
19
+ export type ConsoleTheme = Partial<Record<LogType | 'group' | 'ns', Partial<ConsoleThemeEntry>>>;
20
+ /**
21
+ * Fully-resolved console theme where every level and every field is populated.
22
+ */
23
+ export type ResolvedTheme = Record<LogType | 'group' | 'ns', ConsoleThemeEntry>;
24
+ export type ConsoleTransportOptions = {
25
+ /**
26
+ * Enable ANSI 24-bit color output in Node.js.
27
+ * Default: true when process.stdout.isTTY is true, false otherwise.
28
+ */
29
+ ansi?: boolean;
30
+ /**
31
+ * Object serialization format for Node.js output.
32
+ * - 'json' — JSON.stringify (machine-readable, fails on circular refs)
33
+ * - 'raw' — pass the object directly to the console method (default)
34
+ * Default: 'raw'.
35
+ */
36
+ format?: 'json' | 'raw';
37
+ /**
38
+ * Custom object inspector function. When provided, the `data` object is
39
+ * passed through this function before being written to the console.
40
+ */
41
+ inspectFn?: (value: unknown) => string;
42
+ /** Minimum level to output. Default: 'debug'. */
43
+ level?: LogLevel;
44
+ /** Partial theme overrides merged on top of the default theme. */
45
+ theme?: ConsoleTheme;
46
+ /** Whether to include timestamp in console output. Default: true. */
47
+ timestamp?: boolean;
48
+ };
49
+ export declare const DEFAULT_THEME: ResolvedTheme;
50
+ /** Deep-merges per-level overrides onto the default theme. Only specified fields within each level entry are replaced. */
51
+ export declare function resolveTheme(override: ConsoleTheme | undefined): ResolvedTheme;
52
+ /**
53
+ * Formats and writes log entries to the browser or Node.js console.
54
+ * Uses CSS-styled badges in browsers and plain text in Node.
55
+ * Accepts an optional partial `theme` to override colors and badges per level.
56
+ *
57
+ * @example
58
+ * consoleTransport()
59
+ * consoleTransport({ level: 'warn', timestamp: false })
60
+ * consoleTransport({ theme: { error: { badge: '✖' } } })
61
+ * consoleTransport({ inspectFn: (v) => require('util').inspect(v, { colors: true, depth: 4 }) })
62
+ */
63
+ export declare function consoleTransport(options?: ConsoleTransportOptions): Transport;
64
+ export declare function renderGroup(collapsed: boolean, label: string, namespace: string, theme: ResolvedTheme): void;
65
+ //# sourceMappingURL=console.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAMhF;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,GAAG,OAAO,GAAG,IAAI,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;AAEjG;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,GAAG,OAAO,GAAG,IAAI,EAAE,iBAAiB,CAAC,CAAC;AAIhF,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACvC,iDAAiD;IACjD,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAuBF,eAAO,MAAM,aAAa,EAAE,aAQ3B,CAAC;AAYF,0HAA0H;AAC1H,wBAAgB,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,GAAG,aAAa,CAY9E;AAwFD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,SAAS,CAyBjF;AAID,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAuB5G"}
@@ -0,0 +1,132 @@
1
+ import { isLevelEnabled as e } from "./types.js";
2
+ //#region src/console.ts
3
+ function t(e, t, n) {
4
+ let r = Object.keys(e).length > 0, i = n && r ? n(e) : r ? e : void 0;
5
+ return i !== void 0 && t !== void 0 ? [t, i] : i === void 0 ? t === void 0 ? [] : [t] : [i];
6
+ }
7
+ var n = {
8
+ debug: {
9
+ badge: "🅳",
10
+ bg: "#616161",
11
+ border: "#424242",
12
+ color: "#e0e0e0"
13
+ },
14
+ error: {
15
+ badge: "🅴",
16
+ bg: "#d32f2f",
17
+ border: "#c62828",
18
+ color: "#fff"
19
+ },
20
+ fatal: {
21
+ badge: "🅵",
22
+ bg: "#4a148c",
23
+ border: "#38006b",
24
+ color: "#fff"
25
+ },
26
+ group: {
27
+ badge: "🅶",
28
+ bg: "#546e7a",
29
+ border: "#455a64",
30
+ color: "#fff"
31
+ },
32
+ info: {
33
+ badge: "🅸",
34
+ bg: "#1976d2",
35
+ border: "#1565c0",
36
+ color: "#fff"
37
+ },
38
+ ns: {
39
+ badge: "",
40
+ bg: "#424242",
41
+ border: "#212121",
42
+ color: "#fff"
43
+ },
44
+ warn: {
45
+ badge: "🆆",
46
+ bg: "#ffb300",
47
+ border: "#ffa000",
48
+ color: "#212121"
49
+ }
50
+ }, r = "border-radius: 8px; font: italic small-caps bold 12px; font-weight: lighter; padding: 0 4px;", i = {
51
+ debug: "log",
52
+ error: "error",
53
+ fatal: "error",
54
+ info: "info",
55
+ warn: "warn"
56
+ };
57
+ function a(e) {
58
+ if (!e) return n;
59
+ let t = { ...n };
60
+ for (let r of Object.keys(e)) {
61
+ let i = e[r];
62
+ i && (t[r] = {
63
+ ...n[r],
64
+ ...i
65
+ });
66
+ }
67
+ return t;
68
+ }
69
+ function o(e) {
70
+ return [
71
+ parseInt(e.slice(1, 3), 16),
72
+ parseInt(e.slice(3, 5), 16),
73
+ parseInt(e.slice(5, 7), 16)
74
+ ];
75
+ }
76
+ function s(e, t) {
77
+ let [n, r, i] = o(e);
78
+ return `\x1b[38;2;${n};${r};${i}m${t}\x1b[0m`;
79
+ }
80
+ function c(e) {
81
+ return `\x1b[90m${e}\x1b[0m`;
82
+ }
83
+ function l() {
84
+ return typeof window < "u" ? !1 : globalThis.process?.stdout?.isTTY === !0;
85
+ }
86
+ function u(e, t = "") {
87
+ return `background: ${e.bg}; color: ${e.color}; border: 1px solid ${e.border}; border-radius: 4px; padding: 0 1px${t}`;
88
+ }
89
+ function d(e) {
90
+ return e.replace(/%/g, "%%");
91
+ }
92
+ function f(e, t, n, r, i) {
93
+ let a = e[t], o = d(a.badge), l = [i ? s(a.bg, o) : o];
94
+ if (n) {
95
+ let e = d(n);
96
+ l.push(i ? c(`[${e}]`) : `[${e}]`);
97
+ }
98
+ return r && l.push(i ? c(r) : r), `${l.join(" | ")} |`;
99
+ }
100
+ function p(e, t, n, i) {
101
+ let a = `%c${d(e[t].badge)}%c`, o = [u(e[t]), ""];
102
+ return n && (a += ` %c${d(n)}%c`, o.push(u(e.ns, `; ${r}`), "")), i && (a += ` %c${i}%c`, o.push("color: gray", "")), {
103
+ fmt: a,
104
+ parts: o
105
+ };
106
+ }
107
+ function m(n = {}) {
108
+ let r = n.level ?? "debug", o = n.timestamp ?? !0, s = n.format ?? "raw", c = typeof window > "u", u = n.ansi ?? (c ? l() : !1), d = a(n.theme), m = s === "json" ? (e) => JSON.stringify(e) : n.inspectFn;
109
+ return (n) => {
110
+ if (!e(r, n.level)) return;
111
+ let a = o ? n.timestamp.toISOString().slice(11, 23) : "", s = t(n.data, n.message, m), l = console[i[n.level]];
112
+ if (c) l(f(d, n.level, n.namespace, a, u), ...s);
113
+ else {
114
+ let { fmt: e, parts: t } = p(d, n.level, n.namespace, a);
115
+ l(e, ...t, ...s);
116
+ }
117
+ };
118
+ }
119
+ function h(e, t, n, i) {
120
+ let a = e ? console.groupCollapsed : console.group;
121
+ if (typeof window > "u") {
122
+ let e = [i.group.badge, t];
123
+ n && e.push(`[${n}]`), a(e.join(" | "));
124
+ return;
125
+ }
126
+ let o = `${i.group.badge} %c${d(t)}%c`, s = [u(i.group, "; margin-right: 6px; padding: 1px 3px 0"), ""];
127
+ n && (o += ` %c${d(n)}%c`, s.push(u(i.ns, `; ${r}; margin-right: 6px`), "")), a(o, ...s);
128
+ }
129
+ //#endregion
130
+ export { n as DEFAULT_THEME, m as consoleTransport, h as renderGroup, a as resolveTheme };
131
+
132
+ //# sourceMappingURL=console.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.js","names":[],"sources":["../src/console.ts"],"sourcesContent":["import type { Bindings, LogEntry, LogLevel, LogType, Transport } from './types';\n\nimport { isLevelEnabled } from './types';\n\n/* ─── Console theme types ─── */\n\n/**\n * Per-level style definition for the console transport.\n * All fields are optional when providing a level override — unspecified fields fall back to the default theme.\n */\nexport type ConsoleThemeEntry = {\n badge: string;\n bg: string;\n border: string;\n color: string;\n};\n\n/**\n * Partial theme overrides merged on top of the default theme.\n * Each level entry is also partial — only specify the fields you want to change.\n *\n * @example\n * consoleTransport({ theme: { error: { badge: '✖' } } })\n */\nexport type ConsoleTheme = Partial<Record<LogType | 'group' | 'ns', Partial<ConsoleThemeEntry>>>;\n\n/**\n * Fully-resolved console theme where every level and every field is populated.\n */\nexport type ResolvedTheme = Record<LogType | 'group' | 'ns', ConsoleThemeEntry>;\n\n/* ─── ConsoleTransportOptions ─── */\n\nexport type ConsoleTransportOptions = {\n /**\n * Enable ANSI 24-bit color output in Node.js.\n * Default: true when process.stdout.isTTY is true, false otherwise.\n */\n ansi?: boolean;\n /**\n * Object serialization format for Node.js output.\n * - 'json' — JSON.stringify (machine-readable, fails on circular refs)\n * - 'raw' — pass the object directly to the console method (default)\n * Default: 'raw'.\n */\n format?: 'json' | 'raw';\n /**\n * Custom object inspector function. When provided, the `data` object is\n * passed through this function before being written to the console.\n */\n inspectFn?: (value: unknown) => string;\n /** Minimum level to output. Default: 'debug'. */\n level?: LogLevel;\n /** Partial theme overrides merged on top of the default theme. */\n theme?: ConsoleTheme;\n /** Whether to include timestamp in console output. Default: true. */\n timestamp?: boolean;\n};\n\n/* ─── Payload builder ─── */\n\nfunction buildPayload(\n data: Readonly<Bindings>,\n message: string | undefined,\n inspectFn: ((v: unknown) => string) | undefined,\n): unknown[] {\n const hasData = Object.keys(data).length > 0;\n const formatted: unknown = inspectFn && hasData ? inspectFn(data) : hasData ? data : undefined;\n\n if (formatted !== undefined && message !== undefined) return [message, formatted];\n\n if (formatted !== undefined) return [formatted];\n\n if (message !== undefined) return [message];\n\n return [];\n}\n\n/* ─── Console theme ─── */\n\nexport const DEFAULT_THEME: ResolvedTheme = {\n debug: { badge: '🅳', bg: '#616161', border: '#424242', color: '#e0e0e0' },\n error: { badge: '🅴', bg: '#d32f2f', border: '#c62828', color: '#fff' },\n fatal: { badge: '🅵', bg: '#4a148c', border: '#38006b', color: '#fff' },\n group: { badge: '🅶', bg: '#546e7a', border: '#455a64', color: '#fff' },\n info: { badge: '🅸', bg: '#1976d2', border: '#1565c0', color: '#fff' },\n ns: { badge: '', bg: '#424242', border: '#212121', color: '#fff' },\n warn: { badge: '🆆', bg: '#ffb300', border: '#ffa000', color: '#212121' },\n};\n\nconst NS_STYLE = 'border-radius: 8px; font: italic small-caps bold 12px; font-weight: lighter; padding: 0 4px;';\n\nconst LOG_METHOD: Record<LogType, 'error' | 'info' | 'log' | 'warn'> = {\n debug: 'log',\n error: 'error',\n fatal: 'error',\n info: 'info',\n warn: 'warn',\n};\n\n/** Deep-merges per-level overrides onto the default theme. Only specified fields within each level entry are replaced. */\nexport function resolveTheme(override: ConsoleTheme | undefined): ResolvedTheme {\n if (!override) return DEFAULT_THEME;\n\n const result: ResolvedTheme = { ...DEFAULT_THEME };\n\n for (const key of Object.keys(override) as Array<LogType | 'group' | 'ns'>) {\n const entry = override[key];\n\n if (entry) result[key] = { ...DEFAULT_THEME[key], ...entry };\n }\n\n return result;\n}\n\n/* ─── ANSI helpers (Node) ─── */\n\nfunction hexToRgb(hex: string): [number, number, number] {\n return [parseInt(hex.slice(1, 3), 16), parseInt(hex.slice(3, 5), 16), parseInt(hex.slice(5, 7), 16)];\n}\n\nfunction ansiColor(hex: string, text: string): string {\n const [r, g, b] = hexToRgb(hex);\n\n return `\\x1b[38;2;${r};${g};${b}m${text}\\x1b[0m`;\n}\n\nfunction ansiMuted(text: string): string {\n return `\\x1b[90m${text}\\x1b[0m`;\n}\n\nfunction supportsAnsi(): boolean {\n if (typeof window !== 'undefined') return false;\n\n return (\n (globalThis as Record<string, unknown> & { process?: { stdout?: { isTTY?: boolean } } }).process?.stdout?.isTTY ===\n true\n );\n}\n\nfunction badgeStyle(entry: ConsoleThemeEntry, extra = ''): string {\n return `background: ${entry.bg}; color: ${entry.color}; border: 1px solid ${entry.border}; border-radius: 4px; padding: 0 1px${extra}`;\n}\n\nfunction escapeConsoleFormat(s: string): string {\n return s.replace(/%/g, '%%');\n}\n\n/**\n * Builds the Node console prefix string, passed as the first argument to `console.log()`/etc.\n * alongside the payload. `namespace` is escaped because Node's `console.*` methods run the first\n * string argument through `util.format` — an unescaped `%s`/`%d`/`%o`/etc. in a caller-controlled\n * namespace would consume and hide the actual payload arguments that follow (log forging).\n */\nfunction buildNodePrefix(\n theme: ResolvedTheme,\n type: LogType,\n namespace: string,\n timestamp: string,\n useAnsi: boolean,\n): string {\n const t = theme[type];\n const safeBadge = escapeConsoleFormat(t.badge);\n const badgeText = useAnsi ? ansiColor(t.bg, safeBadge) : safeBadge;\n const meta = [badgeText];\n\n if (namespace) {\n const safeNamespace = escapeConsoleFormat(namespace);\n\n meta.push(useAnsi ? ansiMuted(`[${safeNamespace}]`) : `[${safeNamespace}]`);\n }\n\n if (timestamp) meta.push(useAnsi ? ansiMuted(timestamp) : timestamp);\n\n return `${meta.join(' | ')} |`;\n}\n\nfunction buildBrowserPrefix(\n theme: ResolvedTheme,\n type: LogType,\n namespace: string,\n timestamp: string,\n): { fmt: string; parts: string[] } {\n let fmt = `%c${escapeConsoleFormat(theme[type].badge)}%c`;\n const parts: string[] = [badgeStyle(theme[type]), ''];\n\n if (namespace) {\n fmt += ` %c${escapeConsoleFormat(namespace)}%c`;\n parts.push(badgeStyle(theme.ns, `; ${NS_STYLE}`), '');\n }\n\n if (timestamp) {\n fmt += ` %c${timestamp}%c`;\n parts.push('color: gray', '');\n }\n\n return { fmt, parts };\n}\n\n/* ─── consoleTransport ─── */\n\n/**\n * Formats and writes log entries to the browser or Node.js console.\n * Uses CSS-styled badges in browsers and plain text in Node.\n * Accepts an optional partial `theme` to override colors and badges per level.\n *\n * @example\n * consoleTransport()\n * consoleTransport({ level: 'warn', timestamp: false })\n * consoleTransport({ theme: { error: { badge: '✖' } } })\n * consoleTransport({ inspectFn: (v) => require('util').inspect(v, { colors: true, depth: 4 }) })\n */\nexport function consoleTransport(options: ConsoleTransportOptions = {}): Transport {\n const level = options.level ?? 'debug';\n const showTimestamp = options.timestamp ?? true;\n const format = options.format ?? 'raw';\n const isNode = typeof window === 'undefined';\n const useAnsi = options.ansi ?? (isNode ? supportsAnsi() : false);\n const resolved = resolveTheme(options.theme);\n const inspectFn: ((v: unknown) => string) | undefined =\n format === 'json' ? (v) => JSON.stringify(v) : options.inspectFn;\n\n return (entry: LogEntry): void => {\n if (!isLevelEnabled(level, entry.level)) return;\n\n const timestamp = showTimestamp ? entry.timestamp.toISOString().slice(11, 23) : '';\n const payload = buildPayload(entry.data, entry.message, inspectFn);\n const method = console[LOG_METHOD[entry.level]] as (...args: unknown[]) => void;\n\n if (isNode) {\n method(buildNodePrefix(resolved, entry.level, entry.namespace, timestamp, useAnsi), ...payload);\n } else {\n const { fmt, parts } = buildBrowserPrefix(resolved, entry.level, entry.namespace, timestamp);\n\n method(fmt, ...parts, ...payload);\n }\n };\n}\n\n/* ─── Group rendering (used by logger.ts wrapGroup) ─── */\n\nexport function renderGroup(collapsed: boolean, label: string, namespace: string, theme: ResolvedTheme): void {\n const fn = collapsed ? console.groupCollapsed : console.group;\n const isNode = typeof window === 'undefined';\n\n if (isNode) {\n const meta = [theme.group.badge, label];\n\n if (namespace) meta.push(`[${namespace}]`);\n\n fn(meta.join(' | '));\n\n return;\n }\n\n let fmt = `${theme.group.badge} %c${escapeConsoleFormat(label)}%c`;\n const parts: string[] = [badgeStyle(theme.group, '; margin-right: 6px; padding: 1px 3px 0'), ''];\n\n if (namespace) {\n fmt += ` %c${escapeConsoleFormat(namespace)}%c`;\n parts.push(badgeStyle(theme.ns, `; ${NS_STYLE}; margin-right: 6px`), '');\n }\n\n fn(fmt, ...parts);\n}\n"],"mappings":";;AA6DA,SAAS,EACP,GACA,GACA,GACW;CACX,IAAM,IAAU,OAAO,KAAK,CAAI,CAAC,CAAC,SAAS,GACrC,IAAqB,KAAa,IAAU,EAAU,CAAI,IAAI,IAAU,IAAO,KAAA;CAQrF,OANI,MAAc,KAAA,KAAa,MAAY,KAAA,IAAkB,CAAC,GAAS,CAAS,IAE5E,MAAc,KAAA,IAEd,MAAY,KAAA,IAET,CAAC,IAF0B,CAAC,CAAO,IAFN,CAAC,CAAS;AAKhD;AAIA,IAAa,IAA+B;CAC1C,OAAO;EAAE,OAAO;EAAM,IAAI;EAAW,QAAQ;EAAW,OAAO;CAAU;CACzE,OAAO;EAAE,OAAO;EAAM,IAAI;EAAW,QAAQ;EAAW,OAAO;CAAO;CACtE,OAAO;EAAE,OAAO;EAAM,IAAI;EAAW,QAAQ;EAAW,OAAO;CAAO;CACtE,OAAO;EAAE,OAAO;EAAM,IAAI;EAAW,QAAQ;EAAW,OAAO;CAAO;CACtE,MAAM;EAAE,OAAO;EAAM,IAAI;EAAW,QAAQ;EAAW,OAAO;CAAO;CACrE,IAAI;EAAE,OAAO;EAAI,IAAI;EAAW,QAAQ;EAAW,OAAO;CAAO;CACjE,MAAM;EAAE,OAAO;EAAM,IAAI;EAAW,QAAQ;EAAW,OAAO;CAAU;AAC1E,GAEM,IAAW,gGAEX,IAAiE;CACrE,OAAO;CACP,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;AACR;AAGA,SAAgB,EAAa,GAAmD;CAC9E,IAAI,CAAC,GAAU,OAAO;CAEtB,IAAM,IAAwB,EAAE,GAAG,EAAc;CAEjD,KAAK,IAAM,KAAO,OAAO,KAAK,CAAQ,GAAsC;EAC1E,IAAM,IAAQ,EAAS;EAEvB,AAAI,MAAO,EAAO,KAAO;GAAE,GAAG,EAAc;GAAM,GAAG;EAAM;CAC7D;CAEA,OAAO;AACT;AAIA,SAAS,EAAS,GAAuC;CACvD,OAAO;EAAC,SAAS,EAAI,MAAM,GAAG,CAAC,GAAG,EAAE;EAAG,SAAS,EAAI,MAAM,GAAG,CAAC,GAAG,EAAE;EAAG,SAAS,EAAI,MAAM,GAAG,CAAC,GAAG,EAAE;CAAC;AACrG;AAEA,SAAS,EAAU,GAAa,GAAsB;CACpD,IAAM,CAAC,GAAG,GAAG,KAAK,EAAS,CAAG;CAE9B,OAAO,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAK;AAC1C;AAEA,SAAS,EAAU,GAAsB;CACvC,OAAO,WAAW,EAAK;AACzB;AAEA,SAAS,IAAwB;CAG/B,OAFI,OAAO,SAAW,MAAoB,KAGvC,WAAwF,SAAS,QAAQ,UAC1G;AAEJ;AAEA,SAAS,EAAW,GAA0B,IAAQ,IAAY;CAChE,OAAO,eAAe,EAAM,GAAG,WAAW,EAAM,MAAM,sBAAsB,EAAM,OAAO,sCAAsC;AACjI;AAEA,SAAS,EAAoB,GAAmB;CAC9C,OAAO,EAAE,QAAQ,MAAM,IAAI;AAC7B;AAQA,SAAS,EACP,GACA,GACA,GACA,GACA,GACQ;CACR,IAAM,IAAI,EAAM,IACV,IAAY,EAAoB,EAAE,KAAK,GAEvC,IAAO,CADK,IAAU,EAAU,EAAE,IAAI,CAAS,IAAI,CAClC;CAEvB,IAAI,GAAW;EACb,IAAM,IAAgB,EAAoB,CAAS;EAEnD,EAAK,KAAK,IAAU,EAAU,IAAI,EAAc,EAAE,IAAI,IAAI,EAAc,EAAE;CAC5E;CAIA,OAFI,KAAW,EAAK,KAAK,IAAU,EAAU,CAAS,IAAI,CAAS,GAE5D,GAAG,EAAK,KAAK,KAAK,EAAE;AAC7B;AAEA,SAAS,EACP,GACA,GACA,GACA,GACkC;CAClC,IAAI,IAAM,KAAK,EAAoB,EAAM,EAAK,CAAC,KAAK,EAAE,KAChD,IAAkB,CAAC,EAAW,EAAM,EAAK,GAAG,EAAE;CAYpD,OAVI,MACF,KAAO,MAAM,EAAoB,CAAS,EAAE,KAC5C,EAAM,KAAK,EAAW,EAAM,IAAI,KAAK,GAAU,GAAG,EAAE,IAGlD,MACF,KAAO,MAAM,EAAU,KACvB,EAAM,KAAK,eAAe,EAAE,IAGvB;EAAE;EAAK;CAAM;AACtB;AAeA,SAAgB,EAAiB,IAAmC,CAAC,GAAc;CACjF,IAAM,IAAQ,EAAQ,SAAS,SACzB,IAAgB,EAAQ,aAAa,IACrC,IAAS,EAAQ,UAAU,OAC3B,IAAS,OAAO,SAAW,KAC3B,IAAU,EAAQ,SAAS,IAAS,EAAa,IAAI,KACrD,IAAW,EAAa,EAAQ,KAAK,GACrC,IACJ,MAAW,UAAU,MAAM,KAAK,UAAU,CAAC,IAAI,EAAQ;CAEzD,QAAQ,MAA0B;EAChC,IAAI,CAAC,EAAe,GAAO,EAAM,KAAK,GAAG;EAEzC,IAAM,IAAY,IAAgB,EAAM,UAAU,YAAY,CAAC,CAAC,MAAM,IAAI,EAAE,IAAI,IAC1E,IAAU,EAAa,EAAM,MAAM,EAAM,SAAS,CAAS,GAC3D,IAAS,QAAQ,EAAW,EAAM;EAExC,IAAI,GACF,EAAO,EAAgB,GAAU,EAAM,OAAO,EAAM,WAAW,GAAW,CAAO,GAAG,GAAG,CAAO;OACzF;GACL,IAAM,EAAE,QAAK,aAAU,EAAmB,GAAU,EAAM,OAAO,EAAM,WAAW,CAAS;GAE3F,EAAO,GAAK,GAAG,GAAO,GAAG,CAAO;EAClC;CACF;AACF;AAIA,SAAgB,EAAY,GAAoB,GAAe,GAAmB,GAA4B;CAC5G,IAAM,IAAK,IAAY,QAAQ,iBAAiB,QAAQ;CAGxD,IAFe,OAAO,SAAW,KAErB;EACV,IAAM,IAAO,CAAC,EAAM,MAAM,OAAO,CAAK;EAItC,AAFI,KAAW,EAAK,KAAK,IAAI,EAAU,EAAE,GAEzC,EAAG,EAAK,KAAK,KAAK,CAAC;EAEnB;CACF;CAEA,IAAI,IAAM,GAAG,EAAM,MAAM,MAAM,KAAK,EAAoB,CAAK,EAAE,KACzD,IAAkB,CAAC,EAAW,EAAM,OAAO,yCAAyC,GAAG,EAAE;CAO/F,AALI,MACF,KAAO,MAAM,EAAoB,CAAS,EAAE,KAC5C,EAAM,KAAK,EAAW,EAAM,IAAI,KAAK,EAAS,oBAAoB,GAAG,EAAE,IAGzE,EAAG,GAAK,GAAG,CAAK;AAClB"}
@@ -0,0 +1,2 @@
1
+ var e=class e extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}static is(t){return t instanceof e}},t=class extends e{constructor(e){super(`Transport threw an unhandled error`,{cause:e instanceof Error?e:void 0})}};exports.RuneError=e,exports.RuneTransportError=t;
2
+ //# sourceMappingURL=errors.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all rune errors. Use `instanceof RuneError` to catch any rune-originated error. */\nexport class RuneError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is RuneError {\n return err instanceof RuneError;\n }\n}\n\n/**\n * Constructed internally when a transport function throws during log entry emission.\n * Never thrown/propagated to the caller — the logger catches the underlying error, wraps it here\n * (available as `.cause`), and reports it via a dev-only warning so the failing transport cannot\n * crash the caller of `log.info()`/etc. or prevent sibling transports from receiving the entry.\n */\nexport class RuneTransportError extends RuneError {\n constructor(cause: unknown) {\n super('Transport threw an unhandled error', { cause: cause instanceof Error ? cause : undefined });\n }\n}\n"],"mappings":"AACA,IAAa,EAAb,MAAa,UAAkB,KAAM,CACnC,YAAY,EAAiB,EAAqB,CAChD,MAAM,EAAS,CAAI,EACnB,KAAK,KAAO,IAAI,OAAO,KACvB,OAAO,eAAe,KAAM,IAAI,OAAO,SAAS,CAClD,CAEA,OAAO,GAAG,EAAgC,CACxC,OAAO,aAAe,CACxB,CACF,EAQa,EAAb,cAAwC,CAAU,CAChD,YAAY,EAAgB,CAC1B,MAAM,qCAAsC,CAAE,MAAO,aAAiB,MAAQ,EAAQ,IAAA,EAAU,CAAC,CACnG,CACF"}
@@ -0,0 +1,15 @@
1
+ /** Base class for all rune errors. Use `instanceof RuneError` to catch any rune-originated error. */
2
+ export declare class RuneError extends Error {
3
+ constructor(message: string, opts?: ErrorOptions);
4
+ static is(err: unknown): err is RuneError;
5
+ }
6
+ /**
7
+ * Constructed internally when a transport function throws during log entry emission.
8
+ * Never thrown/propagated to the caller — the logger catches the underlying error, wraps it here
9
+ * (available as `.cause`), and reports it via a dev-only warning so the failing transport cannot
10
+ * crash the caller of `log.info()`/etc. or prevent sibling transports from receiving the entry.
11
+ */
12
+ export declare class RuneTransportError extends RuneError {
13
+ constructor(cause: unknown);
14
+ }
15
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qGAAqG;AACrG,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,YAAY;IAMhD,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,SAAS;CAG1C;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,SAAS;gBACnC,KAAK,EAAE,OAAO;CAG3B"}
package/dist/errors.js ADDED
@@ -0,0 +1,17 @@
1
+ //#region src/errors.ts
2
+ var e = class e extends Error {
3
+ constructor(e, t) {
4
+ super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
5
+ }
6
+ static is(t) {
7
+ return t instanceof e;
8
+ }
9
+ }, t = class extends e {
10
+ constructor(e) {
11
+ super("Transport threw an unhandled error", { cause: e instanceof Error ? e : void 0 });
12
+ }
13
+ };
14
+ //#endregion
15
+ export { e as RuneError, t as RuneTransportError };
16
+
17
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/** Base class for all rune errors. Use `instanceof RuneError` to catch any rune-originated error. */\nexport class RuneError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n static is(err: unknown): err is RuneError {\n return err instanceof RuneError;\n }\n}\n\n/**\n * Constructed internally when a transport function throws during log entry emission.\n * Never thrown/propagated to the caller — the logger catches the underlying error, wraps it here\n * (available as `.cause`), and reports it via a dev-only warning so the failing transport cannot\n * crash the caller of `log.info()`/etc. or prevent sibling transports from receiving the entry.\n */\nexport class RuneTransportError extends RuneError {\n constructor(cause: unknown) {\n super('Transport threw an unhandled error', { cause: cause instanceof Error ? cause : undefined });\n }\n}\n"],"mappings":";AACA,IAAa,IAAb,MAAa,UAAkB,MAAM;CACnC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAM,GAAS,CAAI,GACnB,KAAK,OAAO,IAAI,OAAO,MACvB,OAAO,eAAe,MAAM,IAAI,OAAO,SAAS;CAClD;CAEA,OAAO,GAAG,GAAgC;EACxC,OAAO,aAAe;CACxB;AACF,GAQa,IAAb,cAAwC,EAAU;CAChD,YAAY,GAAgB;EAC1B,MAAM,sCAAsC,EAAE,OAAO,aAAiB,QAAQ,IAAQ,KAAA,EAAU,CAAC;CACnG;AACF"}
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./types.cjs"),n=require("./lazy.cjs"),r=require("./console.cjs"),i=require("./logger.cjs"),a=require("./transports.cjs");exports.DEFAULT_THEME=r.DEFAULT_THEME,exports.PRIORITY=t.PRIORITY,exports.RuneError=e.RuneError,exports.RuneTransportError=e.RuneTransportError,exports.batchTransport=a.batchTransport,exports.consoleTransport=r.consoleTransport,exports.createLogger=i.createLogger,exports.defaultLogger=i.defaultLogger,exports.isLevelEnabled=t.isLevelEnabled,exports.jsonTransport=a.jsonTransport,exports.lazy=n.lazy,exports.pipe=a.pipe,exports.redactTransport=a.redactTransport,exports.remoteTransport=a.remoteTransport,exports.resolveTheme=r.resolveTheme,exports.sampleTransport=a.sampleTransport;
@@ -0,0 +1,10 @@
1
+ export type { BatchHandle, BatchTransportOptions, Bindings, JsonTransportOptions, LogEntry, LogLevel, LogMethod, LogMiddleware, Logger, LogType, PipeOptions, RedactTransportOptions, RemoteLogData, RemoteTransportOptions, RuneOptions, SampleTransportOptions, Transport, } from './types';
2
+ export { RuneError, RuneTransportError } from './errors';
3
+ export { isLevelEnabled, PRIORITY } from './types';
4
+ export type { LazyBinding } from './lazy';
5
+ export { lazy } from './lazy';
6
+ export { defaultLogger, createLogger } from './logger';
7
+ export type { ConsoleTheme, ConsoleThemeEntry, ConsoleTransportOptions, ResolvedTheme } from './console';
8
+ export { DEFAULT_THEME, consoleTransport, resolveTheme } from './console';
9
+ export { batchTransport, jsonTransport, pipe, redactTransport, remoteTransport, sampleTransport } from './transports';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,WAAW,EACX,qBAAqB,EACrB,QAAQ,EACR,oBAAoB,EACpB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,aAAa,EACb,MAAM,EACN,OAAO,EACP,WAAW,EACX,sBAAsB,EACtB,aAAa,EACb,sBAAsB,EACtB,WAAW,EACX,sBAAsB,EACtB,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnD,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACzG,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import { RuneError as e, RuneTransportError as t } from "./errors.js";
2
+ import { PRIORITY as n, isLevelEnabled as r } from "./types.js";
3
+ import { lazy as i } from "./lazy.js";
4
+ import { DEFAULT_THEME as a, consoleTransport as o, resolveTheme as s } from "./console.js";
5
+ import { createLogger as c, defaultLogger as l } from "./logger.js";
6
+ import { batchTransport as u, jsonTransport as d, pipe as f, redactTransport as p, remoteTransport as m, sampleTransport as h } from "./transports.js";
7
+ export { a as DEFAULT_THEME, n as PRIORITY, e as RuneError, t as RuneTransportError, u as batchTransport, o as consoleTransport, c as createLogger, l as defaultLogger, r as isLevelEnabled, d as jsonTransport, i as lazy, f as pipe, p as redactTransport, m as remoteTransport, s as resolveTheme, h as sampleTransport };
package/dist/lazy.cjs ADDED
@@ -0,0 +1,2 @@
1
+ const e=require("./_prototype.cjs");var t=Symbol(`rune.lazy`);function n(e){return typeof e==`object`&&!!e&&e[t]===!0}function r(e){return{factory:e,[t]:!0}}function i(t){let r;for(let[i,a]of Object.entries(t))n(a)&&!e.isUnsafeObjectKey(i)&&(r??={...t},r[i]=a.factory());return r??t}exports.lazy=r,exports.resolveBindings=i;
2
+ //# sourceMappingURL=lazy.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy.cjs","names":[],"sources":["../src/lazy.ts"],"sourcesContent":["import type { Bindings } from './types';\n\nimport { isUnsafeObjectKey } from './_prototype';\n\nconst LAZY_BRAND = Symbol('rune.lazy');\n\n/** A deferred binding value — evaluated only when an entry is actually emitted. Create via `lazy(fn)`. */\nexport type LazyBinding = { readonly factory: () => unknown; readonly [LAZY_BRAND]: true };\n\nfunction isLazy(v: unknown): v is LazyBinding {\n return typeof v === 'object' && v !== null && (v as Record<symbol, unknown>)[LAZY_BRAND] === true;\n}\n\n/**\n * Defer evaluation of an expensive binding value until after the log level check passes.\n * The factory function is only called when an entry is actually emitted.\n *\n * @example\n * const reqLog = log.withBindings({ diagnostics: lazy(() => buildExpensiveDiagnostics()) });\n * reqLog.debug('trace'); // diagnostics() only called when logLevel allows debug\n */\nexport function lazy(fn: () => unknown): LazyBinding {\n return { factory: fn, [LAZY_BRAND]: true } as LazyBinding;\n}\n\n/**\n * Resolve any lazy bindings in the given object, returning a plain Bindings object.\n * Non-lazy values are passed through unchanged. Single-pass: allocates only when a lazy\n * binding is actually present (copy-on-first-lazy).\n *\n * **Shallow only** — if a lazy factory returns an object that itself contains lazy bindings,\n * those nested lazy values are NOT resolved. Only top-level keys of the bindings object\n * are checked.\n */\nexport function resolveBindings(bindings: Bindings): Bindings {\n let resolved: Bindings | undefined;\n\n for (const [k, v] of Object.entries(bindings)) {\n // Guard against a `__proto__`/`constructor`/`prototype` binding key hijacking resolved's own\n // prototype via the bracket-assignment accessor — see _prototype.ts.\n if (isLazy(v) && !isUnsafeObjectKey(k)) {\n resolved ??= { ...bindings };\n resolved[k] = v.factory();\n }\n }\n\n return resolved ?? bindings;\n}\n"],"mappings":"oCAIA,IAAM,EAAa,OAAO,WAAW,EAKrC,SAAS,EAAO,EAA8B,CAC5C,OAAO,OAAO,GAAM,YAAY,GAAe,EAA8B,KAAgB,EAC/F,CAUA,SAAgB,EAAK,EAAgC,CACnD,MAAO,CAAE,QAAS,GAAK,GAAa,EAAK,CAC3C,CAWA,SAAgB,EAAgB,EAA8B,CAC5D,IAAI,EAEJ,IAAK,GAAM,CAAC,EAAG,KAAM,OAAO,QAAQ,CAAQ,EAGtC,EAAO,CAAC,GAAK,CAAC,EAAA,kBAAkB,CAAC,IACnC,IAAa,CAAE,GAAG,CAAS,EAC3B,EAAS,GAAK,EAAE,QAAQ,GAI5B,OAAO,GAAY,CACrB"}
package/dist/lazy.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ import type { Bindings } from './types';
2
+ declare const LAZY_BRAND: unique symbol;
3
+ /** A deferred binding value — evaluated only when an entry is actually emitted. Create via `lazy(fn)`. */
4
+ export type LazyBinding = {
5
+ readonly factory: () => unknown;
6
+ readonly [LAZY_BRAND]: true;
7
+ };
8
+ /**
9
+ * Defer evaluation of an expensive binding value until after the log level check passes.
10
+ * The factory function is only called when an entry is actually emitted.
11
+ *
12
+ * @example
13
+ * const reqLog = log.withBindings({ diagnostics: lazy(() => buildExpensiveDiagnostics()) });
14
+ * reqLog.debug('trace'); // diagnostics() only called when logLevel allows debug
15
+ */
16
+ export declare function lazy(fn: () => unknown): LazyBinding;
17
+ /**
18
+ * Resolve any lazy bindings in the given object, returning a plain Bindings object.
19
+ * Non-lazy values are passed through unchanged. Single-pass: allocates only when a lazy
20
+ * binding is actually present (copy-on-first-lazy).
21
+ *
22
+ * **Shallow only** — if a lazy factory returns an object that itself contains lazy bindings,
23
+ * those nested lazy values are NOT resolved. Only top-level keys of the bindings object
24
+ * are checked.
25
+ */
26
+ export declare function resolveBindings(bindings: Bindings): Bindings;
27
+ export {};
28
+ //# sourceMappingURL=lazy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../src/lazy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAIxC,QAAA,MAAM,UAAU,eAAsB,CAAC;AAEvC,0GAA0G;AAC1G,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC;IAAC,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAM3F;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,OAAO,GAAG,WAAW,CAEnD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAa5D"}
package/dist/lazy.js ADDED
@@ -0,0 +1,21 @@
1
+ import { isUnsafeObjectKey as e } from "./_prototype.js";
2
+ //#region src/lazy.ts
3
+ var t = Symbol("rune.lazy");
4
+ function n(e) {
5
+ return typeof e == "object" && !!e && e[t] === !0;
6
+ }
7
+ function r(e) {
8
+ return {
9
+ factory: e,
10
+ [t]: !0
11
+ };
12
+ }
13
+ function i(t) {
14
+ let r;
15
+ for (let [i, a] of Object.entries(t)) n(a) && !e(i) && (r ??= { ...t }, r[i] = a.factory());
16
+ return r ?? t;
17
+ }
18
+ //#endregion
19
+ export { r as lazy, i as resolveBindings };
20
+
21
+ //# sourceMappingURL=lazy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy.js","names":[],"sources":["../src/lazy.ts"],"sourcesContent":["import type { Bindings } from './types';\n\nimport { isUnsafeObjectKey } from './_prototype';\n\nconst LAZY_BRAND = Symbol('rune.lazy');\n\n/** A deferred binding value — evaluated only when an entry is actually emitted. Create via `lazy(fn)`. */\nexport type LazyBinding = { readonly factory: () => unknown; readonly [LAZY_BRAND]: true };\n\nfunction isLazy(v: unknown): v is LazyBinding {\n return typeof v === 'object' && v !== null && (v as Record<symbol, unknown>)[LAZY_BRAND] === true;\n}\n\n/**\n * Defer evaluation of an expensive binding value until after the log level check passes.\n * The factory function is only called when an entry is actually emitted.\n *\n * @example\n * const reqLog = log.withBindings({ diagnostics: lazy(() => buildExpensiveDiagnostics()) });\n * reqLog.debug('trace'); // diagnostics() only called when logLevel allows debug\n */\nexport function lazy(fn: () => unknown): LazyBinding {\n return { factory: fn, [LAZY_BRAND]: true } as LazyBinding;\n}\n\n/**\n * Resolve any lazy bindings in the given object, returning a plain Bindings object.\n * Non-lazy values are passed through unchanged. Single-pass: allocates only when a lazy\n * binding is actually present (copy-on-first-lazy).\n *\n * **Shallow only** — if a lazy factory returns an object that itself contains lazy bindings,\n * those nested lazy values are NOT resolved. Only top-level keys of the bindings object\n * are checked.\n */\nexport function resolveBindings(bindings: Bindings): Bindings {\n let resolved: Bindings | undefined;\n\n for (const [k, v] of Object.entries(bindings)) {\n // Guard against a `__proto__`/`constructor`/`prototype` binding key hijacking resolved's own\n // prototype via the bracket-assignment accessor — see _prototype.ts.\n if (isLazy(v) && !isUnsafeObjectKey(k)) {\n resolved ??= { ...bindings };\n resolved[k] = v.factory();\n }\n }\n\n return resolved ?? bindings;\n}\n"],"mappings":";;AAIA,IAAM,IAAa,OAAO,WAAW;AAKrC,SAAS,EAAO,GAA8B;CAC5C,OAAO,OAAO,KAAM,cAAY,KAAe,EAA8B,OAAgB;AAC/F;AAUA,SAAgB,EAAK,GAAgC;CACnD,OAAO;EAAE,SAAS;GAAK,IAAa;CAAK;AAC3C;AAWA,SAAgB,EAAgB,GAA8B;CAC5D,IAAI;CAEJ,KAAK,IAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,CAAQ,GAG1C,AAAI,EAAO,CAAC,KAAK,CAAC,EAAkB,CAAC,MACnC,MAAa,EAAE,GAAG,EAAS,GAC3B,EAAS,KAAK,EAAE,QAAQ;CAI5B,OAAO,KAAY;AACrB"}
@@ -0,0 +1,2 @@
1
+ const e=require("./errors.cjs"),t=require("./types.cjs"),n=require("./_prototype.cjs"),r=require("./lazy.cjs"),i=require("./_dev.cjs"),a=require("./console.cjs");function o(e){return{message:e.message,name:e.name,stack:e.stack}}function s(e){let t={};for(let[r,i]of Object.entries(e))n.isUnsafeObjectKey(r)||(t[r]=i instanceof Error?o(i):i);return t}function c(e,t,n){if(typeof e==`string`)return{context:void 0,message:e};if(e instanceof Error){let r=typeof t==`object`&&t?t:{};return{context:{err:o(e),...r},message:n===void 0?t!==void 0&&typeof t!=`object`?String(t):void 0:String(n)}}return typeof e==`object`&&e?{context:e,message:t===void 0?void 0:String(t)}:{context:void 0,message:e===void 0?void 0:String(e)}}function l(e,t){return e?t?`${e}.${t}`:e:t}function u(n={},d){let f=typeof n==`string`?{namespace:n,...d}:n,p=f.logLevel??`debug`,m=f.middleware??[],h=f.namespace??``,g=f.transports??[a.consoleTransport()],_={...f.bindings??{}},v=new AbortController,y=!1,b=e=>t.isLevelEnabled(p,e),x=()=>h?` (namespace: "${h}")`:``,S=t=>{let n=t;if(m.length>0){let e=t;for(let t of m)try{let n=t(e);if(n==null)return;e=n}catch(e){i.warn(`middleware threw and dropped this entry${x()}: ${String(e)}`);return}n=e}for(let t of g)try{t(n)}catch(t){i.warn(`${new e.RuneTransportError(t).message}${x()}: ${String(t)}`)}},C=(e,t,n,i)=>{if(y||!b(e))return;let{context:a,message:o}=c(t,n,i),l=s(r.resolveBindings(_)),u=a?{...l,...s(r.resolveBindings(a))}:l;S({data:u,level:e,message:o,namespace:h,timestamp:new Date})},w=(e,t,n=`debug`)=>{let r=performance.now(),i=t=>{let i={duration_ms:Math.round((performance.now()-r)*100)/100};t!==void 0&&(i.err=o(t instanceof Error?t:Error(String(t)))),C(n,i,e)};try{let e=t();return e instanceof Promise?e.then(e=>(i(),e),e=>(i(e),Promise.reject(e))):(i(),e)}catch(e){throw i(e),e}},T=(e,t,n,r)=>{if(y||p===`off`||r!==void 0&&!b(r))return n();a.renderGroup(e,t,h,a.DEFAULT_THEME);try{let e=n();return e instanceof Promise?e.finally(()=>console.groupEnd()):(console.groupEnd(),e)}catch(e){throw console.groupEnd(),e}},E=(e={})=>u({bindings:{..._,...e.bindings??{}},logLevel:e.logLevel??p,middleware:e.middleware??m,namespace:e.namespace===void 0?h:l(h,e.namespace),transports:e.transports??g}),D={get bindings(){return{..._}},child:E,debug:(e,t,n)=>C(`debug`,e,t,n),get disposalSignal(){return v.signal},dispose:()=>{y||(y=!0,v.abort())},get disposed(){return y},enabled:e=>t.isLevelEnabled(p,e),error:(e,t,n)=>C(`error`,e,t,n),fatal:(e,t,n)=>C(`fatal`,e,t,n),group:(e,t,n)=>T(!1,e,t,n),groupCollapsed:(e,t,n)=>T(!0,e,t,n),info:(e,t,n)=>C(`info`,e,t,n),get logLevel(){return p},get middleware(){return[...m]},get namespace(){return h},[Symbol.dispose](){D.dispose()},time:w,get transports(){return[...g]},use:e=>E({middleware:[...m,e]}),warn:(e,t,n)=>C(`warn`,e,t,n),withBindings:e=>E({bindings:e})};return D}var d=u();exports.createLogger=u,exports.defaultLogger=d;
2
+ //# sourceMappingURL=logger.cjs.map