@genesislcap/foundation-logger 15.41.0 → 15.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,6 +4,39 @@ Documentation for this package is published on the Genesis docs site:
4
4
 
5
5
  **Docs: [Utility methods](https://docs.genesis.global/docs/develop/client-capabilities/utility-methods/)**
6
6
 
7
+ ## Log output
8
+
9
+ Console messages do **not** include an in-message timestamp by default (Chrome's DevTools clock is separate). Opt in with a live global switch so every `createLogger` instance, including those already created by Foundation packages, prefixes messages with local time including milliseconds (for example `11:52:03.412`):
10
+
11
+ ```ts
12
+ import { setLogTimestamps } from '@genesislcap/foundation-logger';
13
+
14
+ setLogTimestamps(true);
15
+ ```
16
+
17
+ The flag is stored on `globalThis` via `Symbol.for`, so one call also reaches Module Federation remotes that bundled their own copy of this package (rather than sharing the host's module).
18
+
19
+ Enable or disable the prefix on one logger only:
20
+
21
+ ```ts
22
+ import { createLogger } from '@genesislcap/foundation-logger';
23
+
24
+ createLogger('demo', { timestamps: true });
25
+ createLogger('quiet', { timestamps: false });
26
+ createLogger('legacy', { formatOptions: { date: true } });
27
+ ```
28
+
29
+ Options passed to `createLogger` are always merged with `defaultLoggerOptions`. Callers that omit `level` therefore get `debug` (this package's default), not Consola's bare `info` default.
30
+
31
+ A default reporter is only installed in the browser. Outside the browser Consola has no default
32
+ reporter, so loggers stay silent unless you pass `reporters` explicitly:
33
+
34
+ ```ts
35
+ import { createLogger, TimestampBrowserReporter } from '@genesislcap/foundation-logger';
36
+
37
+ createLogger('node-side', { reporters: [new TimestampBrowserReporter()] });
38
+ ```
39
+
7
40
  ## Installation
8
41
 
9
42
  Add the package to your `package.json` dependencies. After changing dependencies, run `npm run bootstrap` (or your project's equivalent). See [package.json basics](https://learn.genesis.global/secure/web/basics/package-json-basics/) for more information.
@@ -1,2 +1,4 @@
1
+ export * from './log-timestamps';
1
2
  export * from './logger';
3
+ export * from './timestamp-browser-reporter';
2
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,8BAA8B,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Enables or disables the in-message timestamp prefix for all `createLogger` instances.
3
+ *
4
+ * @remarks
5
+ * This is a live flag: existing loggers pick it up on the next message. Per-logger
6
+ * `timestamps: true | false` and `formatOptions.date: true` still override it.
7
+ *
8
+ * The value is stored on `globalThis` via `Symbol.for` so one call covers every runtime
9
+ * copy of this package (for example host + federated remotes that did not share the module).
10
+ *
11
+ * @example Enable timestamps for every Foundation logger from app bootstrap.
12
+ * ```ts
13
+ * import { setLogTimestamps } from '@genesislcap/foundation-logger';
14
+ * setLogTimestamps(true);
15
+ * ```
16
+ *
17
+ * @param enabled - Whether to prefix console messages with a local timestamp.
18
+ * @public
19
+ */
20
+ export declare function setLogTimestamps(enabled: boolean): void;
21
+ /**
22
+ * Returns whether the global timestamp prefix is currently enabled.
23
+ *
24
+ * @returns `true` when {@link setLogTimestamps} was last called with `true`.
25
+ * @public
26
+ */
27
+ export declare function areLogTimestampsEnabled(): boolean;
28
+ //# sourceMappingURL=log-timestamps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-timestamps.d.ts","sourceRoot":"","sources":["../../src/log-timestamps.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEvD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD"}
@@ -14,9 +14,18 @@ import { ConsolaInstance, ConsolaOptions } from 'consola/core';
14
14
  export { LogLevel, LogLevels, LogType } from 'consola/core';
15
15
  /**
16
16
  * Options for creating a logger.
17
+ *
18
+ * @remarks
19
+ * `timestamps` overrides the global {@link setLogTimestamps} flag for this logger only.
20
+ * `undefined` follows the global flag; `true` always prefixes; `false` never prefixes.
21
+ *
17
22
  * @public
18
23
  */
19
24
  export interface LoggerOptions extends Partial<ConsolaOptions> {
25
+ /**
26
+ * Per-logger timestamp prefix override.
27
+ */
28
+ timestamps?: boolean;
20
29
  }
21
30
  /**
22
31
  * A logger that extends the `Consola` logger.
@@ -57,12 +66,29 @@ export interface Logger extends ConsolaInstance {
57
66
  }
58
67
  /**
59
68
  * The default logger options.
69
+ *
70
+ * @remarks
71
+ * `formatOptions.date` is `false` so Consola's built-in `date: true` default does not turn
72
+ * the prefix on. Enable timestamps with {@link setLogTimestamps} or per-logger options.
73
+ *
74
+ * These defaults are always merged into `createLogger` options. Callers that pass options
75
+ * without a `level` therefore get `debug`, not Consola's bare `info` default.
76
+ *
60
77
  * @public
61
78
  */
62
79
  export declare const defaultLoggerOptions: LoggerOptions;
63
80
  /**
64
81
  * Creates a logger with the given name and options.
65
82
  *
83
+ * @remarks
84
+ * Timestamp prefixes are opt-in. Call {@link setLogTimestamps} once at app bootstrap to enable
85
+ * them for every logger (including instances already created), or pass `timestamps: true` /
86
+ * `formatOptions.date: true` on a single logger. All Consola options are forwarded, not only
87
+ * `level`.
88
+ *
89
+ * A default reporter is only installed in the browser. Outside the browser, pass `reporters`
90
+ * explicitly to produce output.
91
+ *
66
92
  * @example Create a logger in my-package/utils.
67
93
  * ```ts
68
94
  * import { createLogger } from '@genesislcap/foundation-logger';
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,cAAc,EAGf,MAAM,cAAc,CAAC;AAEtB;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5D;;;GAGG;AACH,MAAM,WAAW,aAAc,SAAQ,OAAO,CAAC,cAAc,CAAC;CAAG;AAEjE;;;;;;;GAOG;AACH,MAAM,WAAW,MAAO,SAAQ,eAAe;IAC7C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvF;AAED;;;GAGG;AACH,MAAM,WAAW,MAAO,SAAQ,eAAe;CAAG;AAElD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,aAElC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAoC,GAAG,MAAM,CAgBhG"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,cAAc,EAGf,MAAM,cAAc,CAAC;AAItB;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5D;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAc,SAAQ,OAAO,CAAC,cAAc,CAAC;IAC5D;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,MAAO,SAAQ,eAAe;IAC7C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvF;AAED;;;GAGG;AACH,MAAM,WAAW,MAAO,SAAQ,eAAe;CAAG;AAElD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,EAAE,aAKlC,CAAC;AAwBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAgB9E"}
@@ -0,0 +1,45 @@
1
+ import type { ConsolaOptions, ConsolaReporter, LogObject } from 'consola/core';
2
+ /**
3
+ * Formats a log timestamp as local time with milliseconds.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * formatLogTimestamp(new Date('2026-01-15T11:52:03.412')); // "11:52:03.412" in local time
8
+ * ```
9
+ *
10
+ * @param date - The date to format, typically Consola's `logObj.date`.
11
+ * @returns A `HH:mm:ss.SSS` timestamp string.
12
+ * @public
13
+ */
14
+ export declare function formatLogTimestamp(date: Date): string;
15
+ /**
16
+ * Consola reporter based on the built-in browser reporter, with a local timestamp prefix.
17
+ *
18
+ * @remarks
19
+ * Prefixes each message with {@link formatLogTimestamp} using Consola's `logObj.date`.
20
+ * In browsers the original badge styling is preserved; in non-browser environments a
21
+ * plain `[tag:type]` label is used so `%c` CSS specifiers are not printed literally.
22
+ *
23
+ * Off by default. Enable for every logger with `setLogTimestamps(true)`, for one logger
24
+ * with `timestamps: true` or `formatOptions.date: true`, or replace the reporter via
25
+ * `createLogger(name, { reporters })`.
26
+ *
27
+ * @public
28
+ */
29
+ export declare class TimestampBrowserReporter implements ConsolaReporter {
30
+ private readonly defaultColor;
31
+ private readonly levelColorMap;
32
+ private readonly typeColorMap;
33
+ /**
34
+ * Writes a log object to the console, prefixed with `logObj.date` when enabled.
35
+ *
36
+ * @param logObj - The Consola log object. See Consola's `LogObject`.
37
+ * @param ctx - Reporter context, including Consola options.
38
+ */
39
+ log(logObj: LogObject, ctx: {
40
+ options: ConsolaOptions;
41
+ }): void;
42
+ private logWithBrowserStyles;
43
+ private logPlain;
44
+ }
45
+ //# sourceMappingURL=timestamp-browser-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestamp-browser-reporter.d.ts","sourceRoot":"","sources":["../../src/timestamp-browser-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAgB/E;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAMrD;AAwBD;;;;;;;;;;;;;GAaG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IAC9D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAa;IAC1C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAI5B;IACF,OAAO,CAAC,QAAQ,CAAC,YAAY,CAE3B;IAEF;;;;;OAKG;IACH,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE;QAAE,OAAO,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI;IAe9D,OAAO,CAAC,oBAAoB;IA2B5B,OAAO,CAAC,QAAQ;CAyBjB"}
package/dist/esm/index.js CHANGED
@@ -1 +1,3 @@
1
+ export * from './log-timestamps';
1
2
  export * from './logger';
3
+ export * from './timestamp-browser-reporter';
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared across every copy of this module (including Module Federation remotes that
3
+ * bundled their own `@genesislcap/foundation-logger`). A plain module-level `let` would
4
+ * leave each copy with its own flag, so the host's `setLogTimestamps` would not reach them.
5
+ */
6
+ const LOG_TIMESTAMPS_FLAG = Symbol.for('@genesislcap/foundation-logger:timestamps');
7
+ /**
8
+ * Enables or disables the in-message timestamp prefix for all `createLogger` instances.
9
+ *
10
+ * @remarks
11
+ * This is a live flag: existing loggers pick it up on the next message. Per-logger
12
+ * `timestamps: true | false` and `formatOptions.date: true` still override it.
13
+ *
14
+ * The value is stored on `globalThis` via `Symbol.for` so one call covers every runtime
15
+ * copy of this package (for example host + federated remotes that did not share the module).
16
+ *
17
+ * @example Enable timestamps for every Foundation logger from app bootstrap.
18
+ * ```ts
19
+ * import { setLogTimestamps } from '@genesislcap/foundation-logger';
20
+ * setLogTimestamps(true);
21
+ * ```
22
+ *
23
+ * @param enabled - Whether to prefix console messages with a local timestamp.
24
+ * @public
25
+ */
26
+ export function setLogTimestamps(enabled) {
27
+ Reflect.set(globalThis, LOG_TIMESTAMPS_FLAG, enabled);
28
+ }
29
+ /**
30
+ * Returns whether the global timestamp prefix is currently enabled.
31
+ *
32
+ * @returns `true` when {@link setLogTimestamps} was last called with `true`.
33
+ * @public
34
+ */
35
+ export function areLogTimestampsEnabled() {
36
+ return Reflect.get(globalThis, LOG_TIMESTAMPS_FLAG) === true;
37
+ }
@@ -1,5 +1,6 @@
1
1
  import { createConsola as createConsolaBrowser } from 'consola/browser';
2
2
  import { LogLevels, createConsola as createConsolaCore, } from 'consola/core';
3
+ import { TimestampBrowserReporter } from './timestamp-browser-reporter';
3
4
  /**
4
5
  * Used to set the logging level.
5
6
  *
@@ -15,14 +16,47 @@ import { LogLevels, createConsola as createConsolaCore, } from 'consola/core';
15
16
  export { LogLevels } from 'consola/core';
16
17
  /**
17
18
  * The default logger options.
19
+ *
20
+ * @remarks
21
+ * `formatOptions.date` is `false` so Consola's built-in `date: true` default does not turn
22
+ * the prefix on. Enable timestamps with {@link setLogTimestamps} or per-logger options.
23
+ *
24
+ * These defaults are always merged into `createLogger` options. Callers that pass options
25
+ * without a `level` therefore get `debug`, not Consola's bare `info` default.
26
+ *
18
27
  * @public
19
28
  */
20
29
  export const defaultLoggerOptions = {
21
30
  level: LogLevels.debug,
31
+ formatOptions: {
32
+ date: false,
33
+ },
22
34
  };
35
+ const browserReporter = new TimestampBrowserReporter();
36
+ function resolveLoggerOptions(options, isBrowser) {
37
+ const resolved = Object.assign(Object.assign(Object.assign({}, defaultLoggerOptions), options), { formatOptions: Object.assign(Object.assign({}, defaultLoggerOptions.formatOptions), options.formatOptions) });
38
+ /**
39
+ * Consola ships no reporter outside the browser, so `reporters` is left unset there to keep
40
+ * non-browser output silent. Setting it would start emitting logs from Node-side consumers
41
+ * and test runs that have never produced any.
42
+ */
43
+ if (isBrowser && !resolved.reporters) {
44
+ resolved.reporters = [browserReporter];
45
+ }
46
+ return resolved;
47
+ }
23
48
  /**
24
49
  * Creates a logger with the given name and options.
25
50
  *
51
+ * @remarks
52
+ * Timestamp prefixes are opt-in. Call {@link setLogTimestamps} once at app bootstrap to enable
53
+ * them for every logger (including instances already created), or pass `timestamps: true` /
54
+ * `formatOptions.date: true` on a single logger. All Consola options are forwarded, not only
55
+ * `level`.
56
+ *
57
+ * A default reporter is only installed in the browser. Outside the browser, pass `reporters`
58
+ * explicitly to produce output.
59
+ *
26
60
  * @example Create a logger in my-package/utils.
27
61
  * ```ts
28
62
  * import { createLogger } from '@genesislcap/foundation-logger';
@@ -49,11 +83,10 @@ export const defaultLoggerOptions = {
49
83
  * @returns The resulting logger.
50
84
  * @public
51
85
  */
52
- export function createLogger(name, options = defaultLoggerOptions) {
53
- const createConsola = typeof window !== 'undefined' ? createConsolaBrowser : createConsolaCore;
54
- const logger = createConsola().withTag(name);
55
- if ((options === null || options === void 0 ? void 0 : options.level) !== undefined)
56
- logger.level = options.level;
86
+ export function createLogger(name, options = {}) {
87
+ const isBrowser = typeof window !== 'undefined';
88
+ const createConsola = isBrowser ? createConsolaBrowser : createConsolaCore;
89
+ const logger = createConsola(resolveLoggerOptions(options, isBrowser)).withTag(name);
57
90
  const typedLogger = logger;
58
91
  typedLogger.deprecated = (symbol, instruction = 'Consult docs for better alternative.', removalVersionTarget) => {
59
92
  typedLogger.warn(`Deprecated symbol used '${symbol}'. ${instruction}`, `${removalVersionTarget ? `Will be removed in version ${removalVersionTarget}.` : ''}`);
@@ -0,0 +1,118 @@
1
+ import { areLogTimestampsEnabled } from './log-timestamps';
2
+ const TIME_UNIT_DIGITS = 2;
3
+ const MILLISECOND_DIGITS = 3;
4
+ /**
5
+ * Formats a log timestamp as local time with milliseconds.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * formatLogTimestamp(new Date('2026-01-15T11:52:03.412')); // "11:52:03.412" in local time
10
+ * ```
11
+ *
12
+ * @param date - The date to format, typically Consola's `logObj.date`.
13
+ * @returns A `HH:mm:ss.SSS` timestamp string.
14
+ * @public
15
+ */
16
+ export function formatLogTimestamp(date) {
17
+ const hours = String(date.getHours()).padStart(TIME_UNIT_DIGITS, '0');
18
+ const minutes = String(date.getMinutes()).padStart(TIME_UNIT_DIGITS, '0');
19
+ const seconds = String(date.getSeconds()).padStart(TIME_UNIT_DIGITS, '0');
20
+ const milliseconds = String(date.getMilliseconds()).padStart(MILLISECOND_DIGITS, '0');
21
+ return `${hours}:${minutes}:${seconds}.${milliseconds}`;
22
+ }
23
+ function getConsoleLogFn(level) {
24
+ const consolaConsole = console;
25
+ if (level < 1) {
26
+ return consolaConsole.__error || consolaConsole.error;
27
+ }
28
+ if (level === 1) {
29
+ return consolaConsole.__warn || consolaConsole.warn;
30
+ }
31
+ return consolaConsole.__log || consolaConsole.log;
32
+ }
33
+ function shouldIncludeTimestamp(ctx) {
34
+ var _a, _b, _c;
35
+ const timestamps = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.options) === null || _a === void 0 ? void 0 : _a.timestamps;
36
+ if (typeof timestamps === 'boolean') {
37
+ return timestamps;
38
+ }
39
+ if (((_c = (_b = ctx === null || ctx === void 0 ? void 0 : ctx.options) === null || _b === void 0 ? void 0 : _b.formatOptions) === null || _c === void 0 ? void 0 : _c.date) === true) {
40
+ return true;
41
+ }
42
+ return areLogTimestampsEnabled();
43
+ }
44
+ /**
45
+ * Consola reporter based on the built-in browser reporter, with a local timestamp prefix.
46
+ *
47
+ * @remarks
48
+ * Prefixes each message with {@link formatLogTimestamp} using Consola's `logObj.date`.
49
+ * In browsers the original badge styling is preserved; in non-browser environments a
50
+ * plain `[tag:type]` label is used so `%c` CSS specifiers are not printed literally.
51
+ *
52
+ * Off by default. Enable for every logger with `setLogTimestamps(true)`, for one logger
53
+ * with `timestamps: true` or `formatOptions.date: true`, or replace the reporter via
54
+ * `createLogger(name, { reporters })`.
55
+ *
56
+ * @public
57
+ */
58
+ export class TimestampBrowserReporter {
59
+ constructor() {
60
+ this.defaultColor = '#7f8c8d';
61
+ this.levelColorMap = {
62
+ 0: '#c0392b',
63
+ 1: '#f39c12',
64
+ 3: '#00BCD4',
65
+ };
66
+ this.typeColorMap = {
67
+ success: '#2ecc71',
68
+ };
69
+ }
70
+ /**
71
+ * Writes a log object to the console, prefixed with `logObj.date` when enabled.
72
+ *
73
+ * @param logObj - The Consola log object. See Consola's `LogObject`.
74
+ * @param ctx - Reporter context, including Consola options.
75
+ */
76
+ log(logObj, ctx) {
77
+ const consoleLogFn = getConsoleLogFn(logObj.level);
78
+ const type = logObj.type === 'log' ? '' : logObj.type;
79
+ const tag = logObj.tag || '';
80
+ const timestamp = shouldIncludeTimestamp(ctx) ? formatLogTimestamp(logObj.date) : '';
81
+ const useBrowserStyles = typeof window !== 'undefined' && !!(tag || type);
82
+ if (useBrowserStyles) {
83
+ this.logWithBrowserStyles(consoleLogFn, logObj, tag, type, timestamp);
84
+ return;
85
+ }
86
+ this.logPlain(consoleLogFn, logObj, tag, type, timestamp);
87
+ }
88
+ logWithBrowserStyles(consoleLogFn, logObj, tag, type, timestamp) {
89
+ const color = this.typeColorMap[logObj.type] || this.levelColorMap[logObj.level] || this.defaultColor;
90
+ const style = `
91
+ background: ${color};
92
+ border-radius: 0.5em;
93
+ color: white;
94
+ font-weight: bold;
95
+ padding: 2px 0.5em;
96
+ `;
97
+ const badge = `%c${[tag, type].filter(Boolean).join(':')}`;
98
+ const prefix = timestamp ? `${timestamp} ${badge}` : badge;
99
+ if (typeof logObj.args[0] === 'string') {
100
+ consoleLogFn(`${prefix}%c ${logObj.args[0]}`, style, '', ...logObj.args.slice(1));
101
+ return;
102
+ }
103
+ consoleLogFn(prefix, style, ...logObj.args);
104
+ }
105
+ logPlain(consoleLogFn, logObj, tag, type, timestamp) {
106
+ const label = [tag, type].filter(Boolean).join(':');
107
+ const prefix = [timestamp, label ? `[${label}]` : ''].filter(Boolean).join(' ');
108
+ if (typeof logObj.args[0] === 'string') {
109
+ consoleLogFn(prefix ? `${prefix} ${logObj.args[0]}` : logObj.args[0], ...logObj.args.slice(1));
110
+ return;
111
+ }
112
+ if (prefix) {
113
+ consoleLogFn(prefix, ...logObj.args);
114
+ return;
115
+ }
116
+ consoleLogFn(...logObj.args);
117
+ }
118
+ }
@@ -172,10 +172,38 @@
172
172
  "name": "",
173
173
  "preserveMemberOrder": false,
174
174
  "members": [
175
+ {
176
+ "kind": "Function",
177
+ "canonicalReference": "@genesislcap/foundation-logger!areLogTimestampsEnabled:function(1)",
178
+ "docComment": "/**\n * Returns whether the global timestamp prefix is currently enabled.\n *\n * @returns `true` when {@link setLogTimestamps} was last called with `true`.\n *\n * @public\n */\n",
179
+ "excerptTokens": [
180
+ {
181
+ "kind": "Content",
182
+ "text": "export declare function areLogTimestampsEnabled(): "
183
+ },
184
+ {
185
+ "kind": "Content",
186
+ "text": "boolean"
187
+ },
188
+ {
189
+ "kind": "Content",
190
+ "text": ";"
191
+ }
192
+ ],
193
+ "fileUrlPath": "src/log-timestamps.ts",
194
+ "returnTypeTokenRange": {
195
+ "startIndex": 1,
196
+ "endIndex": 2
197
+ },
198
+ "releaseTag": "Public",
199
+ "overloadIndex": 1,
200
+ "parameters": [],
201
+ "name": "areLogTimestampsEnabled"
202
+ },
175
203
  {
176
204
  "kind": "Function",
177
205
  "canonicalReference": "@genesislcap/foundation-logger!createLogger:function(1)",
178
- "docComment": "/**\n * Creates a logger with the given name and options.\n *\n * @param name - The name to give the logger.\n *\n * @param options - The options to use when creating the logger.\n *\n * @returns The resulting logger.\n *\n * @example\n *\n * Create a logger in my-package/utils.\n * ```ts\n * import { createLogger } from '@genesislcap/foundation-logger';\n * export const logger = createLogger('my-package');\n * ```\n *\n * @example\n *\n * Using the logger within my-package.\n * ```ts\n * import { logger } from '../utils';\n * logger.debug(`Data retrieved from ${resourceName}`);\n * logger.info(this.toggled ? 'On' : 'Off');\n * logger.warn('Not available in this browser.');\n * ```\n *\n * @example\n *\n * Explicitly set the level of an imported package.\n * ```ts\n * import { logger as commsLogger } from '@genesislcap/foundation-comms';\n * import { LogLevel } from '@genesislcap/foundation-logger';\n * commsLogger.level = LogLevel.Warn;\n * ```\n *\n * @public\n */\n",
206
+ "docComment": "/**\n * Creates a logger with the given name and options.\n *\n * @remarks\n *\n * Timestamp prefixes are opt-in. Call {@link setLogTimestamps} once at app bootstrap to enable them for every logger (including instances already created), or pass `timestamps: true` / `formatOptions.date: true` on a single logger. All Consola options are forwarded, not only `level`.\n *\n * A default reporter is only installed in the browser. Outside the browser, pass `reporters` explicitly to produce output.\n *\n * @param name - The name to give the logger.\n *\n * @param options - The options to use when creating the logger.\n *\n * @returns The resulting logger.\n *\n * @example\n *\n * Create a logger in my-package/utils.\n * ```ts\n * import { createLogger } from '@genesislcap/foundation-logger';\n * export const logger = createLogger('my-package');\n * ```\n *\n * @example\n *\n * Using the logger within my-package.\n * ```ts\n * import { logger } from '../utils';\n * logger.debug(`Data retrieved from ${resourceName}`);\n * logger.info(this.toggled ? 'On' : 'Off');\n * logger.warn('Not available in this browser.');\n * ```\n *\n * @example\n *\n * Explicitly set the level of an imported package.\n * ```ts\n * import { logger as commsLogger } from '@genesislcap/foundation-comms';\n * import { LogLevel } from '@genesislcap/foundation-logger';\n * commsLogger.level = LogLevel.Warn;\n * ```\n *\n * @public\n */\n",
179
207
  "excerptTokens": [
180
208
  {
181
209
  "kind": "Content",
@@ -238,7 +266,7 @@
238
266
  {
239
267
  "kind": "Variable",
240
268
  "canonicalReference": "@genesislcap/foundation-logger!defaultLoggerOptions:var",
241
- "docComment": "/**\n * The default logger options.\n *\n * @public\n */\n",
269
+ "docComment": "/**\n * The default logger options.\n *\n * @remarks\n *\n * `formatOptions.date` is `false` so Consola's built-in `date: true` default does not turn the prefix on. Enable timestamps with {@link setLogTimestamps} or per-logger options.\n *\n * These defaults are always merged into `createLogger` options. Callers that pass options without a `level` therefore get `debug`, not Consola's bare `info` default.\n *\n * @public\n */\n",
242
270
  "excerptTokens": [
243
271
  {
244
272
  "kind": "Content",
@@ -259,6 +287,52 @@
259
287
  "endIndex": 2
260
288
  }
261
289
  },
290
+ {
291
+ "kind": "Function",
292
+ "canonicalReference": "@genesislcap/foundation-logger!formatLogTimestamp:function(1)",
293
+ "docComment": "/**\n * Formats a log timestamp as local time with milliseconds.\n *\n * @param date - The date to format, typically Consola's `logObj.date`.\n *\n * @returns A `HH:mm:ss.SSS` timestamp string.\n *\n * @example\n * ```ts\n * formatLogTimestamp(new Date('2026-01-15T11:52:03.412')); // \"11:52:03.412\" in local time\n * ```\n *\n * @public\n */\n",
294
+ "excerptTokens": [
295
+ {
296
+ "kind": "Content",
297
+ "text": "export declare function formatLogTimestamp(date: "
298
+ },
299
+ {
300
+ "kind": "Reference",
301
+ "text": "Date",
302
+ "canonicalReference": "!Date:interface"
303
+ },
304
+ {
305
+ "kind": "Content",
306
+ "text": "): "
307
+ },
308
+ {
309
+ "kind": "Content",
310
+ "text": "string"
311
+ },
312
+ {
313
+ "kind": "Content",
314
+ "text": ";"
315
+ }
316
+ ],
317
+ "fileUrlPath": "src/timestamp-browser-reporter.ts",
318
+ "returnTypeTokenRange": {
319
+ "startIndex": 3,
320
+ "endIndex": 4
321
+ },
322
+ "releaseTag": "Public",
323
+ "overloadIndex": 1,
324
+ "parameters": [
325
+ {
326
+ "parameterName": "date",
327
+ "parameterTypeTokenRange": {
328
+ "startIndex": 1,
329
+ "endIndex": 2
330
+ },
331
+ "isOptional": false
332
+ }
333
+ ],
334
+ "name": "formatLogTimestamp"
335
+ },
262
336
  {
263
337
  "kind": "Interface",
264
338
  "canonicalReference": "@genesislcap/foundation-logger!Logger:interface",
@@ -371,7 +445,7 @@
371
445
  {
372
446
  "kind": "Interface",
373
447
  "canonicalReference": "@genesislcap/foundation-logger!LoggerOptions:interface",
374
- "docComment": "/**\n * Options for creating a logger.\n *\n * @public\n */\n",
448
+ "docComment": "/**\n * Options for creating a logger.\n *\n * @remarks\n *\n * `timestamps` overrides the global {@link setLogTimestamps} flag for this logger only. `undefined` follows the global flag; `true` always prefixes; `false` never prefixes.\n *\n * @public\n */\n",
375
449
  "excerptTokens": [
376
450
  {
377
451
  "kind": "Content",
@@ -404,13 +478,193 @@
404
478
  "releaseTag": "Public",
405
479
  "name": "LoggerOptions",
406
480
  "preserveMemberOrder": false,
407
- "members": [],
481
+ "members": [
482
+ {
483
+ "kind": "PropertySignature",
484
+ "canonicalReference": "@genesislcap/foundation-logger!LoggerOptions#timestamps:member",
485
+ "docComment": "/**\n * Per-logger timestamp prefix override.\n */\n",
486
+ "excerptTokens": [
487
+ {
488
+ "kind": "Content",
489
+ "text": "timestamps?: "
490
+ },
491
+ {
492
+ "kind": "Content",
493
+ "text": "boolean"
494
+ },
495
+ {
496
+ "kind": "Content",
497
+ "text": ";"
498
+ }
499
+ ],
500
+ "isReadonly": false,
501
+ "isOptional": true,
502
+ "releaseTag": "Public",
503
+ "name": "timestamps",
504
+ "propertyTypeTokenRange": {
505
+ "startIndex": 1,
506
+ "endIndex": 2
507
+ }
508
+ }
509
+ ],
408
510
  "extendsTokenRanges": [
409
511
  {
410
512
  "startIndex": 1,
411
513
  "endIndex": 5
412
514
  }
413
515
  ]
516
+ },
517
+ {
518
+ "kind": "Function",
519
+ "canonicalReference": "@genesislcap/foundation-logger!setLogTimestamps:function(1)",
520
+ "docComment": "/**\n * Enables or disables the in-message timestamp prefix for all `createLogger` instances.\n *\n * @remarks\n *\n * This is a live flag: existing loggers pick it up on the next message. Per-logger `timestamps: true | false` and `formatOptions.date: true` still override it.\n *\n * The value is stored on `globalThis` via `Symbol.for` so one call covers every runtime copy of this package (for example host + federated remotes that did not share the module).\n *\n * @param enabled - Whether to prefix console messages with a local timestamp.\n *\n * @example\n *\n * Enable timestamps for every Foundation logger from app bootstrap.\n * ```ts\n * import { setLogTimestamps } from '@genesislcap/foundation-logger';\n * setLogTimestamps(true);\n * ```\n *\n * @public\n */\n",
521
+ "excerptTokens": [
522
+ {
523
+ "kind": "Content",
524
+ "text": "export declare function setLogTimestamps(enabled: "
525
+ },
526
+ {
527
+ "kind": "Content",
528
+ "text": "boolean"
529
+ },
530
+ {
531
+ "kind": "Content",
532
+ "text": "): "
533
+ },
534
+ {
535
+ "kind": "Content",
536
+ "text": "void"
537
+ },
538
+ {
539
+ "kind": "Content",
540
+ "text": ";"
541
+ }
542
+ ],
543
+ "fileUrlPath": "src/log-timestamps.ts",
544
+ "returnTypeTokenRange": {
545
+ "startIndex": 3,
546
+ "endIndex": 4
547
+ },
548
+ "releaseTag": "Public",
549
+ "overloadIndex": 1,
550
+ "parameters": [
551
+ {
552
+ "parameterName": "enabled",
553
+ "parameterTypeTokenRange": {
554
+ "startIndex": 1,
555
+ "endIndex": 2
556
+ },
557
+ "isOptional": false
558
+ }
559
+ ],
560
+ "name": "setLogTimestamps"
561
+ },
562
+ {
563
+ "kind": "Class",
564
+ "canonicalReference": "@genesislcap/foundation-logger!TimestampBrowserReporter:class",
565
+ "docComment": "/**\n * Consola reporter based on the built-in browser reporter, with a local timestamp prefix.\n *\n * @remarks\n *\n * Prefixes each message with {@link formatLogTimestamp} using Consola's `logObj.date`. In browsers the original badge styling is preserved; in non-browser environments a plain `[tag:type]` label is used so `%c` CSS specifiers are not printed literally.\n *\n * Off by default. Enable for every logger with `setLogTimestamps(true)`, for one logger with `timestamps: true` or `formatOptions.date: true`, or replace the reporter via `createLogger(name, { reporters })`.\n *\n * @public\n */\n",
566
+ "excerptTokens": [
567
+ {
568
+ "kind": "Content",
569
+ "text": "export declare class TimestampBrowserReporter implements "
570
+ },
571
+ {
572
+ "kind": "Reference",
573
+ "text": "ConsolaReporter",
574
+ "canonicalReference": "consola!ConsolaReporter:interface"
575
+ },
576
+ {
577
+ "kind": "Content",
578
+ "text": " "
579
+ }
580
+ ],
581
+ "fileUrlPath": "src/timestamp-browser-reporter.ts",
582
+ "releaseTag": "Public",
583
+ "isAbstract": false,
584
+ "name": "TimestampBrowserReporter",
585
+ "preserveMemberOrder": false,
586
+ "members": [
587
+ {
588
+ "kind": "Method",
589
+ "canonicalReference": "@genesislcap/foundation-logger!TimestampBrowserReporter#log:member(1)",
590
+ "docComment": "/**\n * Writes a log object to the console, prefixed with `logObj.date` when enabled.\n *\n * @param logObj - The Consola log object. See Consola's `LogObject`.\n *\n * @param ctx - Reporter context, including Consola options.\n */\n",
591
+ "excerptTokens": [
592
+ {
593
+ "kind": "Content",
594
+ "text": "log(logObj: "
595
+ },
596
+ {
597
+ "kind": "Reference",
598
+ "text": "LogObject",
599
+ "canonicalReference": "consola!LogObject:interface"
600
+ },
601
+ {
602
+ "kind": "Content",
603
+ "text": ", ctx: "
604
+ },
605
+ {
606
+ "kind": "Content",
607
+ "text": "{\n options: "
608
+ },
609
+ {
610
+ "kind": "Reference",
611
+ "text": "ConsolaOptions",
612
+ "canonicalReference": "consola!ConsolaOptions:interface"
613
+ },
614
+ {
615
+ "kind": "Content",
616
+ "text": ";\n }"
617
+ },
618
+ {
619
+ "kind": "Content",
620
+ "text": "): "
621
+ },
622
+ {
623
+ "kind": "Content",
624
+ "text": "void"
625
+ },
626
+ {
627
+ "kind": "Content",
628
+ "text": ";"
629
+ }
630
+ ],
631
+ "isStatic": false,
632
+ "returnTypeTokenRange": {
633
+ "startIndex": 7,
634
+ "endIndex": 8
635
+ },
636
+ "releaseTag": "Public",
637
+ "isProtected": false,
638
+ "overloadIndex": 1,
639
+ "parameters": [
640
+ {
641
+ "parameterName": "logObj",
642
+ "parameterTypeTokenRange": {
643
+ "startIndex": 1,
644
+ "endIndex": 2
645
+ },
646
+ "isOptional": false
647
+ },
648
+ {
649
+ "parameterName": "ctx",
650
+ "parameterTypeTokenRange": {
651
+ "startIndex": 3,
652
+ "endIndex": 6
653
+ },
654
+ "isOptional": false
655
+ }
656
+ ],
657
+ "isOptional": false,
658
+ "isAbstract": false,
659
+ "name": "log"
660
+ }
661
+ ],
662
+ "implementsTokenRanges": [
663
+ {
664
+ "startIndex": 1,
665
+ "endIndex": 2
666
+ }
667
+ ]
414
668
  }
415
669
  ]
416
670
  }
@@ -1,12 +1,31 @@
1
1
  import { ConsolaInstance } from 'consola/core';
2
2
  import { ConsolaOptions } from 'consola/core';
3
+ import type { ConsolaReporter } from 'consola/core';
3
4
  import { LogLevel } from 'consola/core';
4
5
  import { LogLevels } from 'consola/core';
6
+ import type { LogObject } from 'consola/core';
5
7
  import { LogType } from 'consola/core';
6
8
 
9
+ /**
10
+ * Returns whether the global timestamp prefix is currently enabled.
11
+ *
12
+ * @returns `true` when {@link setLogTimestamps} was last called with `true`.
13
+ * @public
14
+ */
15
+ export declare function areLogTimestampsEnabled(): boolean;
16
+
7
17
  /**
8
18
  * Creates a logger with the given name and options.
9
19
  *
20
+ * @remarks
21
+ * Timestamp prefixes are opt-in. Call {@link setLogTimestamps} once at app bootstrap to enable
22
+ * them for every logger (including instances already created), or pass `timestamps: true` /
23
+ * `formatOptions.date: true` on a single logger. All Consola options are forwarded, not only
24
+ * `level`.
25
+ *
26
+ * A default reporter is only installed in the browser. Outside the browser, pass `reporters`
27
+ * explicitly to produce output.
28
+ *
10
29
  * @example Create a logger in my-package/utils.
11
30
  * ```ts
12
31
  * import { createLogger } from '@genesislcap/foundation-logger';
@@ -37,10 +56,32 @@ export declare function createLogger(name: string, options?: LoggerOptions): Log
37
56
 
38
57
  /**
39
58
  * The default logger options.
59
+ *
60
+ * @remarks
61
+ * `formatOptions.date` is `false` so Consola's built-in `date: true` default does not turn
62
+ * the prefix on. Enable timestamps with {@link setLogTimestamps} or per-logger options.
63
+ *
64
+ * These defaults are always merged into `createLogger` options. Callers that pass options
65
+ * without a `level` therefore get `debug`, not Consola's bare `info` default.
66
+ *
40
67
  * @public
41
68
  */
42
69
  export declare const defaultLoggerOptions: LoggerOptions;
43
70
 
71
+ /**
72
+ * Formats a log timestamp as local time with milliseconds.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * formatLogTimestamp(new Date('2026-01-15T11:52:03.412')); // "11:52:03.412" in local time
77
+ * ```
78
+ *
79
+ * @param date - The date to format, typically Consola's `logObj.date`.
80
+ * @returns A `HH:mm:ss.SSS` timestamp string.
81
+ * @public
82
+ */
83
+ export declare function formatLogTimestamp(date: Date): string;
84
+
44
85
  /**
45
86
  * A logger that extends the `Consola` logger.
46
87
  *
@@ -82,9 +123,18 @@ export declare interface Logger extends ConsolaInstance {
82
123
 
83
124
  /**
84
125
  * Options for creating a logger.
126
+ *
127
+ * @remarks
128
+ * `timestamps` overrides the global {@link setLogTimestamps} flag for this logger only.
129
+ * `undefined` follows the global flag; `true` always prefixes; `false` never prefixes.
130
+ *
85
131
  * @public
86
132
  */
87
133
  export declare interface LoggerOptions extends Partial<ConsolaOptions> {
134
+ /**
135
+ * Per-logger timestamp prefix override.
136
+ */
137
+ timestamps?: boolean;
88
138
  }
89
139
 
90
140
  export { LogLevel }
@@ -93,4 +143,56 @@ export { LogLevels }
93
143
 
94
144
  export { LogType }
95
145
 
146
+ /**
147
+ * Enables or disables the in-message timestamp prefix for all `createLogger` instances.
148
+ *
149
+ * @remarks
150
+ * This is a live flag: existing loggers pick it up on the next message. Per-logger
151
+ * `timestamps: true | false` and `formatOptions.date: true` still override it.
152
+ *
153
+ * The value is stored on `globalThis` via `Symbol.for` so one call covers every runtime
154
+ * copy of this package (for example host + federated remotes that did not share the module).
155
+ *
156
+ * @example Enable timestamps for every Foundation logger from app bootstrap.
157
+ * ```ts
158
+ * import { setLogTimestamps } from '@genesislcap/foundation-logger';
159
+ * setLogTimestamps(true);
160
+ * ```
161
+ *
162
+ * @param enabled - Whether to prefix console messages with a local timestamp.
163
+ * @public
164
+ */
165
+ export declare function setLogTimestamps(enabled: boolean): void;
166
+
167
+ /**
168
+ * Consola reporter based on the built-in browser reporter, with a local timestamp prefix.
169
+ *
170
+ * @remarks
171
+ * Prefixes each message with {@link formatLogTimestamp} using Consola's `logObj.date`.
172
+ * In browsers the original badge styling is preserved; in non-browser environments a
173
+ * plain `[tag:type]` label is used so `%c` CSS specifiers are not printed literally.
174
+ *
175
+ * Off by default. Enable for every logger with `setLogTimestamps(true)`, for one logger
176
+ * with `timestamps: true` or `formatOptions.date: true`, or replace the reporter via
177
+ * `createLogger(name, { reporters })`.
178
+ *
179
+ * @public
180
+ */
181
+ export declare class TimestampBrowserReporter implements ConsolaReporter {
182
+ private readonly defaultColor;
183
+ private readonly levelColorMap;
184
+ private readonly typeColorMap;
185
+ /**
186
+ * Writes a log object to the console, prefixed with `logObj.date` when enabled.
187
+ *
188
+ * @param logObj - The Consola log object. See Consola's `LogObject`.
189
+ * @param ctx - Reporter context, including Consola options.
190
+ */
191
+ log(logObj: LogObject, ctx: {
192
+ options: ConsolaOptions;
193
+ }): void;
194
+ private logWithBrowserStyles;
195
+ private logPlain;
196
+ }
197
+
96
198
  export { }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/foundation-logger",
3
3
  "description": "Genesis Foundation Logger",
4
- "version": "15.41.0",
4
+ "version": "15.43.0",
5
5
  "sideEffects": false,
6
6
  "license": "SEE LICENSE IN license.txt",
7
7
  "main": "dist/esm/index.js",
@@ -12,6 +12,8 @@
12
12
  "circular": "npx -y madge --extensions ts --circular ./src",
13
13
  "clean": "rimraf dist temp tsconfig.tsbuildinfo",
14
14
  "dev": "genx dev -b ts",
15
+ "//test": "Node uvu (not genx test / Playwright) so createLogger's typeof-window===undefined path is covered; Playwright always defines window and would silently lose that case.",
16
+ "test": "uvu -r tsx/cjs . 'test.ts' --i dist --i coverage --i temp",
15
17
  "lint": "genx lint -l ox",
16
18
  "lint:fix": "genx lint -l ox --fix"
17
19
  },
@@ -22,6 +24,12 @@
22
24
  }
23
25
  }
24
26
  },
27
+ "devDependencies": {
28
+ "@types/sinon": "^10.0.13",
29
+ "sinon": "^17.0.1",
30
+ "tsx": "^4.7.0",
31
+ "uvu": "0.5.4"
32
+ },
25
33
  "dependencies": {
26
34
  "consola": "^3.0.0"
27
35
  },