@pencroff-lab/kore 0.1.2 → 0.2.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/docs/format_dt.md CHANGED
@@ -25,27 +25,27 @@ interface DtStampOptions {
25
25
  ms?: boolean;
26
26
  tz?: "utc" | "local";
27
27
  parts?: "datetime" | "date" | "time";
28
- compact?: boolean;
28
+ readable?: boolean;
29
29
  }
30
30
  ```
31
31
 
32
32
  | Option | Type | Default | Description |
33
33
  |-------------|-----------------------------------|--------------|-----------------------------------------------------------------------------|
34
- | `delimiter` | `string` | `"_"` | Character(s) between date/time segments. Ignored when `compact` is `true`. |
34
+ | `delimiter` | `string` | `"_"` | Character(s) between date/time segments. |
35
35
  | `ms` | `boolean` | `false` | Include milliseconds in the time portion. |
36
36
  | `tz` | `"utc" \| "local"` | `"utc"` | Timezone for extracting date/time components. |
37
37
  | `parts` | `"datetime" \| "date" \| "time"` | `"datetime"` | Which parts of the stamp to include. |
38
- | `compact` | `boolean` | `false` | When `true`, omits the delimiter entirely (equivalent to `delimiter: ""`). |
38
+ | `readable` | `boolean` | `false` | When `true`, formats with human-readable separators. |
39
39
 
40
40
  ### Option Details
41
41
 
42
42
  #### `delimiter`
43
43
 
44
- Character(s) placed between date and time segments, and between time and milliseconds when `ms` is `true`. Ignored when `compact` is `true`.
44
+ Character(s) placed between date and time segments, and between time and milliseconds when `ms` is `true`.
45
45
 
46
46
  #### `ms`
47
47
 
48
- When `true`, appends milliseconds to the time portion, separated by the delimiter.
48
+ When `true`, appends milliseconds to the time portion, separated by the delimiter (or `.` in readable time-only mode).
49
49
 
50
50
  #### `tz`
51
51
 
@@ -59,9 +59,13 @@ Controls which parts of the timestamp are included:
59
59
  - `"date"` -- date only: `YYYYMMDD`
60
60
  - `"time"` -- time only: `HHmmss` or `HHmmss_SSS` with `ms`
61
61
 
62
- #### `compact`
62
+ #### `readable`
63
63
 
64
- When `true`, omits the delimiter entirely. Equivalent to setting `delimiter: ""`.
64
+ When `true`, formats with human-readable separators:
65
+ - Dashes in date: `YYYY-MM-DD`
66
+ - Colons in time: `HH:MM:SS`
67
+ - `.` before milliseconds in time-only mode: `HH:MM:SS.mmm`
68
+ - Delimiter still used between date/time and between time/milliseconds in datetime mode
65
69
 
66
70
  ## Examples
67
71
 
@@ -74,11 +78,25 @@ dtStamp(new Date("2024-03-15T10:30:45.123Z"));
74
78
  // "20240315_103045"
75
79
  ```
76
80
 
77
- ### Compact with milliseconds
81
+ ### Readable datetime with milliseconds
78
82
 
79
83
  ```typescript
80
- dtStamp(new Date("2024-03-15T10:30:45.123Z"), { compact: true, ms: true });
81
- // "20240315103045123"
84
+ dtStamp(new Date("2024-03-15T10:30:45.123Z"), { readable: true, ms: true });
85
+ // "2024-03-15_10:30:45_123"
86
+ ```
87
+
88
+ ### Readable date only
89
+
90
+ ```typescript
91
+ dtStamp(new Date("2024-03-15T10:30:45.123Z"), { readable: true, parts: "date" });
92
+ // "2024-03-15"
93
+ ```
94
+
95
+ ### Readable time with milliseconds
96
+
97
+ ```typescript
98
+ dtStamp(new Date("2024-03-15T10:30:45.123Z"), { readable: true, parts: "time", ms: true });
99
+ // "10:30:45.123"
82
100
  ```
83
101
 
84
102
  ### Date only
@@ -125,15 +143,17 @@ dtStamp(new Date("2024-03-15T10:30:45.123Z"), { tz: "local" });
125
143
 
126
144
  ## Output Format Reference
127
145
 
128
- | Options | Output |
129
- |----------------------------------------|---------------------|
130
- | `{}` | `20240315_103045` |
131
- | `{ ms: true }` | `20240315_103045_123` |
132
- | `{ compact: true }` | `20240315103045` |
133
- | `{ compact: true, ms: true }` | `20240315103045123` |
134
- | `{ parts: "date" }` | `20240315` |
135
- | `{ parts: "time" }` | `103045` |
136
- | `{ parts: "time", ms: true }` | `103045_123` |
137
- | `{ delimiter: "-" }` | `20240315-103045` |
138
- | `{ delimiter: "-", ms: true }` | `20240315-103045-123` |
139
- | `{ parts: "time", compact: true, ms: true }` | `103045123` |
146
+ | Options | Output |
147
+ |-----------------------------------------------------|-------------------------|
148
+ | `{}` | `20240315_103045` |
149
+ | `{ ms: true }` | `20240315_103045_123` |
150
+ | `{ parts: "date" }` | `20240315` |
151
+ | `{ parts: "time" }` | `103045` |
152
+ | `{ parts: "time", ms: true }` | `103045_123` |
153
+ | `{ delimiter: "-" }` | `20240315-103045` |
154
+ | `{ delimiter: "-", ms: true }` | `20240315-103045-123` |
155
+ | `{ readable: true }` | `2024-03-15_10:30:45` |
156
+ | `{ readable: true, ms: true }` | `2024-03-15_10:30:45_123` |
157
+ | `{ readable: true, parts: "date" }` | `2024-03-15` |
158
+ | `{ readable: true, parts: "time" }` | `10:30:45` |
159
+ | `{ readable: true, parts: "time", ms: true }` | `10:30:45.123` |
package/docs/logger.md ADDED
@@ -0,0 +1,342 @@
1
+ # Logger
2
+
3
+ Structured logging utility with transport dependency injection and Err integration.
4
+
5
+ The logger is a callable function with overloaded signatures and level constants attached as properties. Transports are injectable, making the logger testable without streams or process-level side effects. The built-in pretty transport renders to stderr with ANSI colors and automatic `Err` formatting.
6
+
7
+ For usage patterns, DI conventions, and error logging strategy, see the [Logging Guide](logging_guide.md).
8
+
9
+ ## Exports
10
+
11
+ ```typescript
12
+ import { log, createLogger, prettyTransport, lvl } from "@pencroff-lab/kore";
13
+ import type {
14
+ Logger,
15
+ LevelValue,
16
+ LogEntry,
17
+ LogTransport,
18
+ LoggerOptions,
19
+ PrettyOptions,
20
+ } from "@pencroff-lab/kore";
21
+ ```
22
+
23
+ ## Types
24
+
25
+ ### `LevelValue`
26
+
27
+ ```typescript
28
+ type LevelValue = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
29
+ ```
30
+
31
+ Union type of valid log level strings.
32
+
33
+ ### `LogEntry`
34
+
35
+ A single structured log entry passed to transports.
36
+
37
+ ```typescript
38
+ interface LogEntry {
39
+ level: LevelValue;
40
+ timestamp: number;
41
+ message: string;
42
+ context: Record<string, unknown>;
43
+ modules: string[];
44
+ }
45
+ ```
46
+
47
+ | Property | Type | Description |
48
+ |-------------|----------------------------|--------------------------------------------------|
49
+ | `level` | `LevelValue` | Log level of this entry. |
50
+ | `timestamp` | `number` | Unix timestamp in milliseconds (`Date.now()`). |
51
+ | `message` | `string` | Log message. |
52
+ | `context` | `Record<string, unknown>` | Merged bindings and call-site context. |
53
+ | `modules` | `string[]` | Module chain accumulated by `child()` calls. |
54
+
55
+ ### `LogTransport`
56
+
57
+ Transport interface for log output backends. Implement this to integrate any logging backend (file, remote service, test spy, etc.).
58
+
59
+ ```typescript
60
+ interface LogTransport {
61
+ write(entry: LogEntry): void;
62
+ }
63
+ ```
64
+
65
+ #### Example: Pino transport
66
+
67
+ ```typescript
68
+ import pino from "pino";
69
+ import type { LogTransport, LogEntry } from "@pencroff-lab/kore";
70
+
71
+ const pinoInstance = pino();
72
+ const pinoTransport: LogTransport = {
73
+ write(entry: LogEntry) {
74
+ const prefix = entry.modules.map((m) => `[${m}] `).join("");
75
+ pinoInstance[entry.level](entry.context, prefix + entry.message);
76
+ },
77
+ };
78
+ ```
79
+
80
+ #### Example: Test spy transport
81
+
82
+ ```typescript
83
+ import type { LogEntry, LogTransport } from "@pencroff-lab/kore";
84
+
85
+ const entries: LogEntry[] = [];
86
+ const spy: LogTransport = { write(e) { entries.push(e); } };
87
+ ```
88
+
89
+ ### `PrettyOptions`
90
+
91
+ Options for the built-in pretty console transport.
92
+
93
+ ```typescript
94
+ interface PrettyOptions {
95
+ output?: { write(data: string): void };
96
+ colors?: boolean | "auto";
97
+ levelColors?: Partial<Record<LevelValue, string>>;
98
+ timestamp?: "short" | "iso" | ((ts: number) => string);
99
+ }
100
+ ```
101
+
102
+ | Option | Type | Default | Description |
103
+ |---------------|-----------------------------------------------|------------------|----------------------------------------------------------|
104
+ | `output` | `{ write(data: string): void }` | `process.stderr` | Output stream. |
105
+ | `colors` | `boolean \| "auto"` | `"auto"` | ANSI colors. `"auto"` enables when output is a TTY. |
106
+ | `levelColors` | `Partial<Record<LevelValue, string>>` | (built-in) | Override ANSI escape sequences per level. |
107
+ | `timestamp` | `"short" \| "iso" \| ((ts: number) => string)` | `"short"` | Timestamp format. `"short"` is `HH:MM:SS.mmm` local time. |
108
+
109
+ ### `LoggerOptions`
110
+
111
+ Options for `createLogger`.
112
+
113
+ ```typescript
114
+ interface LoggerOptions {
115
+ level?: LevelValue;
116
+ transports?: LogTransport[];
117
+ }
118
+ ```
119
+
120
+ | Option | Type | Default | Description |
121
+ |--------------|------------------|---------------------------------------|--------------------------------------|
122
+ | `level` | `LevelValue` | `LOG_LEVEL` env or `"info"` | Minimum log level. |
123
+ | `transports` | `LogTransport[]` | `[prettyTransport()]` | Transports to write entries to. |
124
+
125
+ ### `Logger`
126
+
127
+ Callable logger interface. The Logger is both a function (for logging) and an object (with level constants and the `child` method).
128
+
129
+ ```typescript
130
+ interface Logger {
131
+ readonly TRACE: "trace";
132
+ readonly DEBUG: "debug";
133
+ readonly INFO: "info";
134
+ readonly WARN: "warn";
135
+ readonly ERROR: "error";
136
+ readonly FATAL: "fatal";
137
+
138
+ (message: string): void;
139
+ (message: string, context: object | Err): void;
140
+ (message: string, detail: string): void;
141
+ (level: LevelValue, message: string): void;
142
+ (level: LevelValue, message: string, context: object | Err): void;
143
+
144
+ child(module: string, bindings?: object): Logger;
145
+ }
146
+ ```
147
+
148
+ #### Call Signatures
149
+
150
+ | Signature | Level | Description |
151
+ |----------------------------------------|--------|-------------------------------------|
152
+ | `log(message)` | INFO | Log at default INFO level. |
153
+ | `log(message, context)` | INFO | INFO with context object or Err. |
154
+ | `log(message, detail)` | INFO | INFO with detail string. |
155
+ | `log(level, message)` | given | Log at specific level. |
156
+ | `log(level, message, context)` | given | Specific level with context or Err. |
157
+
158
+ Context handling:
159
+ - `Err` instances are stored as `{ err: <Err> }` in `context`
160
+ - Plain strings are stored as `{ detail: <string> }` in `context`
161
+ - Objects are used as-is
162
+
163
+ ## `lvl`
164
+
165
+ Level constants object for use with `createLogger` options.
166
+
167
+ ```typescript
168
+ const lvl = {
169
+ TRACE: "trace",
170
+ DEBUG: "debug",
171
+ INFO: "info",
172
+ WARN: "warn",
173
+ ERROR: "error",
174
+ FATAL: "fatal",
175
+ } as const;
176
+ ```
177
+
178
+ **Level hierarchy** (lowest to highest):
179
+
180
+ | Numeric | Level | Tag | Description |
181
+ |---------|---------|-------|--------------------------------------|
182
+ | 0 | `trace` | `TRC` | Detailed debugging information |
183
+ | 1 | `debug` | `DBG` | Debugging information |
184
+ | 2 | `info` | `INF` | General informational messages |
185
+ | 3 | `warn` | `WRN` | Warning messages |
186
+ | 4 | `error` | `ERR` | Error messages for failures |
187
+ | 5 | `fatal` | `FTL` | Fatal errors causing termination |
188
+
189
+ Consumer code should use level constants on the logger instance (`log.INFO`, `log.ERROR`, etc.) rather than importing `lvl` directly. The `lvl` export is intended for `createLogger` options only.
190
+
191
+ ## `createLogger()`
192
+
193
+ Create a logger instance with optional module name and configuration.
194
+
195
+ ```typescript
196
+ function createLogger(module?: string, options?: LoggerOptions): Logger
197
+ ```
198
+
199
+ ### Parameters
200
+
201
+ | Parameter | Type | Default | Description |
202
+ |-----------|-----------------|-------------|-------------------------------------------------|
203
+ | `module` | `string` | `undefined` | Module name added as the first entry in `modules`. |
204
+ | `options` | `LoggerOptions` | `{}` | Logger configuration. |
205
+
206
+ ### Examples
207
+
208
+ ```typescript
209
+ // Default logger (INFO level, pretty transport)
210
+ const logger = createLogger();
211
+ logger("Application ready");
212
+
213
+ // Module-specific logger
214
+ const dbLogger = createLogger("database");
215
+ dbLogger("Connected");
216
+ // Output: [database] Connected
217
+
218
+ // With explicit level and custom transport
219
+ const entries: LogEntry[] = [];
220
+ const spy: LogTransport = { write(e) { entries.push(e); } };
221
+ const testLogger = createLogger("test", {
222
+ transports: [spy],
223
+ level: lvl.TRACE,
224
+ });
225
+ ```
226
+
227
+ ## `prettyTransport()`
228
+
229
+ Create a built-in pretty console transport.
230
+
231
+ ```typescript
232
+ function prettyTransport(options?: PrettyOptions): LogTransport
233
+ ```
234
+
235
+ Renders log entries to a human-readable format with optional ANSI colors. Writes to `process.stderr` by default.
236
+
237
+ ### Output Format
238
+
239
+ ```
240
+ {dim timestamp} {colored TAG} {[mod] [mod]} {message} {dim context}
241
+ ```
242
+
243
+ Example (colors disabled):
244
+
245
+ ```
246
+ 12:34:56.789 INF [app] Server started {"port":3000}
247
+ 12:34:56.800 ERR [app] Request failed
248
+ err: Err: Connection timeout [NET:TIMEOUT]
249
+ ```
250
+
251
+ `Err` instances in context are rendered via `Err.toString()` on their own indented line below the main line.
252
+
253
+ ### Examples
254
+
255
+ ```typescript
256
+ // Default: stderr, auto colors, short timestamps
257
+ const transport = prettyTransport();
258
+
259
+ // Disable colors, use ISO timestamps
260
+ const plain = prettyTransport({
261
+ colors: false,
262
+ timestamp: "iso",
263
+ });
264
+
265
+ // Custom output stream
266
+ const buf: string[] = [];
267
+ const capture = prettyTransport({
268
+ output: { write(data) { buf.push(data); } },
269
+ colors: false,
270
+ });
271
+
272
+ // Custom timestamp formatter
273
+ const custom = prettyTransport({
274
+ timestamp: (ts) => new Date(ts).toLocaleDateString(),
275
+ });
276
+ ```
277
+
278
+ ## `log`
279
+
280
+ Default logger instance created with no module name and default options. Provided for convenience; prefer `createLogger` with an explicit module name for production use.
281
+
282
+ ```typescript
283
+ export const log: Logger = createLogger();
284
+ ```
285
+
286
+ ### Examples
287
+
288
+ ```typescript
289
+ import { log } from "@pencroff-lab/kore";
290
+
291
+ log("Application started");
292
+ log(log.INFO, "Server listening", { port: 3000 });
293
+ log(log.ERROR, "Startup failed", err);
294
+ ```
295
+
296
+ ## `child()`
297
+
298
+ Create a child logger with module-specific context.
299
+
300
+ ```typescript
301
+ child(module: string, bindings?: object): Logger
302
+ ```
303
+
304
+ ### Parameters
305
+
306
+ | Parameter | Type | Default | Description |
307
+ |------------|----------|-------------|---------------------------------------------------|
308
+ | `module` | `string` | (required) | Module name appended to the `modules` array. |
309
+ | `bindings` | `object` | `undefined` | Key-value pairs merged into every entry's context. |
310
+
311
+ Child loggers inherit level, transports, and parent bindings. Module names are accumulated, producing `[parent] [child]` prefixes in output.
312
+
313
+ ### Examples
314
+
315
+ ```typescript
316
+ const appLog = createLogger("app");
317
+
318
+ // Service-scoped child
319
+ const dbLog = appLog.child("database", { version: "1.0" });
320
+ dbLog("Connected to postgres");
321
+ // Output: [app] [database] Connected to postgres {"version":"1.0"}
322
+
323
+ // Nested children
324
+ const userLog = dbLog.child("users");
325
+ userLog("User created");
326
+ // Output: [app] [database] [users] User created {"version":"1.0"}
327
+
328
+ // Run-scoped child with correlation ID
329
+ const runLog = appLog.child("handler", { runId: "abc-123" });
330
+ runLog(runLog.INFO, "Processing started");
331
+ // Output: [app] [handler] Processing started {"runId":"abc-123"}
332
+ ```
333
+
334
+ ## Environment Configuration
335
+
336
+ ```bash
337
+ # Minimum log level (default: info)
338
+ # Valid values: trace, debug, info, warn, error, fatal
339
+ LOG_LEVEL=debug
340
+ ```
341
+
342
+ The logger reads `LOG_LEVEL` at creation time. When not set, defaults to `info`. The level can be overridden via `createLogger` options.