@mhmdhammoud/meritt-utils 1.5.5 → 1.5.7

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.
@@ -109,4 +109,28 @@ describe('route and format logs', () => {
109
109
  key1: 'val1',
110
110
  }));
111
111
  });
112
+ test('remap reserved elastic field names dynamically', () => {
113
+ //@ts-ignore
114
+ jest.spyOn(pino_1.pino, 'destination').mockReturnValue(PINO_DESTINATION);
115
+ //@ts-ignore
116
+ pino_1.pino.mockReturnValue(PINO);
117
+ const logger = new logger_1.default(LOGGER_NAME);
118
+ logger.info(LOG_EVENT, {
119
+ _id: 'abc123',
120
+ nested: {
121
+ _id: 'nested-1',
122
+ _index: 'bad-index',
123
+ },
124
+ });
125
+ expect(PINO.info).toHaveBeenCalledWith(expect.objectContaining({
126
+ mongo_id: 'abc123',
127
+ nested: expect.objectContaining({
128
+ mongo_id: 'nested-1',
129
+ es_index: 'bad-index',
130
+ }),
131
+ }));
132
+ expect(PINO.info).not.toHaveBeenCalledWith(expect.objectContaining({
133
+ _id: expect.anything(),
134
+ }));
135
+ });
112
136
  });
@@ -56,7 +56,7 @@ function initializeBulkHandler(opts, client, splitter) {
56
56
  datasource: splitter,
57
57
  flushBytes: (_e = (_d = opts.flushBytes) !== null && _d !== void 0 ? _d : opts['flush-bytes']) !== null && _e !== void 0 ? _e : 1000,
58
58
  flushInterval: (_g = (_f = opts.flushInterval) !== null && _f !== void 0 ? _f : opts['flush-interval']) !== null && _g !== void 0 ? _g : 3000,
59
- refreshOnCompletion: indexName(),
59
+ refreshOnCompletion: false,
60
60
  onDocument(doc) {
61
61
  var _a, _b;
62
62
  const d = doc;
@@ -42,12 +42,107 @@ const trace_store_1 = require("./trace-store");
42
42
  dotenv.config();
43
43
  /** Convert camelCase to snake_case for Kibana/ECS-friendly field names */
44
44
  const toSnakeCase = (str) => str.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
45
+ /**
46
+ * Elasticsearch reserves metadata fields (e.g. `_id`) and rejects documents
47
+ * containing them in payload body. Remap them to safe application fields.
48
+ */
49
+ const RESERVED_ES_FIELD_ALIASES = {
50
+ _id: 'mongo_id',
51
+ _index: 'es_index',
52
+ _type: 'es_type',
53
+ _score: 'es_score',
54
+ _source: 'es_source',
55
+ _routing: 'es_routing',
56
+ _seq_no: 'es_seq_no',
57
+ _primary_term: 'es_primary_term',
58
+ _version: 'es_version',
59
+ };
60
+ /** Convert arbitrary field names into ES-safe flattened keys */
61
+ const toSafeElasticFieldName = (key) => {
62
+ const normalized = toSnakeCase(key);
63
+ const mapped = RESERVED_ES_FIELD_ALIASES[normalized];
64
+ if (mapped) {
65
+ return mapped;
66
+ }
67
+ // Any leading underscore can conflict with ES internals, remap defensively.
68
+ return normalized.startsWith('_') ? `meta${normalized}` : normalized;
69
+ };
70
+ const isObjectIdLike = (v) => v !== null &&
71
+ typeof v === 'object' &&
72
+ 'toHexString' in v &&
73
+ typeof v.toHexString === 'function';
45
74
  /** Check if value is a plain object (not Error, Date, Array, null) */
46
75
  const isPlainObject = (v) => v !== null &&
47
76
  typeof v === 'object' &&
48
77
  !Array.isArray(v) &&
49
78
  !(v instanceof Error) &&
50
79
  !(v instanceof Date);
80
+ /**
81
+ * Recursively sanitize log values for Elasticsearch safety.
82
+ * - Remaps reserved key names (e.g. `_id` -> `mongo_id`)
83
+ * - Converts Error to a stable serializable shape
84
+ * - Converts ObjectId-like objects to hex strings
85
+ * - Prevents circular structure failures
86
+ */
87
+ const sanitizeForElastic = (value, seen = new WeakSet()) => {
88
+ if (value === null ||
89
+ typeof value === 'string' ||
90
+ typeof value === 'number' ||
91
+ typeof value === 'boolean') {
92
+ return value;
93
+ }
94
+ if (value instanceof Date) {
95
+ return value.toISOString();
96
+ }
97
+ if (value instanceof Error) {
98
+ return { message: value.message, type: value.constructor.name };
99
+ }
100
+ if (isObjectIdLike(value)) {
101
+ return value.toHexString();
102
+ }
103
+ if (Array.isArray(value)) {
104
+ return value.map((item) => sanitizeForElastic(item, seen));
105
+ }
106
+ if (isPlainObject(value)) {
107
+ if (seen.has(value)) {
108
+ return '[Circular]';
109
+ }
110
+ seen.add(value);
111
+ const sanitizedObject = {};
112
+ for (const [k, v] of Object.entries(value)) {
113
+ sanitizedObject[toSafeElasticFieldName(k)] = sanitizeForElastic(v, seen);
114
+ }
115
+ return sanitizedObject;
116
+ }
117
+ // Fallback for class instances and non-plain objects.
118
+ return String(value);
119
+ };
120
+ /**
121
+ * Captures the call site from the stack when log event is missing.
122
+ * Returns file:line (e.g. product.service.ts:2266) for Kibana/Elasticsearch filtering.
123
+ */
124
+ const getCallSiteForMissingLog = () => {
125
+ var _a;
126
+ try {
127
+ const stack = (_a = new Error().stack) !== null && _a !== void 0 ? _a : '';
128
+ const lines = stack.split('\n');
129
+ // First frame outside Logger / node_modules / meritt-utils
130
+ const appFrame = lines.find((line) => !line.includes('node_modules') &&
131
+ !line.includes('meritt-utils') &&
132
+ !line.includes('Logger.'));
133
+ if (!appFrame)
134
+ return undefined;
135
+ // Extract file:line e.g. "product.service.ts:2266"
136
+ const match = appFrame.match(/([^/\\]+\.(?:ts|js|tsx|jsx)):(\d+)/);
137
+ if (match) {
138
+ return `${match[1]}:${match[2]}`;
139
+ }
140
+ return appFrame.trim().slice(0, 100);
141
+ }
142
+ catch (_b) {
143
+ return undefined;
144
+ }
145
+ };
51
146
  /**
52
147
  * Pino logger backend - singleton
53
148
  */
@@ -208,6 +303,13 @@ function getLogger(elasticConfig) {
208
303
  esTransport.on('insertError', (err) => {
209
304
  console.error('[Logger] Elasticsearch insert error:', err.message);
210
305
  console.error('[Logger] Some logs failed to index to Elasticsearch.');
306
+ if (err.document) {
307
+ const docStr = JSON.stringify(err.document);
308
+ const preview = docStr.length > 500
309
+ ? `${docStr.substring(0, 500)}... (truncated)`
310
+ : docStr;
311
+ console.error('[Logger] Dropped document preview:', preview);
312
+ }
211
313
  });
212
314
  // Log successful connection (for debugging)
213
315
  esTransport.on('insert', () => {
@@ -259,35 +361,36 @@ class Logger {
259
361
  buildPayload(logLevel, logEvent, args) {
260
362
  var _a, _b;
261
363
  const isLocal = process.env.NODE_ENV === 'local' || process.env.NODE_ENV === 'test';
262
- // ECS-aligned fields for Kibana Discover, Lens, alerts
364
+ // Defensive: missing Logs constant (undefined) crashes on logEvent.code
365
+ const useFallback = !logEvent || typeof logEvent !== 'object' || !('code' in logEvent);
366
+ const event = useFallback
367
+ ? { code: 'UNKNOWN', msg: 'Missing or invalid log event constant' }
368
+ : logEvent;
369
+ // ECS-aligned fields for Kibana (flat names to avoid mapping conflicts with existing indices)
263
370
  const ecs = {
264
- 'log.level': logLevel,
265
- 'log.logger': this._name,
266
- 'event.code': logEvent.code,
267
- message: logEvent.msg,
268
- service: {
269
- name: (_a = process.env.SERVER_NICKNAME) !== null && _a !== void 0 ? _a : 'unknown',
270
- environment: (_b = process.env.NODE_ENV) !== null && _b !== void 0 ? _b : 'development',
271
- },
272
- host: {
273
- name: (0, os_1.hostname)(),
274
- },
371
+ log_level: logLevel,
372
+ log_logger: this._name,
373
+ event_code: event.code,
374
+ message: event.msg,
375
+ service_name: (_a = process.env.SERVER_NICKNAME) !== null && _a !== void 0 ? _a : 'unknown',
376
+ service_environment: (_b = process.env.NODE_ENV) !== null && _b !== void 0 ? _b : 'development',
377
+ host_name: (0, os_1.hostname)(),
275
378
  };
276
379
  // Trace context for request-scoped correlation
277
380
  const trace = (0, trace_store_1.getTraceContext)();
278
381
  if (trace) {
279
- ;
280
- ecs.trace = { id: trace.traceId };
382
+ ecs.trace_id = trace.traceId;
281
383
  }
282
384
  // Structured context: flatten single plain object as top-level fields
385
+ // Sanitize values to avoid ES mapping conflicts (e.g. Error objects → serializable shape)
283
386
  let detail;
284
387
  if (args.length === 1 &&
285
388
  isPlainObject(args[0]) &&
286
389
  Object.keys(args[0]).length > 0) {
287
390
  const obj = args[0];
288
391
  for (const [k, v] of Object.entries(obj)) {
289
- const key = toSnakeCase(k);
290
- ecs[key] = v;
392
+ const key = toSafeElasticFieldName(k);
393
+ ecs[key] = sanitizeForElastic(v);
291
394
  }
292
395
  }
293
396
  else {
@@ -297,12 +400,19 @@ class Logger {
297
400
  const base = {
298
401
  ...ecs,
299
402
  component: this._name,
300
- code: logEvent.code,
301
- msg: logEvent.msg,
403
+ code: event.code,
404
+ msg: event.msg,
302
405
  };
303
406
  if (detail !== undefined) {
304
407
  base.detail = detail;
305
408
  }
409
+ // When fallback used: add call site for Kibana/Elasticsearch querying
410
+ if (useFallback) {
411
+ const callSite = getCallSiteForMissingLog();
412
+ if (callSite) {
413
+ base.missing_log_call_site = callSite;
414
+ }
415
+ }
306
416
  return base;
307
417
  }
308
418
  log(logLevel, logEvent, ...args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mhmdhammoud/meritt-utils",
3
- "version": "1.5.5",
3
+ "version": "1.5.7",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "private": false,
@@ -89,4 +89,35 @@ describe('route and format logs', () => {
89
89
  })
90
90
  )
91
91
  })
92
+
93
+ test('remap reserved elastic field names dynamically', () => {
94
+ //@ts-ignore
95
+ jest.spyOn(pino, 'destination').mockReturnValue(PINO_DESTINATION)
96
+ //@ts-ignore
97
+ pino.mockReturnValue(PINO)
98
+ const logger = new Logger(LOGGER_NAME)
99
+
100
+ logger.info(LOG_EVENT, {
101
+ _id: 'abc123',
102
+ nested: {
103
+ _id: 'nested-1',
104
+ _index: 'bad-index',
105
+ },
106
+ })
107
+
108
+ expect(PINO.info).toHaveBeenCalledWith(
109
+ expect.objectContaining({
110
+ mongo_id: 'abc123',
111
+ nested: expect.objectContaining({
112
+ mongo_id: 'nested-1',
113
+ es_index: 'bad-index',
114
+ }),
115
+ })
116
+ )
117
+ expect(PINO.info).not.toHaveBeenCalledWith(
118
+ expect.objectContaining({
119
+ _id: expect.anything(),
120
+ })
121
+ )
122
+ })
92
123
  })
@@ -107,7 +107,7 @@ function initializeBulkHandler(
107
107
  datasource: splitter as unknown as Readable,
108
108
  flushBytes: opts.flushBytes ?? opts['flush-bytes'] ?? 1000,
109
109
  flushInterval: opts.flushInterval ?? opts['flush-interval'] ?? 3000,
110
- refreshOnCompletion: indexName(),
110
+ refreshOnCompletion: false,
111
111
  onDocument(doc: unknown) {
112
112
  const d = doc as LogDocument
113
113
  const date = d.time ?? d['@timestamp'] ?? new Date().toISOString()
package/src/lib/logger.ts CHANGED
@@ -1,9 +1,9 @@
1
- import {Logger as PinoLogger, pino, stdTimeFunctions} from 'pino'
1
+ import { Logger as PinoLogger, pino, stdTimeFunctions } from 'pino'
2
2
  import * as dotenv from 'dotenv'
3
- import {hostname} from 'os'
4
- import {createElasticTransport} from './elastic-transport'
5
- import {getTraceContext} from './trace-store'
6
- import {LOG_LEVEL, LogEvent, ElasticConfig} from '../types'
3
+ import { hostname } from 'os'
4
+ import { createElasticTransport } from './elastic-transport'
5
+ import { getTraceContext } from './trace-store'
6
+ import { LOG_LEVEL, LogEvent, ElasticConfig } from '../types'
7
7
 
8
8
  dotenv.config()
9
9
 
@@ -11,6 +11,39 @@ dotenv.config()
11
11
  const toSnakeCase = (str: string): string =>
12
12
  str.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)
13
13
 
14
+ /**
15
+ * Elasticsearch reserves metadata fields (e.g. `_id`) and rejects documents
16
+ * containing them in payload body. Remap them to safe application fields.
17
+ */
18
+ const RESERVED_ES_FIELD_ALIASES: Record<string, string> = {
19
+ _id: 'mongo_id',
20
+ _index: 'es_index',
21
+ _type: 'es_type',
22
+ _score: 'es_score',
23
+ _source: 'es_source',
24
+ _routing: 'es_routing',
25
+ _seq_no: 'es_seq_no',
26
+ _primary_term: 'es_primary_term',
27
+ _version: 'es_version',
28
+ }
29
+
30
+ /** Convert arbitrary field names into ES-safe flattened keys */
31
+ const toSafeElasticFieldName = (key: string): string => {
32
+ const normalized = toSnakeCase(key)
33
+ const mapped = RESERVED_ES_FIELD_ALIASES[normalized]
34
+ if (mapped) {
35
+ return mapped
36
+ }
37
+ // Any leading underscore can conflict with ES internals, remap defensively.
38
+ return normalized.startsWith('_') ? `meta${normalized}` : normalized
39
+ }
40
+
41
+ const isObjectIdLike = (v: unknown): v is { toHexString: () => string } =>
42
+ v !== null &&
43
+ typeof v === 'object' &&
44
+ 'toHexString' in v &&
45
+ typeof (v as { toHexString?: unknown }).toHexString === 'function'
46
+
14
47
  /** Check if value is a plain object (not Error, Date, Array, null) */
15
48
  const isPlainObject = (v: unknown): v is Record<string, unknown> =>
16
49
  v !== null &&
@@ -19,6 +52,86 @@ const isPlainObject = (v: unknown): v is Record<string, unknown> =>
19
52
  !(v instanceof Error) &&
20
53
  !(v instanceof Date)
21
54
 
55
+ /**
56
+ * Recursively sanitize log values for Elasticsearch safety.
57
+ * - Remaps reserved key names (e.g. `_id` -> `mongo_id`)
58
+ * - Converts Error to a stable serializable shape
59
+ * - Converts ObjectId-like objects to hex strings
60
+ * - Prevents circular structure failures
61
+ */
62
+ const sanitizeForElastic = (
63
+ value: unknown,
64
+ seen: WeakSet<object> = new WeakSet<object>()
65
+ ): unknown => {
66
+ if (
67
+ value === null ||
68
+ typeof value === 'string' ||
69
+ typeof value === 'number' ||
70
+ typeof value === 'boolean'
71
+ ) {
72
+ return value
73
+ }
74
+
75
+ if (value instanceof Date) {
76
+ return value.toISOString()
77
+ }
78
+
79
+ if (value instanceof Error) {
80
+ return { message: value.message, type: value.constructor.name }
81
+ }
82
+
83
+ if (isObjectIdLike(value)) {
84
+ return value.toHexString()
85
+ }
86
+
87
+ if (Array.isArray(value)) {
88
+ return value.map((item) => sanitizeForElastic(item, seen))
89
+ }
90
+
91
+ if (isPlainObject(value)) {
92
+ if (seen.has(value)) {
93
+ return '[Circular]'
94
+ }
95
+ seen.add(value)
96
+
97
+ const sanitizedObject: Record<string, unknown> = {}
98
+ for (const [k, v] of Object.entries(value)) {
99
+ sanitizedObject[toSafeElasticFieldName(k)] = sanitizeForElastic(v, seen)
100
+ }
101
+ return sanitizedObject
102
+ }
103
+
104
+ // Fallback for class instances and non-plain objects.
105
+ return String(value)
106
+ }
107
+
108
+ /**
109
+ * Captures the call site from the stack when log event is missing.
110
+ * Returns file:line (e.g. product.service.ts:2266) for Kibana/Elasticsearch filtering.
111
+ */
112
+ const getCallSiteForMissingLog = (): string | undefined => {
113
+ try {
114
+ const stack = new Error().stack ?? ''
115
+ const lines = stack.split('\n')
116
+ // First frame outside Logger / node_modules / meritt-utils
117
+ const appFrame = lines.find(
118
+ (line) =>
119
+ !line.includes('node_modules') &&
120
+ !line.includes('meritt-utils') &&
121
+ !line.includes('Logger.')
122
+ )
123
+ if (!appFrame) return undefined
124
+ // Extract file:line e.g. "product.service.ts:2266"
125
+ const match = appFrame.match(/([^/\\]+\.(?:ts|js|tsx|jsx)):(\d+)/)
126
+ if (match) {
127
+ return `${match[1]}:${match[2]}`
128
+ }
129
+ return appFrame.trim().slice(0, 100)
130
+ } catch {
131
+ return undefined
132
+ }
133
+ }
134
+
22
135
  /**
23
136
  * Pino logger backend - singleton
24
137
  */
@@ -228,9 +341,17 @@ function getLogger(elasticConfig?: ElasticConfig): PinoLogger {
228
341
  })
229
342
 
230
343
  // Handle insert errors (document indexing failures)
231
- esTransport.on('insertError', (err: Error) => {
344
+ esTransport.on('insertError', (err: Error & { document?: unknown }) => {
232
345
  console.error('[Logger] Elasticsearch insert error:', err.message)
233
346
  console.error('[Logger] Some logs failed to index to Elasticsearch.')
347
+ if (err.document) {
348
+ const docStr = JSON.stringify(err.document)
349
+ const preview =
350
+ docStr.length > 500
351
+ ? `${docStr.substring(0, 500)}... (truncated)`
352
+ : docStr
353
+ console.error('[Logger] Dropped document preview:', preview)
354
+ }
234
355
  })
235
356
 
236
357
  // Log successful connection (for debugging)
@@ -308,42 +429,50 @@ class Logger {
308
429
  * - Structured: Single plain object flattened as top-level snake_case fields (Kibana filterable)
309
430
  * - Trace: trace.id when running inside runWithTrace
310
431
  */
311
- private buildPayload(logLevel: LOG_LEVEL, logEvent: LogEvent, args: unknown[]) {
432
+ private buildPayload(
433
+ logLevel: LOG_LEVEL,
434
+ logEvent: LogEvent,
435
+ args: unknown[]
436
+ ) {
312
437
  const isLocal =
313
438
  process.env.NODE_ENV === 'local' || process.env.NODE_ENV === 'test'
314
439
 
315
- // ECS-aligned fields for Kibana Discover, Lens, alerts
440
+ // Defensive: missing Logs constant (undefined) crashes on logEvent.code
441
+ const useFallback =
442
+ !logEvent || typeof logEvent !== 'object' || !('code' in logEvent)
443
+ const event = useFallback
444
+ ? { code: 'UNKNOWN', msg: 'Missing or invalid log event constant' }
445
+ : logEvent
446
+
447
+ // ECS-aligned fields for Kibana (flat names to avoid mapping conflicts with existing indices)
316
448
  const ecs: Record<string, unknown> = {
317
- 'log.level': logLevel,
318
- 'log.logger': this._name,
319
- 'event.code': logEvent.code,
320
- message: logEvent.msg,
321
- service: {
322
- name: process.env.SERVER_NICKNAME ?? 'unknown',
323
- environment: process.env.NODE_ENV ?? 'development',
324
- },
325
- host: {
326
- name: hostname(),
327
- },
449
+ log_level: logLevel,
450
+ log_logger: this._name,
451
+ event_code: event.code,
452
+ message: event.msg,
453
+ service_name: process.env.SERVER_NICKNAME ?? 'unknown',
454
+ service_environment: process.env.NODE_ENV ?? 'development',
455
+ host_name: hostname(),
328
456
  }
329
457
 
330
458
  // Trace context for request-scoped correlation
331
459
  const trace = getTraceContext()
332
460
  if (trace) {
333
- ;(ecs as Record<string, unknown>).trace = {id: trace.traceId}
461
+ ecs.trace_id = trace.traceId
334
462
  }
335
463
 
336
464
  // Structured context: flatten single plain object as top-level fields
465
+ // Sanitize values to avoid ES mapping conflicts (e.g. Error objects → serializable shape)
337
466
  let detail: unknown
338
467
  if (
339
468
  args.length === 1 &&
340
469
  isPlainObject(args[0]) &&
341
470
  Object.keys(args[0]).length > 0
342
471
  ) {
343
- const obj = args[0] as Record<string, unknown>
472
+ const obj = args[0]
344
473
  for (const [k, v] of Object.entries(obj)) {
345
- const key = toSnakeCase(k)
346
- ;(ecs as Record<string, unknown>)[key] = v
474
+ const key = toSafeElasticFieldName(k)
475
+ ecs[key] = sanitizeForElastic(v)
347
476
  }
348
477
  } else {
349
478
  detail = isLocal ? args : JSON.stringify(args)
@@ -353,12 +482,19 @@ class Logger {
353
482
  const base: Record<string, unknown> = {
354
483
  ...ecs,
355
484
  component: this._name,
356
- code: logEvent.code,
357
- msg: logEvent.msg,
485
+ code: event.code,
486
+ msg: event.msg,
358
487
  }
359
488
  if (detail !== undefined) {
360
489
  base.detail = detail
361
490
  }
491
+ // When fallback used: add call site for Kibana/Elasticsearch querying
492
+ if (useFallback) {
493
+ const callSite = getCallSiteForMissingLog()
494
+ if (callSite) {
495
+ base.missing_log_call_site = callSite
496
+ }
497
+ }
362
498
  return base
363
499
  }
364
500
 
@@ -431,7 +567,7 @@ class Logger {
431
567
  const result = await fn()
432
568
  const durationMs = Date.now() - start
433
569
  const payload = this.buildPayload('info', logEvent, [
434
- {...context, duration_ms: durationMs, success: true},
570
+ { ...context, duration_ms: durationMs, success: true },
435
571
  ])
436
572
  this._logger.info(payload)
437
573
  return result
@@ -439,10 +575,10 @@ class Logger {
439
575
  const durationMs = Date.now() - start
440
576
  const errObj =
441
577
  error instanceof Error
442
- ? {error_message: error.message, error_type: error.constructor.name}
578
+ ? { error_message: error.message, error_type: error.constructor.name }
443
579
  : {}
444
580
  const payload = this.buildPayload('error', logEvent, [
445
- {...context, ...errObj, duration_ms: durationMs, success: false},
581
+ { ...context, ...errObj, duration_ms: durationMs, success: false },
446
582
  ])
447
583
  this._logger.error(payload)
448
584
  throw error
@@ -1,5 +1,5 @@
1
- import {AsyncLocalStorage} from 'async_hooks'
2
- import {randomUUID} from 'crypto'
1
+ import { AsyncLocalStorage } from 'async_hooks'
2
+ import { randomUUID } from 'crypto'
3
3
 
4
4
  /**
5
5
  * Trace context for request-scoped correlation in Kibana.
@@ -27,7 +27,7 @@ export const runWithTrace = async <T>(
27
27
  traceId?: string
28
28
  ): Promise<T> => {
29
29
  const id = traceId ?? randomUUID()
30
- return traceStorage.run({traceId: id}, () => fn())
30
+ return traceStorage.run({ traceId: id }, () => fn())
31
31
  }
32
32
 
33
33
  /**
@@ -35,7 +35,7 @@ export const runWithTrace = async <T>(
35
35
  */
36
36
  export const runWithTraceSync = <T>(fn: () => T, traceId?: string): T => {
37
37
  const id = traceId ?? randomUUID()
38
- return traceStorage.run({traceId: id}, () => fn())
38
+ return traceStorage.run({ traceId: id }, () => fn())
39
39
  }
40
40
 
41
41
  /**