@dxos/log 0.8.3 → 0.8.4-main.16b68245aa

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/lib/browser/chunk-IEP6GGEX.mjs +23 -0
  2. package/dist/lib/browser/chunk-IEP6GGEX.mjs.map +7 -0
  3. package/dist/lib/browser/chunk-V7FYKT4H.mjs +311 -0
  4. package/dist/lib/browser/chunk-V7FYKT4H.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +359 -207
  6. package/dist/lib/browser/index.mjs.map +4 -4
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/platform/browser/index.mjs +26 -0
  9. package/dist/lib/browser/platform/browser/index.mjs.map +7 -0
  10. package/dist/lib/browser/platform/node/index.mjs +21 -0
  11. package/dist/lib/browser/platform/node/index.mjs.map +7 -0
  12. package/dist/lib/browser/processors/console-processor.mjs +102 -0
  13. package/dist/lib/browser/processors/console-processor.mjs.map +7 -0
  14. package/dist/lib/browser/processors/console-stub.mjs +9 -0
  15. package/dist/lib/browser/processors/console-stub.mjs.map +7 -0
  16. package/dist/lib/node-esm/chunk-2SZHAWBN.mjs +24 -0
  17. package/dist/lib/node-esm/chunk-2SZHAWBN.mjs.map +7 -0
  18. package/dist/lib/node-esm/chunk-5TBDXMQF.mjs +313 -0
  19. package/dist/lib/node-esm/chunk-5TBDXMQF.mjs.map +7 -0
  20. package/dist/lib/node-esm/index.mjs +362 -295
  21. package/dist/lib/node-esm/index.mjs.map +4 -4
  22. package/dist/lib/node-esm/meta.json +1 -1
  23. package/dist/lib/node-esm/platform/browser/index.mjs +27 -0
  24. package/dist/lib/node-esm/platform/browser/index.mjs.map +7 -0
  25. package/dist/lib/node-esm/platform/node/index.mjs +22 -0
  26. package/dist/lib/node-esm/platform/node/index.mjs.map +7 -0
  27. package/dist/lib/node-esm/processors/console-processor.mjs +103 -0
  28. package/dist/lib/node-esm/processors/console-processor.mjs.map +7 -0
  29. package/dist/lib/node-esm/processors/console-stub.mjs +10 -0
  30. package/dist/lib/node-esm/processors/console-stub.mjs.map +7 -0
  31. package/dist/types/src/config.d.ts +2 -3
  32. package/dist/types/src/config.d.ts.map +1 -1
  33. package/dist/types/src/context.d.ts +79 -3
  34. package/dist/types/src/context.d.ts.map +1 -1
  35. package/dist/types/src/dbg.d.ts +23 -0
  36. package/dist/types/src/dbg.d.ts.map +1 -0
  37. package/dist/types/src/decorators.d.ts +1 -1
  38. package/dist/types/src/decorators.d.ts.map +1 -1
  39. package/dist/types/src/environment.d.ts +24 -0
  40. package/dist/types/src/environment.d.ts.map +1 -0
  41. package/dist/types/src/environment.test.d.ts +2 -0
  42. package/dist/types/src/environment.test.d.ts.map +1 -0
  43. package/dist/types/src/experimental/ownership.d.ts.map +1 -1
  44. package/dist/types/src/index.d.ts +7 -3
  45. package/dist/types/src/index.d.ts.map +1 -1
  46. package/dist/types/src/jsonl.d.ts +53 -0
  47. package/dist/types/src/jsonl.d.ts.map +1 -0
  48. package/dist/types/src/jsonl.test.d.ts +2 -0
  49. package/dist/types/src/jsonl.test.d.ts.map +1 -0
  50. package/dist/types/src/log-buffer.d.ts +20 -0
  51. package/dist/types/src/log-buffer.d.ts.map +1 -0
  52. package/dist/types/src/log-buffer.test.d.ts +2 -0
  53. package/dist/types/src/log-buffer.test.d.ts.map +1 -0
  54. package/dist/types/src/log.d.ts +55 -18
  55. package/dist/types/src/log.d.ts.map +1 -1
  56. package/dist/types/src/meta.d.ts +20 -1
  57. package/dist/types/src/meta.d.ts.map +1 -1
  58. package/dist/types/src/options.d.ts +1 -6
  59. package/dist/types/src/options.d.ts.map +1 -1
  60. package/dist/types/src/platform/browser/index.d.ts.map +1 -1
  61. package/dist/types/src/platform/index.d.ts +1 -1
  62. package/dist/types/src/platform/index.d.ts.map +1 -1
  63. package/dist/types/src/platform/node/index.d.ts.map +1 -1
  64. package/dist/types/src/processors/browser-processor.d.ts.map +1 -1
  65. package/dist/types/src/processors/common.d.ts.map +1 -1
  66. package/dist/types/src/processors/console-processor.d.ts +1 -1
  67. package/dist/types/src/processors/console-processor.d.ts.map +1 -1
  68. package/dist/types/src/processors/file-processor.d.ts.map +1 -1
  69. package/dist/types/src/processors/index.d.ts +3 -3
  70. package/dist/types/src/processors/index.d.ts.map +1 -1
  71. package/dist/types/src/scope.d.ts.map +1 -1
  72. package/dist/types/tsconfig.tsbuildinfo +1 -1
  73. package/package.json +32 -17
  74. package/src/config.ts +3 -2
  75. package/src/context.ts +280 -10
  76. package/src/dbg.ts +34 -0
  77. package/src/decorators.ts +3 -3
  78. package/src/environment.test.ts +222 -0
  79. package/src/environment.ts +129 -0
  80. package/src/experimental/classes.test.ts +1 -1
  81. package/src/index.ts +7 -4
  82. package/src/jsonl.test.ts +121 -0
  83. package/src/jsonl.ts +104 -0
  84. package/src/log-buffer.test.ts +158 -0
  85. package/src/log-buffer.ts +89 -0
  86. package/src/log.test.ts +58 -23
  87. package/src/log.ts +147 -60
  88. package/src/meta.ts +29 -1
  89. package/src/options.ts +27 -11
  90. package/src/platform/index.ts +1 -1
  91. package/src/processors/browser-processor.ts +32 -29
  92. package/src/processors/console-processor.ts +11 -15
  93. package/src/processors/file-processor.ts +9 -8
  94. package/src/processors/index.ts +3 -3
  95. package/src/scope.ts +1 -1
  96. package/dist/lib/node/index.cjs +0 -695
  97. package/dist/lib/node/index.cjs.map +0 -7
  98. package/dist/lib/node/meta.json +0 -1
package/src/log.test.ts CHANGED
@@ -3,10 +3,11 @@
3
3
  //
4
4
 
5
5
  import path from 'node:path';
6
- import { describe, test } from 'vitest';
6
+ import { beforeEach, describe, test } from 'vitest';
7
7
 
8
8
  import { LogLevel } from './config';
9
- import { log } from './log';
9
+ import { shouldLog } from './context';
10
+ import { type Log, createLog } from './log';
10
11
 
11
12
  class LogError extends Error {
12
13
  constructor(
@@ -24,14 +25,53 @@ class LogError extends Error {
24
25
  }
25
26
  }
26
27
 
27
- log.config({
28
- filter: LogLevel.DEBUG,
29
- });
28
+ describe('log', () => {
29
+ let log!: Log;
30
+
31
+ beforeEach(() => {
32
+ log = createLog();
33
+ log.config({
34
+ filter: LogLevel.DEBUG,
35
+ });
36
+ });
30
37
 
31
- /* eslint-disable @stayradiated/prefer-arrow-functions/prefer-arrow-functions */
38
+ test('filters', ({ expect }) => {
39
+ const tests = [
40
+ { expected: 0, filter: 'ERROR' },
41
+ { expected: 2, filter: 'INFO' },
42
+ { expected: 1, filter: 'foo:INFO' },
43
+ { expected: 4, filter: 'DEBUG' },
44
+ { expected: 2, filter: 'DEBUG,-foo:*' },
45
+ { expected: 1, filter: 'INFO,-foo:*' },
46
+ { expected: 3, filter: 'DEBUG,-foo:INFO' },
47
+ { expected: 3, filter: 'foo:DEBUG,bar:INFO' },
48
+ ];
49
+
50
+ for (const test of tests) {
51
+ let count = 0;
52
+ const log = createLog();
53
+ const remove = log.addProcessor((config, entry) => {
54
+ if (shouldLog(entry, config.filters)) {
55
+ count++;
56
+ }
57
+ });
58
+ log.config({
59
+ filter: test.filter,
60
+ });
61
+
62
+ console.group(`Filter: "${test.filter}"`);
63
+ log.debug('line 1', {}, { F: 'foo.ts', L: 1, S: undefined });
64
+ log.info('line 2', {}, { F: 'foo.ts', L: 2, S: undefined });
65
+ log.debug('line 3', {}, { F: 'bar.ts', L: 3, S: undefined });
66
+ log.info('line 4', {}, { F: 'bar.ts', L: 4, S: undefined });
67
+ console.groupEnd();
68
+
69
+ expect(count, `Filter: "${test.filter}"`).toBe(test.expected);
70
+ remove();
71
+ }
72
+ });
32
73
 
33
- describe('log', function () {
34
- test('throws an error', function () {
74
+ test('throws an error', () => {
35
75
  try {
36
76
  throw new LogError('Test failed', { value: 1 });
37
77
  } catch (err: any) {
@@ -39,7 +79,7 @@ describe('log', function () {
39
79
  }
40
80
  });
41
81
 
42
- test('throws an error showing stacktrace', function () {
82
+ test('throws an error showing stacktrace', () => {
43
83
  try {
44
84
  throw new LogError('Test failed', { value: 2 });
45
85
  } catch (err: any) {
@@ -47,7 +87,7 @@ describe('log', function () {
47
87
  }
48
88
  });
49
89
 
50
- test('catches an error', function () {
90
+ test('catches an error', () => {
51
91
  try {
52
92
  throw new LogError('ERROR ON LINE 21', { value: 3 });
53
93
  } catch (err: any) {
@@ -55,17 +95,7 @@ describe('log', function () {
55
95
  }
56
96
  });
57
97
 
58
- test('config', function () {
59
- log.config({
60
- filter: LogLevel.INFO,
61
- });
62
-
63
- log.debug('Debug level log message');
64
- log.info('Info level log message');
65
- log.warn('Warn level log message');
66
- });
67
-
68
- test('config file', function () {
98
+ test('config file', () => {
69
99
  log.config({
70
100
  file: path.join('packages/common/log/test-config.yml'),
71
101
  });
@@ -75,7 +105,7 @@ describe('log', function () {
75
105
  log.warn('Warn level log message');
76
106
  });
77
107
 
78
- test('levels', function () {
108
+ test('levels', () => {
79
109
  log('Default level log message');
80
110
  log.debug('Debug level log message');
81
111
  log.info('Info level log message');
@@ -83,10 +113,15 @@ describe('log', function () {
83
113
  log.error('Error level log message');
84
114
  });
85
115
 
86
- test('context', function () {
116
+ test('context', () => {
87
117
  log.info('Message with context', {
88
118
  title: 'test',
89
119
  context: 123,
90
120
  });
91
121
  });
122
+
123
+ test('error', () => {
124
+ const myError = new Error('Test error', { cause: new Error('Cause') });
125
+ log.catch(myError);
126
+ });
92
127
  });
package/src/log.ts CHANGED
@@ -3,10 +3,19 @@
3
3
  //
4
4
 
5
5
  import { type LogConfig, LogLevel, type LogOptions } from './config';
6
- import { type LogContext, type LogProcessor } from './context';
6
+ import { type LogContext, LogEntry, type LogProcessor } from './context';
7
7
  import { createFunctionLogDecorator, createMethodLogDecorator } from './decorators';
8
8
  import { type CallMetadata } from './meta';
9
- import { getConfig, DEFAULT_PROCESSORS } from './options';
9
+ import { createConfig } from './options';
10
+
11
+ /**
12
+ * Accessible from browser console.
13
+ * Example: `DX_LOG.config({ filter: 'ERROR' })`
14
+ * NOTE: File level filtering isn't supported in storybooks.
15
+ */
16
+ declare global {
17
+ const DX_LOG: Log;
18
+ }
10
19
 
11
20
  /**
12
21
  * Logging function.
@@ -17,99 +26,185 @@ type LogFunction = (message: string, context?: LogContext, meta?: CallMetadata)
17
26
  * Logging methods.
18
27
  */
19
28
  export interface LogMethods {
29
+ config: (options?: LogOptions) => Log;
30
+ addProcessor: (processor: LogProcessor, addDefault?: boolean) => () => void;
31
+
32
+ /**
33
+ * Log at `trace` level.
34
+ *
35
+ * Generally not surfaced to the developer and not captured in a log file.
36
+ */
20
37
  trace: LogFunction;
38
+
39
+ /**
40
+ * Log at `debug` level.
41
+ * Generally not surfaced to the developer and captured in a log file.
42
+ */
21
43
  debug: LogFunction;
44
+
45
+ /**
46
+ * Log at `verbose` level.
47
+ * Generally not surfaced to the developer and not captured in a log file.
48
+ */
22
49
  verbose: LogFunction;
50
+
51
+ /**
52
+ * Log at `info` level.
53
+ * Generally surfaced to the developer and captured in a log file.
54
+ */
23
55
  info: LogFunction;
56
+
57
+ /**
58
+ * Log at `warn` level.
59
+ * Generally surfaced to the developer and captured in a log file.
60
+ */
24
61
  warn: LogFunction;
62
+
63
+ /**
64
+ * Log at `error` level.
65
+ * Generally surfaced to the developer and captured in a log file.
66
+ */
25
67
  error: LogFunction;
68
+
69
+ /**
70
+ * Log an error and its stack trace at an `error` level.
71
+ * Generally surfaced to the developer and captured in a log file.
72
+ */
26
73
  catch: (error: Error | any, context?: LogContext, meta?: CallMetadata) => void;
27
- break: () => void;
28
- stack: (message?: string, context?: never, meta?: CallMetadata) => void;
74
+
75
+ /**
76
+ * Decorator to log method parameters and return value at the `info` level.
77
+ */
29
78
  method: (arg0?: never, arg1?: never, meta?: CallMetadata) => MethodDecorator;
30
- func: <F extends (...args: any[]) => any>(
79
+
80
+ /**
81
+ * Wrapper to log function parameters and return value at the `info` level.
82
+ */
83
+ function: <F extends (...args: any[]) => any>(
31
84
  name: string,
32
85
  fn: F,
33
- opts?: { transformOutput?: (result: ReturnType<F>) => Promise<any> | any },
86
+ opts?: {
87
+ transformOutput?: (result: ReturnType<F>) => Promise<any> | any;
88
+ },
34
89
  ) => F;
90
+
91
+ /**
92
+ * Log a horizontal rule at the `info` level.
93
+ */
94
+ break: () => void;
95
+
96
+ /**
97
+ * Log a stack trace at the `info` level.
98
+ */
99
+ stack: (message?: string, context?: never, meta?: CallMetadata) => void;
35
100
  }
36
101
 
37
102
  /**
38
103
  * Properties accessible on the logging function.
104
+ * @internal
39
105
  */
40
- interface Log extends LogMethods, LogFunction {
41
- config: (options: LogOptions) => void;
42
- addProcessor: (processor: LogProcessor) => void;
43
- runtimeConfig: LogConfig;
106
+ export interface Log extends LogFunction, LogMethods {
107
+ readonly runtimeConfig: LogConfig;
44
108
  }
45
109
 
110
+ /**
111
+ * @internal
112
+ */
46
113
  interface LogImp extends Log {
114
+ _id: string;
47
115
  _config: LogConfig;
48
116
  }
49
117
 
50
- const createLog = (): LogImp => {
51
- const log: LogImp = ((...params) => processLog(LogLevel.DEBUG, ...params)) as LogImp;
52
-
53
- log._config = getConfig();
54
- Object.defineProperty(log, 'runtimeConfig', { get: () => log._config });
55
-
56
- log.addProcessor = (processor: LogProcessor) => {
57
- if (DEFAULT_PROCESSORS.filter((p) => p === processor).length === 0) {
58
- DEFAULT_PROCESSORS.push(processor);
59
- }
60
- if (log._config.processors.filter((p) => p === processor).length === 0) {
61
- log._config.processors.push(processor);
62
- }
63
- };
118
+ let logCount = 0;
64
119
 
65
- // Set config.
66
- log.config = (options: LogOptions) => {
67
- log._config = getConfig(options);
68
- };
69
-
70
- // TODO(burdon): API to set context and separate error object.
71
- // E.g., log.warn('failed', { key: 123 }, err);
72
-
73
- log.trace = (...params) => processLog(LogLevel.TRACE, ...params);
74
- log.debug = (...params) => processLog(LogLevel.DEBUG, ...params);
75
- log.verbose = (...params) => processLog(LogLevel.VERBOSE, ...params);
76
- log.info = (...params) => processLog(LogLevel.INFO, ...params);
77
- log.warn = (...params) => processLog(LogLevel.WARN, ...params);
78
- log.error = (...params) => processLog(LogLevel.ERROR, ...params);
79
-
80
- // Catch only shows error message, not stacktrace.
81
- log.catch = (error: Error | any, context, meta) =>
82
- processLog(LogLevel.ERROR, error?.message ?? String(error), context, meta, error);
83
-
84
- // Show break.
85
- log.break = () => log.info('——————————————————————————————————————————————————');
120
+ /**
121
+ * Create a logging function with properties.
122
+ * @internal
123
+ */
124
+ export const createLog = (): LogImp => {
125
+ // Default function.
126
+ const log: LogImp = ((...params) => processLog(LogLevel.DEBUG, ...params)) as LogImp;
86
127
 
87
- log.stack = (message, context, meta) =>
88
- processLog(LogLevel.INFO, `${message ?? 'Stack Dump'}\n${getFormattedStackTrace()}`, context, meta);
128
+ // Add private properties.
129
+ Object.assign<LogImp, Partial<LogImp>>(log, {
130
+ _id: `log-${++logCount}`,
131
+ _config: createConfig(),
132
+ });
89
133
 
90
- log.method = createMethodLogDecorator(log);
91
- log.func = createFunctionLogDecorator(log);
134
+ // TODO(burdon): Document.
135
+ Object.defineProperty(log, 'runtimeConfig', {
136
+ get: () => log._config,
137
+ });
92
138
 
93
139
  /**
94
140
  * Process the current log call.
95
141
  */
96
142
  const processLog = (
97
143
  level: LogLevel,
98
- message: string,
144
+ message: string | undefined,
99
145
  context: LogContext = {},
100
146
  meta?: CallMetadata,
101
147
  error?: Error,
102
148
  ) => {
103
- log._config.processors.forEach((processor) => processor(log._config, { level, message, context, meta, error }));
149
+ // TODO(burdon): Do the filter matching upstream (here) rather than in each processor?
150
+ const entry = new LogEntry({ level, message, context, meta, error });
151
+ log._config.processors.forEach((processor) => processor(log._config, entry));
104
152
  };
105
153
 
154
+ /**
155
+ * API.
156
+ */
157
+ Object.assign<Log, LogMethods>(log, {
158
+ /**
159
+ * Update config.
160
+ * NOTE: Preserves any processors that were already added to this logger instance
161
+ * unless an explicit processor option is provided.
162
+ */
163
+ config: ({ processor, ...options } = {}) => {
164
+ const config = createConfig(options);
165
+ // TODO(burdon): This could be buggy since the behavior is not reentrant.
166
+ const processors = processor ? config.processors : log._config.processors;
167
+ log._config = { ...config, processors };
168
+ return log;
169
+ },
170
+
171
+ /**
172
+ * Adds a processor to the logger.
173
+ */
174
+ addProcessor: (processor) => {
175
+ if (log._config.processors.filter((p) => p === processor).length === 0) {
176
+ log._config.processors.push(processor);
177
+ }
178
+
179
+ return () => {
180
+ log._config.processors = log._config.processors.filter((p) => p !== processor);
181
+ };
182
+ },
183
+
184
+ trace: (...params) => processLog(LogLevel.TRACE, ...params),
185
+ debug: (...params) => processLog(LogLevel.DEBUG, ...params),
186
+ verbose: (...params) => processLog(LogLevel.VERBOSE, ...params),
187
+ info: (...params) => processLog(LogLevel.INFO, ...params),
188
+ warn: (...params) => processLog(LogLevel.WARN, ...params),
189
+ error: (...params) => processLog(LogLevel.ERROR, ...params),
190
+ catch: (error, context, meta) => processLog(LogLevel.ERROR, undefined, context, meta, error),
191
+
192
+ method: createMethodLogDecorator(log),
193
+ function: createFunctionLogDecorator(log),
194
+
195
+ break: () => log.info('-'.repeat(80)),
196
+ stack: (message, context, meta) => {
197
+ return processLog(LogLevel.INFO, `${message ?? 'Stack Dump'}\n${getFormattedStackTrace()}`, context, meta);
198
+ },
199
+ });
200
+
106
201
  return log;
107
202
  };
108
203
 
109
204
  /**
110
205
  * Global logging function.
111
206
  */
112
- export const log: Log = ((globalThis as any).dx_log ??= createLog());
207
+ export const log: Log = ((globalThis as any).DX_LOG ??= createLog());
113
208
 
114
209
  const start = Date.now();
115
210
  let last = start;
@@ -129,12 +224,4 @@ export const debug = (label?: any, args?: any) => {
129
224
  last = Date.now();
130
225
  };
131
226
 
132
- /**
133
- * Accessible from browser console.
134
- */
135
- declare global {
136
- // eslint-disable-next-line camelcase
137
- const dx_log: Log;
138
- }
139
-
140
227
  const getFormattedStackTrace = () => new Error().stack!.split('\n').slice(3).join('\n');
package/src/meta.ts CHANGED
@@ -3,11 +3,27 @@
3
3
  //
4
4
 
5
5
  /**
6
- * Metadata injected b y the log transform plugin.
6
+ * Marker key + value injected on every {@link CallMetadata} object by the log transform plugin.
7
+ * Used by {@link isLogMeta} to detect a log-meta argument at runtime (e.g. for variadic
8
+ * `param_index: 'last'` callees that don't have a fixed meta slot).
9
+ */
10
+ export const LOG_META_MARKER = '~LogMeta';
11
+
12
+ /**
13
+ * Metadata injected by the log transform plugin.
7
14
  *
8
15
  * Field names are intentionally short to reduce the size of the generated code.
9
16
  */
10
17
  export interface CallMetadata {
18
+ /**
19
+ * Marker tag — when present, equal to {@link LOG_META_MARKER} ({@link `'~LogMeta'`}).
20
+ * Injected by the log transform plugin on every emitted meta object so that {@link isLogMeta}
21
+ * can distinguish a meta argument from a regular user-supplied value at runtime.
22
+ * Optional because hand-written `CallMetadata` literals (decorators, RPC mappers, tests)
23
+ * don't need the marker — they are recognized by position in the call signature.
24
+ */
25
+ '~LogMeta'?: typeof LOG_META_MARKER;
26
+
11
27
  /**
12
28
  * File name.
13
29
  */
@@ -35,3 +51,15 @@ export interface CallMetadata {
35
51
  */
36
52
  A?: string[];
37
53
  }
54
+
55
+ /**
56
+ * Type guard: `true` when `value` is a {@link CallMetadata} object emitted by the log transform plugin.
57
+ * Detection is based on the presence of the {@link LOG_META_MARKER} marker key/value.
58
+ */
59
+ export const isLogMeta = (value: unknown): value is CallMetadata => {
60
+ return (
61
+ value != null &&
62
+ typeof value === 'object' &&
63
+ (value as Record<string, unknown>)[LOG_META_MARKER] === LOG_META_MARKER
64
+ );
65
+ };
package/src/options.ts CHANGED
@@ -7,37 +7,53 @@ import defaultsDeep from 'lodash.defaultsdeep';
7
7
  import { type LogConfig, type LogFilter, LogLevel, type LogOptions, LogProcessorType, levels } from './config';
8
8
  import { type LogProcessor } from './context';
9
9
  import { loadOptions } from './platform';
10
- import { CONSOLE_PROCESSOR, DEBUG_PROCESSOR, BROWSER_PROCESSOR } from './processors';
10
+ import { BROWSER_PROCESSOR, CONSOLE_PROCESSOR, DEBUG_PROCESSOR } from './processors';
11
11
 
12
12
  /**
13
13
  * Processor variants.
14
14
  */
15
- export const processors: { [index: string]: LogProcessor } = {
15
+ export const processors: Record<string, LogProcessor> = {
16
16
  [LogProcessorType.CONSOLE]: CONSOLE_PROCESSOR,
17
17
  [LogProcessorType.BROWSER]: BROWSER_PROCESSOR,
18
18
  [LogProcessorType.DEBUG]: DEBUG_PROCESSOR,
19
19
  };
20
20
 
21
- const IS_BROWSER = typeof window !== 'undefined' || typeof navigator !== 'undefined';
21
+ const browser =
22
+ (typeof window !== 'undefined' || typeof navigator !== 'undefined') &&
23
+ !(typeof process !== 'undefined' && process?.env?.VITEST);
22
24
 
23
- export const DEFAULT_PROCESSORS = [IS_BROWSER ? BROWSER_PROCESSOR : CONSOLE_PROCESSOR];
25
+ export const DEFAULT_PROCESSORS = [browser ? BROWSER_PROCESSOR : CONSOLE_PROCESSOR];
24
26
 
27
+ const parseLogLevel = (level: string, defValue = LogLevel.WARN) => levels[level.toLowerCase()] ?? defValue;
28
+
29
+ /**
30
+ * @internal
31
+ */
25
32
  export const parseFilter = (filter: string | string[] | LogLevel): LogFilter[] => {
26
33
  if (typeof filter === 'number') {
27
34
  return [{ level: filter }];
28
35
  }
29
36
 
30
- const parseLogLevel = (level: string, defValue = LogLevel.WARN) => levels[level.toLowerCase()] ?? defValue;
31
-
32
37
  const lines = typeof filter === 'string' ? filter.split(/,\s*/) : filter;
33
38
  return lines.map((filter) => {
34
39
  const [pattern, level] = filter.split(':');
35
- return level ? { level: parseLogLevel(level), pattern } : { level: parseLogLevel(pattern) };
40
+ return level
41
+ ? {
42
+ level: parseLogLevel(level),
43
+ pattern,
44
+ }
45
+ : {
46
+ level: parseLogLevel(pattern),
47
+ };
36
48
  });
37
49
  };
38
50
 
39
- export const getConfig = (options?: LogOptions): LogConfig => {
40
- const nodeOptions: LogOptions | undefined =
51
+ /**
52
+ * @internal
53
+ */
54
+ export const createConfig = (options?: LogOptions): LogConfig => {
55
+ // Node only.
56
+ const envOptions: LogOptions | undefined =
41
57
  'process' in globalThis
42
58
  ? {
43
59
  file: process!.env.LOG_CONFIG,
@@ -46,12 +62,12 @@ export const getConfig = (options?: LogOptions): LogConfig => {
46
62
  }
47
63
  : undefined;
48
64
 
49
- const mergedOptions: LogOptions = defaultsDeep({}, loadOptions(nodeOptions?.file), nodeOptions, options);
65
+ const mergedOptions: LogOptions = defaultsDeep({}, loadOptions(envOptions?.file), envOptions, options);
50
66
  return {
51
67
  options: mergedOptions,
52
68
  filters: parseFilter(mergedOptions.filter ?? LogLevel.INFO),
53
69
  captureFilters: parseFilter(mergedOptions.captureFilter ?? LogLevel.WARN),
54
- processors: mergedOptions.processor ? [processors[mergedOptions.processor]] : DEFAULT_PROCESSORS,
70
+ processors: mergedOptions.processor ? [processors[mergedOptions.processor]] : [...DEFAULT_PROCESSORS],
55
71
  prefix: mergedOptions.prefix,
56
72
  };
57
73
  };
@@ -2,4 +2,4 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- export * from './node';
5
+ export * from '#platform';
@@ -2,22 +2,10 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { getDebugName, safariCheck } from '@dxos/util';
5
+ import { safariCheck } from '@dxos/util';
6
6
 
7
7
  import { LogLevel } from '../config';
8
- import { getContextFromEntry, type LogProcessor, shouldLog } from '../context';
9
-
10
- const getRelativeFilename = (filename: string) => {
11
- // TODO(burdon): Hack uses "packages" as an anchor (pre-parse NX?)
12
- // Including `packages/` part of the path so that excluded paths (e.g. from dist) are clickable in vscode.
13
- const match = filename.match(/.+\/(packages\/.+\/.+)/);
14
- if (match) {
15
- const [, filePath] = match;
16
- return filePath;
17
- }
18
-
19
- return filename;
20
- };
8
+ import { type LogProcessor, getContextFromEntry, shouldLog } from '../context';
21
9
 
22
10
  type Config = {
23
11
  useTestProcessor: boolean;
@@ -47,31 +35,38 @@ const APP_BROWSER_PROCESSOR: LogProcessor = (config, entry) => {
47
35
  // const LOG_BROWSER_CSS = ['color:gray; font-size:10px; padding-bottom: 4px', 'color:#B97852; font-size:14px;'];
48
36
  const LOG_BROWSER_CSS: string[] = [];
49
37
 
38
+ const { filename, line: lineNumber, context: scopeDebugName } = entry.computedMeta;
39
+
50
40
  let link = '';
51
- if (entry.meta) {
52
- const filename = getRelativeFilename(entry.meta.F);
41
+ if (filename !== undefined && lineNumber !== undefined) {
53
42
  const filepath = `${LOG_BROWSER_PREFIX.replace(/\/$/, '')}/${filename}`;
54
43
  // TODO(burdon): Line numbers not working for app link, even with colons.
55
44
  // https://stackoverflow.com/a/54459820/2804332
56
- link = `${filepath}#L${entry.meta.L}`;
45
+ link = `${filepath}#L${lineNumber}`;
57
46
  }
58
47
 
59
48
  let args = [];
60
49
 
61
- if (entry.meta?.S) {
62
- const scope = entry.meta?.S;
63
- const scopeName = scope.name || getDebugName(scope);
64
- const processPrefix = entry.meta.S?.hostSessionId ? '[worker] ' : '';
65
- // TODO(dmaretskyi): Those can be made clickable with a custom formatter.
66
- args.push(`%c${processPrefix}${scopeName}`, 'color:#C026D3;font-weight:bold');
50
+ const scope = entry.meta?.S;
51
+ if (scope) {
52
+ const scopeName = scope.name || scopeDebugName;
53
+ if (scopeName) {
54
+ const processPrefix = scope.hostSessionId ? '[worker] ' : '';
55
+ // TODO(dmaretskyi): Those can be made clickable with a custom formatter.
56
+ args.push(`%c${processPrefix}${scopeName}`, 'color:#C026D3;font-weight:bold');
57
+ }
67
58
  }
68
59
 
69
- args.push(entry.message);
60
+ if (entry.message) {
61
+ args.push(entry.message);
62
+ }
70
63
 
71
64
  const context = getContextFromEntry(entry);
72
65
  if (context) {
73
66
  if (Object.keys(context).length === 1 && 'error' in context) {
74
- args.push(context.error);
67
+ args.push(unwrapEffectError(context.error));
68
+ } else if (Object.keys(context).length === 1 && 'err' in context) {
69
+ args.push(unwrapEffectError(context.err));
75
70
  } else {
76
71
  args.push(context);
77
72
  }
@@ -110,10 +105,8 @@ const TEST_BROWSER_PROCESSOR: LogProcessor = (config, entry) => {
110
105
  return;
111
106
  }
112
107
 
113
- let path = '';
114
- if (entry.meta) {
115
- path = `${getRelativeFilename(entry.meta.F)}:${entry.meta.L}`;
116
- }
108
+ const { filename, line: lineNumber } = entry.computedMeta;
109
+ const path = filename !== undefined && lineNumber !== undefined ? `${filename}:${lineNumber}` : '';
117
110
 
118
111
  let args = [];
119
112
 
@@ -144,3 +137,13 @@ const TEST_BROWSER_PROCESSOR: LogProcessor = (config, entry) => {
144
137
  };
145
138
 
146
139
  export const BROWSER_PROCESSOR: LogProcessor = CONFIG.useTestProcessor ? TEST_BROWSER_PROCESSOR : APP_BROWSER_PROCESSOR;
140
+
141
+ // effect-specific
142
+ const originalSymbol = Symbol.for('effect/OriginalAnnotation');
143
+
144
+ const unwrapEffectError = (error: any) => {
145
+ if (typeof error === 'object' && error !== null && originalSymbol in error) {
146
+ return error[originalSymbol];
147
+ }
148
+ return error;
149
+ };