@vvlad1973/simple-logger 2.2.0 → 2.3.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.
@@ -92,19 +92,86 @@ export function colorizeLevel(level: LoggerLevel, label: string): string {
92
92
 
93
93
  /**
94
94
  * Prepares the log call by rearranging the provided arguments into a standardized format.
95
+ * For external loggers (like pino), ensures context object comes BEFORE message string.
95
96
  *
96
97
  * @param {unknown[]} args - The array of arguments to prepare for the log call.
97
98
  * @return {PreparedLogCall} The prepared log call arguments, or null if the input is invalid.
98
99
  */
99
100
  export function prepareLogCall(args: unknown[]): PreparedLogCall {
100
- if (typeof args[0] === 'string') {
101
- return [args[0], ...args.slice(1)];
102
- }
101
+ // Case 1: First arg is object (context) - standard pino format
103
102
  if (typeof args[0] === 'object' && args[0] !== null) {
104
103
  const msg = typeof args[1] === 'string' ? args[1] : undefined;
105
104
  const rest = args.slice(msg ? 2 : 1);
106
105
  return [args[0], msg, ...rest];
107
106
  }
107
+
108
+ // Case 2: First arg is string (message)
109
+ if (typeof args[0] === 'string') {
110
+ const msg = args[0];
111
+ // Check if second arg is context object - if so, swap them for pino
112
+ if (typeof args[1] === 'object' && args[1] !== null && !Array.isArray(args[1])) {
113
+ const context = args[1];
114
+ const rest = args.slice(2);
115
+ return [context, msg, ...rest];
116
+ }
117
+ // Otherwise keep as is (message only or message with non-object args)
118
+ return [msg, ...args.slice(1)];
119
+ }
120
+
108
121
  return null;
109
122
  }
110
123
 
124
+ /**
125
+ * Safely wraps a value for logging to ensure it's a proper context object
126
+ *
127
+ * Use this helper ONLY for single values of unknown type that may be:
128
+ * - undefined/null
129
+ * - primitives (string, number, boolean)
130
+ * - Error objects (will be wrapped as an object with err property)
131
+ *
132
+ * DO NOT use for:
133
+ * - Composite objects with multiple fields (they're already safe)
134
+ * - Objects you construct yourself (use them directly)
135
+ *
136
+ * @param value - Value to wrap (can be Error, primitive, undefined, or object)
137
+ * @returns Safe object for logging or undefined if value is nullish
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * // Use for unknown single values
142
+ * catch (error) {
143
+ * logger.error(safeLogContext(error), 'Failed');
144
+ * }
145
+ *
146
+ * catch (reason) {
147
+ * logger.error(safeLogContext(reason), 'Rejected');
148
+ * }
149
+ *
150
+ * // DO NOT use for composite objects
151
+ * logger.error({ err, userId }, 'Failed'); // Direct, no helper needed
152
+ * ```
153
+ */
154
+ export function safeLogContext(value: unknown): Record<string, unknown> {
155
+ // Nullish values - return empty record
156
+ if (value === null || value === undefined) {
157
+ return {} as Record<string, unknown>;
158
+ }
159
+
160
+ // Error objects - wrap in standard "err" field
161
+ if (value instanceof Error) {
162
+ return { err: value };
163
+ }
164
+
165
+ // Already an object (not array) - validate it's a plain object
166
+ if (typeof value === 'object' && !Array.isArray(value)) {
167
+ // Use type guard to safely return the object
168
+ if (isPlainObject(value)) {
169
+ return value;
170
+ }
171
+ // For non-plain objects (class instances, etc), wrap them
172
+ return { value };
173
+ }
174
+
175
+ // Primitives or arrays - wrap in "value" field
176
+ return { value };
177
+ }
package/src/index.ts CHANGED
@@ -4,4 +4,5 @@ export default SimpleLogger;
4
4
  export { SimpleLogger };
5
5
 
6
6
  export type * from './types/simple-logger.types.js';
7
- export * from './constants/constants.js';
7
+ export * from './constants/constants.js';
8
+ export * from './helpers/helpers.js'
package/tsconfig.json CHANGED
@@ -11,7 +11,8 @@
11
11
  "sourceMap": true,
12
12
  "resolveJsonModule": true,
13
13
  "skipLibCheck": true,
14
- "forceConsistentCasingInFileNames": true
14
+ "forceConsistentCasingInFileNames": true,
15
+ "types": ["node", "vitest/globals"]
15
16
  },
16
17
  "include": [
17
18
  "src/**/*"
package/vitest.config.ts CHANGED
@@ -12,6 +12,7 @@ export default defineConfig({
12
12
  include: ['src/**/*.test.ts'],
13
13
  exclude: ['node_modules/**', 'dist/**', 'docs/**'],
14
14
  coverage: {
15
+ enabled: true,
15
16
  provider: 'v8',
16
17
  reporter: ['text', 'json', 'html', 'lcov'],
17
18
  include: ['src/**/*.ts'],
@@ -22,7 +23,6 @@ export default defineConfig({
22
23
  '**/*.test.ts',
23
24
  '**/*.config.ts',
24
25
  '**/*.config.js',
25
- 'src/__test__/**'
26
26
  ],
27
27
  thresholds: {
28
28
  lines: 80,