@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.
- package/README.md +95 -0
- package/dist/_dev.cjs +2 -0
- package/dist/_dev.cjs.map +1 -0
- package/dist/_dev.d.ts +2 -0
- package/dist/_dev.d.ts.map +1 -0
- package/dist/_dev.js +9 -0
- package/dist/_dev.js.map +1 -0
- package/dist/_prototype.cjs +2 -0
- package/dist/_prototype.cjs.map +1 -0
- package/dist/_prototype.d.ts +2 -0
- package/dist/_prototype.d.ts.map +1 -0
- package/dist/_prototype.js +13 -0
- package/dist/_prototype.js.map +1 -0
- package/dist/console.cjs +2 -0
- package/dist/console.cjs.map +1 -0
- package/dist/console.d.ts +65 -0
- package/dist/console.d.ts.map +1 -0
- package/dist/console.js +132 -0
- package/dist/console.js.map +1 -0
- package/dist/errors.cjs +2 -0
- package/dist/errors.cjs.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +17 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/lazy.cjs +2 -0
- package/dist/lazy.cjs.map +1 -0
- package/dist/lazy.d.ts +28 -0
- package/dist/lazy.d.ts.map +1 -0
- package/dist/lazy.js +21 -0
- package/dist/lazy.js.map +1 -0
- package/dist/logger.cjs +2 -0
- package/dist/logger.cjs.map +1 -0
- package/dist/logger.d.ts +15 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +158 -0
- package/dist/logger.js.map +1 -0
- package/dist/rune.cjs +3 -0
- package/dist/rune.cjs.map +1 -0
- package/dist/rune.iife.js +3 -0
- package/dist/rune.iife.js.map +1 -0
- package/dist/rune.js +3 -0
- package/dist/rune.js.map +1 -0
- package/dist/transports.cjs +3 -0
- package/dist/transports.cjs.map +1 -0
- package/dist/transports.d.ts +88 -0
- package/dist/transports.d.ts.map +1 -0
- package/dist/transports.js +111 -0
- package/dist/transports.js.map +1 -0
- package/dist/types.cjs +2 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.ts +274 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- 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
|
+
[](https://www.npmjs.com/package/@vielzeug/rune) [](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
<details>
|
|
8
|
+
<summary>Quick Reference</summary>
|
|
9
|
+
|
|
10
|
+
**Package:** `@vielzeug/rune` · **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 @@
|
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"_dev.d.ts","sourceRoot":"","sources":["../src/_dev.ts"],"names":[],"mappings":""}
|
package/dist/_dev.js
ADDED
package/dist/_dev.js.map
ADDED
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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"}
|
package/dist/console.cjs
ADDED
|
@@ -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"}
|
package/dist/console.js
ADDED
|
@@ -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":"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"}
|
package/dist/errors.cjs
ADDED
|
@@ -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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -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;
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
package/dist/lazy.js.map
ADDED
|
@@ -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"}
|
package/dist/logger.cjs
ADDED
|
@@ -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
|