hazo_logs 1.1.0 → 2.0.1

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 (80) hide show
  1. package/README.md +198 -4
  2. package/config/hazo_logs_config.ini.sample +2 -0
  3. package/dist/index.d.ts +6 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +6 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/lib/alert-triggers.d.ts +54 -0
  8. package/dist/lib/alert-triggers.d.ts.map +1 -0
  9. package/dist/lib/alert-triggers.js +96 -0
  10. package/dist/lib/alert-triggers.js.map +1 -0
  11. package/dist/lib/config_loader.d.ts.map +1 -1
  12. package/dist/lib/config_loader.js +1 -7
  13. package/dist/lib/config_loader.js.map +1 -1
  14. package/dist/lib/console-logger.d.ts +17 -1
  15. package/dist/lib/console-logger.d.ts.map +1 -1
  16. package/dist/lib/console-logger.js +47 -4
  17. package/dist/lib/console-logger.js.map +1 -1
  18. package/dist/lib/context/log-context.d.ts.map +1 -1
  19. package/dist/lib/context/log-context.js +4 -0
  20. package/dist/lib/context/log-context.js.map +1 -1
  21. package/dist/lib/eslint_plugin/index.d.ts +109 -0
  22. package/dist/lib/eslint_plugin/index.d.ts.map +1 -0
  23. package/dist/lib/eslint_plugin/index.js +168 -0
  24. package/dist/lib/eslint_plugin/index.js.map +1 -0
  25. package/dist/lib/global-error-handlers.d.ts +23 -0
  26. package/dist/lib/global-error-handlers.d.ts.map +1 -0
  27. package/dist/lib/global-error-handlers.js +46 -0
  28. package/dist/lib/global-error-handlers.js.map +1 -0
  29. package/dist/lib/hazo_logger.d.ts +9 -0
  30. package/dist/lib/hazo_logger.d.ts.map +1 -1
  31. package/dist/lib/hazo_logger.js +32 -0
  32. package/dist/lib/hazo_logger.js.map +1 -1
  33. package/dist/lib/level_resolver.d.ts +26 -0
  34. package/dist/lib/level_resolver.d.ts.map +1 -0
  35. package/dist/lib/level_resolver.js +65 -0
  36. package/dist/lib/level_resolver.js.map +1 -0
  37. package/dist/lib/log-reader.d.ts.map +1 -1
  38. package/dist/lib/log-reader.js +6 -4
  39. package/dist/lib/log-reader.js.map +1 -1
  40. package/dist/lib/package_logger.d.ts +47 -3
  41. package/dist/lib/package_logger.d.ts.map +1 -1
  42. package/dist/lib/package_logger.js +196 -10
  43. package/dist/lib/package_logger.js.map +1 -1
  44. package/dist/lib/ring_buffer.d.ts +51 -0
  45. package/dist/lib/ring_buffer.d.ts.map +1 -0
  46. package/dist/lib/ring_buffer.js +107 -0
  47. package/dist/lib/ring_buffer.js.map +1 -0
  48. package/dist/lib/types.d.ts +57 -24
  49. package/dist/lib/types.d.ts.map +1 -1
  50. package/dist/lib/universal-logger.d.ts.map +1 -1
  51. package/dist/lib/universal-logger.js +18 -0
  52. package/dist/lib/universal-logger.js.map +1 -1
  53. package/dist/lib/utils/caller_info.d.ts +25 -1
  54. package/dist/lib/utils/caller_info.d.ts.map +1 -1
  55. package/dist/lib/utils/caller_info.js +62 -1
  56. package/dist/lib/utils/caller_info.js.map +1 -1
  57. package/dist/lib/with-error-handler.d.ts +19 -0
  58. package/dist/lib/with-error-handler.d.ts.map +1 -0
  59. package/dist/lib/with-error-handler.js +77 -0
  60. package/dist/lib/with-error-handler.js.map +1 -0
  61. package/dist/server.d.ts +9 -0
  62. package/dist/server.d.ts.map +1 -1
  63. package/dist/server.js +7 -0
  64. package/dist/server.js.map +1 -1
  65. package/dist/ui/components/ErrorBoundary.d.ts +44 -0
  66. package/dist/ui/components/ErrorBoundary.d.ts.map +1 -0
  67. package/dist/ui/components/ErrorBoundary.js +57 -0
  68. package/dist/ui/components/ErrorBoundary.js.map +1 -0
  69. package/dist/ui/components/LogLevelBadge.d.ts.map +1 -1
  70. package/dist/ui/components/LogLevelBadge.js +1 -0
  71. package/dist/ui/components/LogLevelBadge.js.map +1 -1
  72. package/dist/ui/hooks/useErrorReporter.d.ts +16 -0
  73. package/dist/ui/hooks/useErrorReporter.d.ts.map +1 -0
  74. package/dist/ui/hooks/useErrorReporter.js +56 -0
  75. package/dist/ui/hooks/useErrorReporter.js.map +1 -0
  76. package/dist/ui/index.d.ts +4 -0
  77. package/dist/ui/index.d.ts.map +1 -1
  78. package/dist/ui/index.js +3 -0
  79. package/dist/ui/index.js.map +1 -1
  80. package/package.json +19 -7
package/README.md CHANGED
@@ -5,12 +5,88 @@ A Winston-based logging library for Node.js/Next.js applications with a built-in
5
5
  [![npm version](https://img.shields.io/npm/v/hazo_logs.svg)](https://www.npmjs.com/package/hazo_logs)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
+ ## Log Level Convention
9
+
10
+ hazo_logs uses a 5-level system (from design spec D-015). Each level has a defined purpose — use the narrowest one that fits.
11
+
12
+ | Level | When to use |
13
+ |-------|-------------|
14
+ | `TRACE` | High-frequency operational noise: polling ticks, cache hits, ping responses. Off in production. |
15
+ | `DEBUG` | Operational detail useful when actively debugging a problem. Off in production. |
16
+ | `INFO` | State changes and user-facing events (login, job started, order placed). Default level in dev. |
17
+ | `WARN` | Recoverable problems: rate-limited, retrying, degraded-mode fallback. Default level in production. |
18
+ | `ERROR` | Unrecovered failures that require attention: unhandled exception, data loss, external service down. |
19
+
20
+ ### Default levels
21
+
22
+ | Environment | Default level |
23
+ |-------------|--------------|
24
+ | `development` / `test` (`NODE_ENV`) | `info` |
25
+ | `production` / `staging` (`HAZO_ENV` or `NODE_ENV`) | `warn` |
26
+
27
+ The resolver checks `HAZO_ENV` first, then `NODE_ENV`, then falls back to `development`.
28
+
29
+ ### Per-namespace overrides (INI config)
30
+
31
+ Fine-tune the level for a specific package or sub-namespace in `config/hazo_logs_config.ini`:
32
+
33
+ ```ini
34
+ [log.overrides]
35
+ ; Package-level — matches hazo_chat and any hazo_chat/* sub-namespace
36
+ hazo_chat = warn
37
+
38
+ ; Sub-namespace — more specific, takes precedence over the package-level override
39
+ hazo_chat/poll = error
40
+
41
+ ; Prefix match — matches any hazo_files/* sub-namespace
42
+ hazo_files/ = debug
43
+ ```
44
+
45
+ Precedence (highest wins): exact match > prefix match > package-level > global default.
46
+
47
+ You can also use environment variables to override INI config at runtime:
48
+
49
+ ```bash
50
+ HAZO_LOGS_LOG_OVERRIDES_hazo_files=debug node server.js
51
+ ```
52
+
53
+ ### Smart logging helpers
54
+
55
+ Use these instead of raw `log.info` loops to reduce noise without losing signal:
56
+
57
+ **`log.heartbeat(name, { status, ...fields }, opts?)`**
58
+ Emits only on state change. Silent on repeated same-status calls. Use for "is it still working?" checks on polled resources (DB connectivity, external API, queue depth).
59
+
60
+ ```typescript
61
+ // Emits once when connected, once on disconnect, once when it reconnects
62
+ log.heartbeat('db.primary', { status: connected ? 'ok' : 'down', latencyMs });
63
+ ```
64
+
65
+ **`log.sampled(level, msg, fields?, { sample })`**
66
+ Emits with the given probability. Default sample rate is 0.1 (10%). Use for high-frequency paths where you want occasional visibility without flooding logs.
67
+
68
+ ```typescript
69
+ // Logs roughly 1 in 10 cache hits
70
+ log.sampled('debug', 'cache.hit', { key }, { sample: 0.1 });
71
+ ```
72
+
73
+ **`log.dedupe(level, msg, fields?, { window, key? })`**
74
+ Collapses identical-keyed messages within a time window. The first occurrence emits immediately; at window end, if more than one occurred, emits a summary `"msg (×N over Xs)"`. Use when the same warning fires rapidly (e.g. validation errors on bulk import).
75
+
76
+ ```typescript
77
+ // First occurrence emits; subsequent in the same minute are collapsed
78
+ log.dedupe('warn', 'validation.failed', { field }, { window: '1m' });
79
+ ```
80
+
8
81
  ## Features
9
82
 
10
83
  - **Structured Logging**: Winston wrapper with singleton pattern for consistent logging across your app
11
84
  - **Package Tagging**: Automatically tag logs by package/module name
12
85
  - **Daily Rotation**: Automatic log file rotation with configurable retention
13
86
  - **Session Tracking**: Track logs across async operations with sessionId and reference
87
+ - **Fatal Level**: `logger.fatal()` for process-critical events, above `error` in severity
88
+ - **Context Fields**: Attach `scope` (dotted operation path) and `userId` to all logs via ALS context
89
+ - **Error Handling Surface**: `withErrorHandler`, `installGlobalErrorHandlers`, `registerAlertTrigger`, `<ErrorBoundary />`, `useErrorReporter` — structured error capture with correlationId tracking
14
90
  - **Built-in UI**: React-based log viewer with filtering, sorting, and pagination
15
91
  - **Zero Config**: Works out of the box with sensible defaults
16
92
  - **Minimal Integration**: Add log viewer to your app with just 2 files (~6 lines of code)
@@ -249,22 +325,27 @@ import { createLogger, runWithLogContext } from 'hazo_logs/server';
249
325
 
250
326
  const logger = createLogger('auth');
251
327
 
252
- async function handleRequest(req) {
328
+ async function handle_request(req) {
253
329
  await runWithLogContext(
254
330
  {
255
331
  sessionId: req.sessionId,
256
332
  reference: `user-${req.userId}`,
257
333
  depth: 0,
334
+ scope: 'auth.login', // Fine-grained operation scope (dotted notation)
335
+ userId: req.userId, // User ID propagated to all log entries
258
336
  },
259
337
  async () => {
260
- logger.info('Request started'); // Automatically includes sessionId and reference
261
- await processRequest(req);
338
+ logger.info('Request started'); // Automatically includes all context fields
339
+ await process_request(req);
262
340
  logger.info('Request completed');
263
341
  }
264
342
  );
265
343
  }
266
344
  ```
267
345
 
346
+ Available context fields: `sessionId`, `reference`, `depth`, `scope`, `userId`. All fields present in the ALS context are automatically written to every `LogEntry` produced within the callback.
347
+ ```
348
+
268
349
  ### Embedded Log Viewer
269
350
 
270
351
  Use the log viewer as a sidebar component:
@@ -450,6 +531,7 @@ logger.info('Hello world');
450
531
 
451
532
  ```typescript
452
533
  interface Logger {
534
+ fatal(message: string, data?: Record<string, unknown>): void;
453
535
  error(message: string, data?: Record<string, unknown>): void;
454
536
  warn(message: string, data?: Record<string, unknown>): void;
455
537
  info(message: string, data?: Record<string, unknown>): void;
@@ -501,6 +583,52 @@ const logger = createClientLogger({
501
583
  });
502
584
  ```
503
585
 
586
+ #### `ErrorBoundary`
587
+
588
+ React class component that catches render errors, generates a `correlationId`, and logs to the server via the client logger pipeline. The `fallback` render prop receives `{ error, correlationId, retry }`.
589
+
590
+ ```tsx
591
+ import { ErrorBoundary } from 'hazo_logs/ui';
592
+
593
+ <ErrorBoundary
594
+ fallback={({ error, correlationId, retry }) => (
595
+ <div>
596
+ <p>Something went wrong. Reference: {correlationId}</p>
597
+ <button onClick={retry}>Retry</button>
598
+ </div>
599
+ )}
600
+ >
601
+ <MyComponent />
602
+ </ErrorBoundary>
603
+ ```
604
+
605
+ Props: `fallback` (required), `onError?: (error, correlationId) => void`, `apiBasePath?: string`, `packageName?: string`.
606
+
607
+ **React 19 + Next.js 16 note:** Wrap state changes that may cause throws inside `startTransition()` to prevent the dev overlay from covering the error fallback. React 19 dev mode re-reports caught errors globally; transitions suppress this.
608
+
609
+ #### `useErrorReporter(options?)`
610
+
611
+ React hook for manual error reporting. Each method returns a correlationId string. Auto-attaches `route_path` (window.location.pathname) and `user_agent` to every entry.
612
+
613
+ ```tsx
614
+ import { useErrorReporter } from 'hazo_logs/ui';
615
+
616
+ function MyComponent() {
617
+ const { reportError } = useErrorReporter({ userId: session?.user?.id });
618
+
619
+ const handle_action = async () => {
620
+ try {
621
+ await risky_operation();
622
+ } catch (err) {
623
+ const id = reportError(err, { component: 'MyComponent' });
624
+ show_toast(`Something went wrong. Reference: ${id}`);
625
+ }
626
+ };
627
+ }
628
+ ```
629
+
630
+ Methods: `reportError(error, context?)`, `reportWarning(message, context?)`, `reportInfo(message, context?)`. Pass `options.userId` to attach `user_id` to all entries — no `hazo_auth` import required.
631
+
504
632
  #### `LogViewerPage`
505
633
 
506
634
  Main log viewer component.
@@ -562,6 +690,70 @@ const authHandler = withLogAuth(handler, async (request) => {
562
690
  });
563
691
  ```
564
692
 
693
+ ### Error Handling Surface (`hazo_logs/server`)
694
+
695
+ #### `withErrorHandler(handler)`
696
+
697
+ Wraps a Next.js App Router handler with structured error handling. Generates a `correlationId` (UUID), sets `X-Correlation-Id` on error responses, catches `HandledError` duck-typed throws returning structured JSON with the correct HTTP status, and catches generic `Error` logging at `fatal` and returning 500 (internal message never reaches the client).
698
+
699
+ ```typescript
700
+ import { withErrorHandler } from 'hazo_logs/server';
701
+
702
+ export const GET = withErrorHandler(async (req: Request) => {
703
+ // Throw HandledError for structured 4xx/5xx responses:
704
+ throw { code: 'not_found', statusCode: 404, userMessage: 'Resource not found' };
705
+ // Throw Error for 500 — message never reaches client:
706
+ throw new Error('db connection failed');
707
+ });
708
+ ```
709
+
710
+ Error response shape:
711
+ ```json
712
+ { "error": "Resource not found", "code": "not_found", "correlationId": "uuid" }
713
+ ```
714
+
715
+ Level routing: 5xx throws → `error`, 4xx throws → `warn`, generic `Error` → `fatal`.
716
+
717
+ #### `installGlobalErrorHandlers(options?)`
718
+
719
+ Installs `process.on('unhandledRejection')` and `process.on('uncaughtException')`. Both log at `fatal` with a correlationId, call optional `onFatal`, then wait 500ms and call `process.exit(1)`. Call once at process startup (e.g. `instrumentation.ts`).
720
+
721
+ ```typescript
722
+ import { installGlobalErrorHandlers } from 'hazo_logs/server';
723
+
724
+ installGlobalErrorHandlers({
725
+ onFatal: async (err) => {
726
+ await alertOpsTeam(err);
727
+ },
728
+ exitOnUncaught: true, // default
729
+ });
730
+ ```
731
+
732
+ Options: `onFatal?: (err: Error) => void | Promise<void>`, `exitOnUncaught?: boolean`.
733
+
734
+ #### `registerAlertTrigger(config)`
735
+
736
+ Sliding-window alert trigger via `hazo_notify` (optional peer dep). Hooks into `HazoLogger.addLogHook` and fires when a match threshold is crossed within a time window. Returns an unregister function.
737
+
738
+ ```typescript
739
+ import { registerAlertTrigger } from 'hazo_logs/server';
740
+
741
+ const unregister = registerAlertTrigger({
742
+ id: 'payment_errors',
743
+ match: { level: 'error', scopePrefix: 'payment.' },
744
+ threshold: { count: 3, windowMs: 60_000 },
745
+ notify: {
746
+ kind: 'ops.payment_error',
747
+ scopeId: 'my_app',
748
+ recipientUserIds: ['ops-user-uuid'],
749
+ surfaces: ['email'],
750
+ },
751
+ });
752
+ // Call unregister() to stop listening
753
+ ```
754
+
755
+ `match` supports: `level`, `scopePrefix`, `message_contains`. Set `threshold.count` to `0` to fire on every match. `recipientUserIds` can be a static array or `(entry) => string[]`.
756
+
565
757
  ## Live tail (v1.1+)
566
758
 
567
759
  `tailLogs` watches the active rotating log file and emits new entries as they are written. Filters mirror `readLogs`. Use it directly for in-process consumers:
@@ -597,7 +789,7 @@ Logs are stored as newline-delimited JSON:
597
789
 
598
790
  **Fields:**
599
791
  - `timestamp` - ISO 8601 timestamp
600
- - `level` - Log level (error, warn, info, debug)
792
+ - `level` - Log level (fatal, error, warn, info, debug)
601
793
  - `package` - Package name from createLogger
602
794
  - `message` - Log message
603
795
  - `filename` - Source file name
@@ -606,6 +798,8 @@ Logs are stored as newline-delimited JSON:
606
798
  - `sessionId` - Optional session ID from context
607
799
  - `reference` - Optional reference from context
608
800
  - `depth` - Optional call depth from context
801
+ - `scope` - Optional operation scope from context (dotted notation, e.g. `"payment.checkout.refund"`)
802
+ - `userId` - Optional user ID from ALS context
609
803
  - `data` - Optional additional data
610
804
 
611
805
  ## UI Screenshots
@@ -0,0 +1,2 @@
1
+ ; hazo_logs configuration sample
2
+ ; Copy to hazo_logs_config.ini and fill in values.
package/dist/index.d.ts CHANGED
@@ -33,5 +33,10 @@
33
33
  * ```
34
34
  */
35
35
  export { createLogger, createLoggerAsync, preloadServerLogger, ConsoleLogger, } from './lib/universal-logger.js';
36
- export type { Logger, LogLevel, LogData, LogEntry, LogContext, HazoLogConfig, PackageLoggerOptions, LogSource, LogOrigin, } from './lib/types.js';
36
+ export { getRingBuffer, RingBuffer, RingBufferRegistry, ringBufferRegistry } from './lib/ring_buffer.js';
37
+ export { getCallerInfo, get_filename, get_line_number } from './lib/utils/caller_info.js';
38
+ export type { CallerInfo } from './lib/utils/caller_info.js';
39
+ export { LevelResolver } from './lib/level_resolver.js';
40
+ export type { OverrideMap } from './lib/level_resolver.js';
41
+ export type { Logger, LogLevel, LogData, LogEntry, LogContext, HazoLogConfig, PackageLoggerOptions, LogSource, LogOrigin, RingBufferEntry, } from './lib/types.js';
37
42
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,GACd,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EACV,MAAM,EACN,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,UAAU,EACV,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,SAAS,GACV,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,GACd,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAGzG,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC1F,YAAY,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,YAAY,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,YAAY,EACV,MAAM,EACN,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,UAAU,EACV,aAAa,EACb,oBAAoB,EACpB,SAAS,EACT,SAAS,EACT,eAAe,GAChB,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -34,4 +34,10 @@
34
34
  */
35
35
  // Universal logger - works on both server and client
36
36
  export { createLogger, createLoggerAsync, preloadServerLogger, ConsoleLogger, } from './lib/universal-logger.js';
37
+ // Ring buffer — safe for server; no-ops on client
38
+ export { getRingBuffer, RingBuffer, RingBufferRegistry, ringBufferRegistry } from './lib/ring_buffer.js';
39
+ // Caller-info utilities — server-side stack-trace helpers
40
+ export { getCallerInfo, get_filename, get_line_number } from './lib/utils/caller_info.js';
41
+ // LevelResolver — pure logic, no Node.js deps, safe for universal use
42
+ export { LevelResolver } from './lib/level_resolver.js';
37
43
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,qDAAqD;AACrD,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,GACd,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,qDAAqD;AACrD,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,aAAa,GACd,MAAM,2BAA2B,CAAC;AAEnC,kDAAkD;AAClD,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAEzG,0DAA0D;AAC1D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAG1F,sEAAsE;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * registerAlertTrigger — fires hazo_notify dispatch when log volume crosses a threshold.
3
+ *
4
+ * Requires hazo_notify to be installed (optional peer dep).
5
+ * Sliding window state is in-memory — resets on process restart.
6
+ */
7
+ import type { LogEntry, LogLevel } from './types.js';
8
+ export interface AlertTriggerMatch {
9
+ /** Only match entries at this level or above */
10
+ level?: LogLevel;
11
+ /** Only match entries whose `scope` starts with this prefix */
12
+ scopePrefix?: string;
13
+ /** Only match entries whose message contains this substring */
14
+ message_contains?: string;
15
+ }
16
+ export interface AlertTriggerNotify {
17
+ /** hazo_notify event_type */
18
+ kind: string;
19
+ /** hazo_notify scope_id */
20
+ scopeId: string;
21
+ /** Static list OR function returning IDs from the triggering log entry */
22
+ recipientUserIds: string[] | ((entry: LogEntry) => string[]);
23
+ /** hazo_notify surfaces (e.g. ['email', 'bell']) */
24
+ surfaces?: string[];
25
+ }
26
+ export interface AlertTriggerConfig {
27
+ id: string;
28
+ match: AlertTriggerMatch;
29
+ threshold: {
30
+ /** Number of matching events required to fire */
31
+ count: number;
32
+ /** Sliding window duration in ms. 0 = fire on every match (no window) */
33
+ windowMs: number;
34
+ };
35
+ notify: AlertTriggerNotify;
36
+ }
37
+ /**
38
+ * Register an alert trigger. Returns an unregister function.
39
+ *
40
+ * @example
41
+ * const unregister = registerAlertTrigger({
42
+ * id: 'payment_errors',
43
+ * match: { level: 'error', scopePrefix: 'payment.' },
44
+ * threshold: { count: 1, windowMs: 0 },
45
+ * notify: {
46
+ * kind: 'ops.payment_error',
47
+ * scopeId: 'my_app',
48
+ * recipientUserIds: ['ops-user-uuid'],
49
+ * surfaces: ['email'],
50
+ * },
51
+ * });
52
+ */
53
+ export declare function registerAlertTrigger(config: AlertTriggerConfig): () => void;
54
+ //# sourceMappingURL=alert-triggers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert-triggers.d.ts","sourceRoot":"","sources":["../../src/lib/alert-triggers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAErD,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,gBAAgB,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,KAAK,MAAM,EAAE,CAAC,CAAC;IAC7D,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,iBAAiB,CAAC;IACzB,SAAS,EAAE;QACT,iDAAiD;QACjD,KAAK,EAAE,MAAM,CAAC;QACd,yEAAyE;QACzE,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AA2DD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,IAAI,CA2B3E"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * registerAlertTrigger — fires hazo_notify dispatch when log volume crosses a threshold.
3
+ *
4
+ * Requires hazo_notify to be installed (optional peer dep).
5
+ * Sliding window state is in-memory — resets on process restart.
6
+ */
7
+ import { optional_import } from 'hazo_core';
8
+ import { HazoLogger } from './hazo_logger.js';
9
+ const HAZO_LOG_LEVEL_PRIORITY = {
10
+ fatal: 0,
11
+ error: 1,
12
+ warn: 2,
13
+ info: 3,
14
+ debug: 4,
15
+ trace: 5,
16
+ };
17
+ function matchesTrigger(entry, match) {
18
+ if (match.level) {
19
+ // Entry level must be at least as severe as the match level
20
+ if (HAZO_LOG_LEVEL_PRIORITY[entry.level] > HAZO_LOG_LEVEL_PRIORITY[match.level]) {
21
+ return false;
22
+ }
23
+ }
24
+ if (match.scopePrefix && !entry.scope?.startsWith(match.scopePrefix)) {
25
+ return false;
26
+ }
27
+ if (match.message_contains && !entry.message.includes(match.message_contains)) {
28
+ return false;
29
+ }
30
+ return true;
31
+ }
32
+ async function fireNotification(config, entry) {
33
+ const dispatcher = await optional_import('hazo_notify/dispatcher');
34
+ if (!dispatcher) {
35
+ console.error(`[hazo_logs] Alert trigger "${config.id}" fired but hazo_notify is not installed. ` +
36
+ 'Add hazo_notify as a dependency to receive alert notifications.');
37
+ return;
38
+ }
39
+ const recipientUserIds = Array.isArray(config.notify.recipientUserIds)
40
+ ? config.notify.recipientUserIds
41
+ : config.notify.recipientUserIds(entry);
42
+ await dispatcher.dispatch({
43
+ scope_id: config.notify.scopeId,
44
+ event_type: config.notify.kind,
45
+ subject_id: entry.timestamp,
46
+ recipient_user_ids: recipientUserIds,
47
+ surfaces: config.notify.surfaces,
48
+ payloads: {
49
+ log_level: entry.level,
50
+ log_package: entry.package,
51
+ log_message: entry.message,
52
+ log_scope: entry.scope,
53
+ log_timestamp: entry.timestamp,
54
+ },
55
+ });
56
+ }
57
+ /**
58
+ * Register an alert trigger. Returns an unregister function.
59
+ *
60
+ * @example
61
+ * const unregister = registerAlertTrigger({
62
+ * id: 'payment_errors',
63
+ * match: { level: 'error', scopePrefix: 'payment.' },
64
+ * threshold: { count: 1, windowMs: 0 },
65
+ * notify: {
66
+ * kind: 'ops.payment_error',
67
+ * scopeId: 'my_app',
68
+ * recipientUserIds: ['ops-user-uuid'],
69
+ * surfaces: ['email'],
70
+ * },
71
+ * });
72
+ */
73
+ export function registerAlertTrigger(config) {
74
+ // Per-trigger sliding window: array of event timestamps
75
+ const eventTimestamps = [];
76
+ const unregister = HazoLogger.addLogHook((entry) => {
77
+ if (!matchesTrigger(entry, config.match))
78
+ return;
79
+ const now = Date.now();
80
+ // Prune events outside the window (skip for windowMs = 0)
81
+ if (config.threshold.windowMs > 0) {
82
+ const cutoff = now - config.threshold.windowMs;
83
+ while (eventTimestamps.length > 0 && eventTimestamps[0] < cutoff) {
84
+ eventTimestamps.shift();
85
+ }
86
+ }
87
+ eventTimestamps.push(now);
88
+ if (eventTimestamps.length >= config.threshold.count) {
89
+ // Reset window before async fire so re-entrant logs don't double-fire
90
+ eventTimestamps.length = 0;
91
+ void fireNotification(config, entry);
92
+ }
93
+ });
94
+ return unregister;
95
+ }
96
+ //# sourceMappingURL=alert-triggers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert-triggers.js","sourceRoot":"","sources":["../../src/lib/alert-triggers.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"config_loader.d.ts","sourceRoot":"","sources":["../../src/lib/config_loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,YAAY,CAAC;AAQ1D;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,CAmC7D"}
1
+ {"version":3,"file":"config_loader.d.ts","sourceRoot":"","sources":["../../src/lib/config_loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,YAAY,CAAC;AAKhE;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,CA6B7D"}
@@ -7,8 +7,6 @@ import path from 'path';
7
7
  import ini from 'ini';
8
8
  import { DEFAULT_CONFIG } from './utils/constants.js';
9
9
  const CONFIG_FILENAME = 'config/hazo_logs_config.ini';
10
- // Track whether the missing config warning has been shown
11
- let _config_warning_shown = false;
12
10
  /**
13
11
  * Load configuration from INI file
14
12
  * @param configPath - Optional explicit path to config file
@@ -17,10 +15,6 @@ let _config_warning_shown = false;
17
15
  export function loadConfig(configPath) {
18
16
  const resolvedPath = configPath || findConfigFile();
19
17
  if (!resolvedPath || !fs.existsSync(resolvedPath)) {
20
- if (!_config_warning_shown) {
21
- _config_warning_shown = true;
22
- console.warn(`[HazoLog] No config found, using defaults. Create ${CONFIG_FILENAME} to customize.`);
23
- }
24
18
  return { ...DEFAULT_CONFIG };
25
19
  }
26
20
  try {
@@ -71,7 +65,7 @@ function findConfigFile() {
71
65
  return null;
72
66
  }
73
67
  /**
74
- * Validate and return a valid log level
68
+ * Validate and return a valid config log level (fatal excluded — not a valid min-level)
75
69
  */
76
70
  function validateLogLevel(level) {
77
71
  const validLevels = ['error', 'warn', 'info', 'debug'];
@@ -1 +1 @@
1
- {"version":3,"file":"config_loader.js","sourceRoot":"","sources":["../../src/lib/config_loader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config_loader.js","sourceRoot":"","sources":["../../src/lib/config_loader.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  * This module imports ONLY from types.ts (pure type definitions)
6
6
  * so it is safe for both server and client environments.
7
7
  */
8
- import type { Logger, LogData, LogLevel } from './types.js';
8
+ import type { Logger, LogData, LogLevel, RingBufferEntry } from './types.js';
9
9
  export declare const LOG_LEVEL_PRIORITY: Record<LogLevel, number>;
10
10
  /**
11
11
  * Console-based logger for client-side use
@@ -18,9 +18,25 @@ export declare class ConsoleLogger implements Logger {
18
18
  constructor(packageName: string, minLevel?: LogLevel, on_log?: (level: string, message: string, data?: Record<string, unknown>) => void);
19
19
  private shouldLog;
20
20
  private formatMessage;
21
+ fatal(message: string, data?: LogData): void;
21
22
  error(message: string, data?: LogData): void;
22
23
  warn(message: string, data?: LogData): void;
23
24
  info(message: string, data?: LogData): void;
24
25
  debug(message: string, data?: LogData): void;
26
+ trace(message: string, data?: LogData): void;
27
+ heartbeat(_name: string, _fields: {
28
+ status: string;
29
+ [key: string]: unknown;
30
+ }, _opts?: {
31
+ trigger?: (prev: Record<string, unknown>, curr: Record<string, unknown>) => boolean;
32
+ }): void;
33
+ sampled(level: 'trace' | 'debug' | 'info' | 'warn', msg: string, fields?: Record<string, unknown>, opts?: {
34
+ sample: number;
35
+ }): void;
36
+ dedupe(level: 'trace' | 'debug' | 'info' | 'warn', msg: string, fields?: Record<string, unknown>, _opts?: {
37
+ window: string;
38
+ key?: string;
39
+ }): void;
40
+ getRingBuffer(_correlationId: string, _n?: number): RingBufferEntry[];
25
41
  }
26
42
  //# sourceMappingURL=console-logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"console-logger.d.ts","sourceRoot":"","sources":["../../src/lib/console-logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE5D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAKvD,CAAC;AAEF;;;GAGG;AACH,qBAAa,aAAc,YAAW,MAAM;IAC1C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,MAAM,CAAC,CAA2E;gBAE9E,WAAW,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAkB,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;IAMhJ,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,aAAa;IAKrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAO5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAO3C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAO3C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;CAM7C"}
1
+ {"version":3,"file":"console-logger.d.ts","sourceRoot":"","sources":["../../src/lib/console-logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7E,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAOvD,CAAC;AAEF;;;GAGG;AACH,qBAAa,aAAc,YAAW,MAAM;IAC1C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,MAAM,CAAC,CAA2E;gBAE9E,WAAW,EAAE,MAAM,EAAE,QAAQ,GAAE,QAAkB,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;IAMhJ,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,aAAa;IAKrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAO5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAO5C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAO3C,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAO3C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAO5C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;IAU5C,SAAS,CACP,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,EACnD,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAA;KAAE,GAC9F,IAAI;IAOP,OAAO,CACL,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,EAC1C,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,IAAI,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GACxB,IAAI;IAUP,MAAM,CACJ,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,EAC1C,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GACvC,IAAI;IASP,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE;CAItE"}
@@ -6,10 +6,12 @@
6
6
  * so it is safe for both server and client environments.
7
7
  */
8
8
  export const LOG_LEVEL_PRIORITY = {
9
- error: 0,
10
- warn: 1,
11
- info: 2,
12
- debug: 3,
9
+ fatal: 0,
10
+ error: 1,
11
+ warn: 2,
12
+ info: 3,
13
+ debug: 4,
14
+ trace: 5,
13
15
  };
14
16
  /**
15
17
  * Console-based logger for client-side use
@@ -31,6 +33,12 @@ export class ConsoleLogger {
31
33
  const timestamp = new Date().toISOString();
32
34
  return `[${timestamp}] [${level.toUpperCase()}] [${this.packageName}] ${message}`;
33
35
  }
36
+ fatal(message, data) {
37
+ if (this.shouldLog('fatal')) {
38
+ console.error(this.formatMessage('fatal', message), data ?? '');
39
+ this.on_log?.('fatal', message, data);
40
+ }
41
+ }
34
42
  error(message, data) {
35
43
  if (this.shouldLog('error')) {
36
44
  console.error(this.formatMessage('error', message), data ?? '');
@@ -55,5 +63,40 @@ export class ConsoleLogger {
55
63
  this.on_log?.('debug', message, data);
56
64
  }
57
65
  }
66
+ trace(message, data) {
67
+ if (this.shouldLog('trace')) {
68
+ // Use console.debug for trace — console.trace prints a stack trace
69
+ console.debug(this.formatMessage('trace', message), data ?? '');
70
+ this.on_log?.('trace', message, data);
71
+ }
72
+ }
73
+ // ─── Smart helpers (no-ops on client — server-side state would be meaningless) ─
74
+ heartbeat(_name, _fields, _opts) {
75
+ // Client-side: emit at debug level so it is at least visible when debugging
76
+ if (this.shouldLog('debug')) {
77
+ console.debug(this.formatMessage('debug', `heartbeat: ${_name}`), _fields);
78
+ }
79
+ }
80
+ sampled(level, msg, fields, opts) {
81
+ const sample = opts?.sample ?? 0.1;
82
+ if (Math.random() < sample && this.shouldLog(level)) {
83
+ const sampleLabel = `(sampled ~1/${Math.round(1 / sample)})`;
84
+ const method = level === 'warn' ? 'warn' : level === 'info' ? 'info' : 'debug';
85
+ console[method](this.formatMessage(level, `${msg} ${sampleLabel}`), fields ?? '');
86
+ this.on_log?.(level, `${msg} ${sampleLabel}`, fields);
87
+ }
88
+ }
89
+ dedupe(level, msg, fields, _opts) {
90
+ // Client-side: no deduplication state — just emit
91
+ if (this.shouldLog(level)) {
92
+ const method = level === 'warn' ? 'warn' : level === 'info' ? 'info' : 'debug';
93
+ console[method](this.formatMessage(level, msg), fields ?? '');
94
+ this.on_log?.(level, msg, fields);
95
+ }
96
+ }
97
+ getRingBuffer(_correlationId, _n) {
98
+ // Client-side: no ring buffer
99
+ return [];
100
+ }
58
101
  }
59
102
  //# sourceMappingURL=console-logger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"console-logger.js","sourceRoot":"","sources":["../../src/lib/console-logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,CAAC,MAAM,kBAAkB,GAA6B;IAC1D,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAEF;;;GAGG;AACH,MAAM,OAAO,aAAa;IAChB,WAAW,CAAS;IACpB,QAAQ,CAAW;IACnB,MAAM,CAA4E;IAE1F,YAAY,WAAmB,EAAE,WAAqB,OAAO,EAAE,MAAiF;QAC9I,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAEO,SAAS,CAAC,KAAe;QAC/B,OAAO,kBAAkB,CAAC,KAAK,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxE,CAAC;IAEO,aAAa,CAAC,KAAe,EAAE,OAAe;QACpD,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,OAAO,IAAI,SAAS,MAAM,KAAK,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;IACpF,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAAc;QACnC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;YAChE,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAAc;QAClC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;YAC9D,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAAc;QAClC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;YAC9D,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAAc;QACnC,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;YAChE,IAAI,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"console-logger.js","sourceRoot":"","sources":["../../src/lib/console-logger.ts"],"names":[],"mappings":"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"}