@happyvertical/logger 0.80.0 → 0.80.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.
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, mkdirSync, copyFileSync } from "node:fs";
2
+ import { copyFileSync, existsSync, mkdirSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- const Dirname = dirname(fileURLToPath(import.meta.url));
6
- const pkgRoot = join(Dirname, "../..");
7
- const targetDir = join(process.cwd(), ".claude");
8
- if (!existsSync(targetDir)) {
9
- mkdirSync(targetDir, { recursive: true });
10
- }
11
- const pkgName = "logger";
12
- const agentMdSrc = existsSync(join(pkgRoot, "AGENT.md")) ? join(pkgRoot, "AGENT.md") : join(pkgRoot, "CLAUDE.md");
13
- const metaSrc = existsSync(join(pkgRoot, "metadata.json")) ? join(pkgRoot, "metadata.json") : join(pkgRoot, ".claude-meta.json");
14
- if (existsSync(agentMdSrc)) {
15
- copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));
16
- }
17
- if (existsSync(metaSrc)) {
18
- copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));
19
- }
5
+ //#region src/cli/claude-context.ts
6
+ /**
7
+ * CLI script to install agent context for @happyvertical/logger
8
+ * Run the published context installer binary for this package.
9
+ */
10
+ var pkgRoot = join(dirname(fileURLToPath(import.meta.url)), "../..");
11
+ var targetDir = join(process.cwd(), ".claude");
12
+ if (!existsSync(targetDir)) mkdirSync(targetDir, { recursive: true });
13
+ var pkgName = "logger";
14
+ var agentMdSrc = existsSync(join(pkgRoot, "AGENT.md")) ? join(pkgRoot, "AGENT.md") : join(pkgRoot, "CLAUDE.md");
15
+ var metaSrc = existsSync(join(pkgRoot, "metadata.json")) ? join(pkgRoot, "metadata.json") : join(pkgRoot, ".claude-meta.json");
16
+ if (existsSync(agentMdSrc)) copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));
17
+ if (existsSync(metaSrc)) copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));
20
18
  console.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);
21
- //# sourceMappingURL=claude-context.js.map
19
+ //#endregion
20
+
21
+ //# sourceMappingURL=claude-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"claude-context.js","sources":["../../src/cli/claude-context.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI script to install agent context for @happyvertical/logger\n * Run the published context installer binary for this package.\n */\nimport { copyFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst Dirname = dirname(fileURLToPath(import.meta.url));\nconst pkgRoot = join(Dirname, '../..');\nconst targetDir = join(process.cwd(), '.claude');\n\nif (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true });\n}\n\nconst pkgName = 'logger';\nconst agentMdSrc = existsSync(join(pkgRoot, 'AGENT.md'))\n ? join(pkgRoot, 'AGENT.md')\n : join(pkgRoot, 'CLAUDE.md');\nconst metaSrc = existsSync(join(pkgRoot, 'metadata.json'))\n ? join(pkgRoot, 'metadata.json')\n : join(pkgRoot, '.claude-meta.json');\n\nif (existsSync(agentMdSrc)) {\n copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));\n}\n\nif (existsSync(metaSrc)) {\n copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));\n}\n\nconsole.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);\n"],"names":[],"mappings":";;;;AASA,MAAM,UAAU,QAAQ,cAAc,YAAY,GAAG,CAAC;AACtD,MAAM,UAAU,KAAK,SAAS,OAAO;AACrC,MAAM,YAAY,KAAK,QAAQ,IAAA,GAAO,SAAS;AAE/C,IAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,YAAU,WAAW,EAAE,WAAW,KAAA,CAAM;AAC1C;AAEA,MAAM,UAAU;AAChB,MAAM,aAAa,WAAW,KAAK,SAAS,UAAU,CAAC,IACnD,KAAK,SAAS,UAAU,IACxB,KAAK,SAAS,WAAW;AAC7B,MAAM,UAAU,WAAW,KAAK,SAAS,eAAe,CAAC,IACrD,KAAK,SAAS,eAAe,IAC7B,KAAK,SAAS,mBAAmB;AAErC,IAAI,WAAW,UAAU,GAAG;AAC1B,eAAa,YAAY,KAAK,WAAW,QAAQ,OAAO,KAAK,CAAC;AAChE;AAEA,IAAI,WAAW,OAAO,GAAG;AACvB,eAAa,SAAS,KAAK,WAAW,QAAQ,OAAO,YAAY,CAAC;AACpE;AAEA,QAAQ,IAAI,8BAA8B,OAAO,sBAAsB;"}
1
+ {"version":3,"file":"claude-context.js","names":[],"sources":["../../src/cli/claude-context.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI script to install agent context for @happyvertical/logger\n * Run the published context installer binary for this package.\n */\nimport { copyFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst Dirname = dirname(fileURLToPath(import.meta.url));\nconst pkgRoot = join(Dirname, '../..');\nconst targetDir = join(process.cwd(), '.claude');\n\nif (!existsSync(targetDir)) {\n mkdirSync(targetDir, { recursive: true });\n}\n\nconst pkgName = 'logger';\nconst agentMdSrc = existsSync(join(pkgRoot, 'AGENT.md'))\n ? join(pkgRoot, 'AGENT.md')\n : join(pkgRoot, 'CLAUDE.md');\nconst metaSrc = existsSync(join(pkgRoot, 'metadata.json'))\n ? join(pkgRoot, 'metadata.json')\n : join(pkgRoot, '.claude-meta.json');\n\nif (existsSync(agentMdSrc)) {\n copyFileSync(agentMdSrc, join(targetDir, `have-${pkgName}.md`));\n}\n\nif (existsSync(metaSrc)) {\n copyFileSync(metaSrc, join(targetDir, `have-${pkgName}.meta.json`));\n}\n\nconsole.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);\n"],"mappings":";;;;;;;;;AAUA,IAAM,UAAU,KADA,QAAQ,cAAc,OAAO,KAAK,GAAG,CAChC,GAAS,OAAO;AACrC,IAAM,YAAY,KAAK,QAAQ,IAAI,GAAG,SAAS;AAE/C,IAAI,CAAC,WAAW,SAAS,GACvB,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAG1C,IAAM,UAAU;AAChB,IAAM,aAAa,WAAW,KAAK,SAAS,UAAU,CAAC,IACnD,KAAK,SAAS,UAAU,IACxB,KAAK,SAAS,WAAW;AAC7B,IAAM,UAAU,WAAW,KAAK,SAAS,eAAe,CAAC,IACrD,KAAK,SAAS,eAAe,IAC7B,KAAK,SAAS,mBAAmB;AAErC,IAAI,WAAW,UAAU,GACvB,aAAa,YAAY,KAAK,WAAW,QAAQ,QAAQ,IAAI,CAAC;AAGhE,IAAI,WAAW,OAAO,GACpB,aAAa,SAAS,KAAK,WAAW,QAAQ,QAAQ,WAAW,CAAC;AAGpE,QAAQ,IAAI,8BAA8B,QAAQ,qBAAqB"}
package/dist/index.js CHANGED
@@ -1,155 +1,186 @@
1
1
  import { loadEnvConfig } from "@happyvertical/utils";
2
- class LoggerAdapter {
3
- constructor(logger) {
4
- this.logger = logger;
5
- }
6
- /**
7
- * Handle a signal and log appropriately
8
- *
9
- * @param signal - Signal to log
10
- */
11
- async handle(signal) {
12
- const context = {
13
- id: signal.id,
14
- objectId: signal.objectId,
15
- className: signal.className,
16
- method: signal.method,
17
- timestamp: signal.timestamp
18
- };
19
- if (signal.duration !== void 0) {
20
- context.duration = signal.duration;
21
- }
22
- if (signal.metadata) {
23
- context.metadata = signal.metadata;
24
- }
25
- switch (signal.type) {
26
- case "start":
27
- this.logger.debug(
28
- `${signal.className}.${signal.method}() started`,
29
- context
30
- );
31
- break;
32
- case "step":
33
- this.logger.debug(
34
- `${signal.className}.${signal.method}() step: ${signal.step || "unknown"}`,
35
- context
36
- );
37
- break;
38
- case "end":
39
- this.logger.info(
40
- `${signal.className}.${signal.method}() completed in ${signal.duration}ms`,
41
- {
42
- ...context,
43
- result: signal.result !== void 0 ? "present" : "none"
44
- }
45
- );
46
- break;
47
- case "error":
48
- this.logger.error(
49
- `${signal.className}.${signal.method}() failed: ${signal.error?.message || "Unknown error"}`,
50
- {
51
- ...context,
52
- error: signal.error ? {
53
- message: signal.error.message,
54
- name: signal.error.name,
55
- stack: signal.error.stack
56
- } : void 0
57
- }
58
- );
59
- break;
60
- }
61
- }
62
- }
63
- class ConsoleLogger {
64
- constructor(level = "info") {
65
- this.level = level;
66
- }
67
- static LEVELS = [
68
- "debug",
69
- "info",
70
- "warn",
71
- "error"
72
- ];
73
- /**
74
- * Check if a log level should be output
75
- *
76
- * @param level - Log level to check
77
- * @returns True if level meets threshold
78
- */
79
- shouldLog(level) {
80
- const currentIndex = ConsoleLogger.LEVELS.indexOf(this.level);
81
- const messageIndex = ConsoleLogger.LEVELS.indexOf(level);
82
- return messageIndex >= currentIndex;
83
- }
84
- /**
85
- * Format context for console output
86
- *
87
- * @param context - Structured metadata
88
- * @returns Formatted context string
89
- */
90
- formatContext(context) {
91
- if (!context || Object.keys(context).length === 0) {
92
- return "";
93
- }
94
- return ` ${JSON.stringify(context)}`;
95
- }
96
- debug(message, context) {
97
- if (this.shouldLog("debug")) {
98
- console.debug(`[DEBUG] ${message}${this.formatContext(context)}`);
99
- }
100
- }
101
- info(message, context) {
102
- if (this.shouldLog("info")) {
103
- console.info(`[INFO] ${message}${this.formatContext(context)}`);
104
- }
105
- }
106
- warn(message, context) {
107
- if (this.shouldLog("warn")) {
108
- console.warn(`[WARN] ${message}${this.formatContext(context)}`);
109
- }
110
- }
111
- error(message, context) {
112
- if (this.shouldLog("error")) {
113
- console.error(`[ERROR] ${message}${this.formatContext(context)}`);
114
- }
115
- }
116
- }
117
- class NoopLogger {
118
- debug(_message, _context) {
119
- }
120
- info(_message, _context) {
121
- }
122
- warn(_message, _context) {
123
- }
124
- error(_message, _context) {
125
- }
126
- }
2
+ //#region src/adapter.ts
3
+ /**
4
+ * Logger Adapter - Converts signals to structured log messages
5
+ *
6
+ * Transforms signals from the SMRT framework into structured log entries.
7
+ * Each signal type is mapped to an appropriate log level:
8
+ * - start → debug
9
+ * - step debug
10
+ * - end → info
11
+ * - error → error
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const logger = new ConsoleLogger('info');
16
+ * const adapter = new LoggerAdapter(logger);
17
+ * signalBus.register(adapter);
18
+ * ```
19
+ */
20
+ var LoggerAdapter = class {
21
+ logger;
22
+ constructor(logger) {
23
+ this.logger = logger;
24
+ }
25
+ /**
26
+ * Handle a signal and log appropriately
27
+ *
28
+ * @param signal - Signal to log
29
+ */
30
+ async handle(signal) {
31
+ const context = {
32
+ id: signal.id,
33
+ objectId: signal.objectId,
34
+ className: signal.className,
35
+ method: signal.method,
36
+ timestamp: signal.timestamp
37
+ };
38
+ if (signal.duration !== void 0) context.duration = signal.duration;
39
+ if (signal.metadata) context.metadata = signal.metadata;
40
+ switch (signal.type) {
41
+ case "start":
42
+ this.logger.debug(`${signal.className}.${signal.method}() started`, context);
43
+ break;
44
+ case "step":
45
+ this.logger.debug(`${signal.className}.${signal.method}() step: ${signal.step || "unknown"}`, context);
46
+ break;
47
+ case "end":
48
+ this.logger.info(`${signal.className}.${signal.method}() completed in ${signal.duration}ms`, {
49
+ ...context,
50
+ result: signal.result !== void 0 ? "present" : "none"
51
+ });
52
+ break;
53
+ case "error":
54
+ this.logger.error(`${signal.className}.${signal.method}() failed: ${signal.error?.message || "Unknown error"}`, {
55
+ ...context,
56
+ error: signal.error ? {
57
+ message: signal.error.message,
58
+ name: signal.error.name,
59
+ stack: signal.error.stack
60
+ } : void 0
61
+ });
62
+ break;
63
+ }
64
+ }
65
+ };
66
+ //#endregion
67
+ //#region src/console.ts
68
+ /**
69
+ * Console-based logger with level filtering
70
+ *
71
+ * Logs are written to console with appropriate severity levels.
72
+ * Messages are only output if they meet the configured log level threshold.
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const logger = new ConsoleLogger('info');
77
+ * logger.debug('Debug message'); // Not output (below 'info')
78
+ * logger.info('Info message'); // Output
79
+ * logger.error('Error message'); // Output
80
+ * ```
81
+ */
82
+ var ConsoleLogger = class ConsoleLogger {
83
+ level;
84
+ static LEVELS = [
85
+ "debug",
86
+ "info",
87
+ "warn",
88
+ "error"
89
+ ];
90
+ constructor(level = "info") {
91
+ this.level = level;
92
+ }
93
+ /**
94
+ * Check if a log level should be output
95
+ *
96
+ * @param level - Log level to check
97
+ * @returns True if level meets threshold
98
+ */
99
+ shouldLog(level) {
100
+ const currentIndex = ConsoleLogger.LEVELS.indexOf(this.level);
101
+ return ConsoleLogger.LEVELS.indexOf(level) >= currentIndex;
102
+ }
103
+ /**
104
+ * Format context for console output
105
+ *
106
+ * @param context - Structured metadata
107
+ * @returns Formatted context string
108
+ */
109
+ formatContext(context) {
110
+ if (!context || Object.keys(context).length === 0) return "";
111
+ return ` ${JSON.stringify(context)}`;
112
+ }
113
+ debug(message, context) {
114
+ if (this.shouldLog("debug")) console.debug(`[DEBUG] ${message}${this.formatContext(context)}`);
115
+ }
116
+ info(message, context) {
117
+ if (this.shouldLog("info")) console.info(`[INFO] ${message}${this.formatContext(context)}`);
118
+ }
119
+ warn(message, context) {
120
+ if (this.shouldLog("warn")) console.warn(`[WARN] ${message}${this.formatContext(context)}`);
121
+ }
122
+ error(message, context) {
123
+ if (this.shouldLog("error")) console.error(`[ERROR] ${message}${this.formatContext(context)}`);
124
+ }
125
+ };
126
+ //#endregion
127
+ //#region src/index.ts
128
+ /**
129
+ * No-op logger that discards all log messages
130
+ *
131
+ * Used when logging is disabled (config: false)
132
+ */
133
+ var NoopLogger = class {
134
+ debug(_message, _context) {}
135
+ info(_message, _context) {}
136
+ warn(_message, _context) {}
137
+ error(_message, _context) {}
138
+ };
139
+ /**
140
+ * Create a logger from configuration
141
+ *
142
+ * Supports environment variable configuration via HAVE_LOGGER_LEVEL.
143
+ * User-provided options take precedence over environment variables.
144
+ *
145
+ * @param config - Logger configuration (boolean or object)
146
+ * @returns Configured logger instance
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * // Console logger with 'info' level (default)
151
+ * const logger1 = createLogger(true);
152
+ *
153
+ * // Console logger with level from HAVE_LOGGER_LEVEL env var
154
+ * process.env.HAVE_LOGGER_LEVEL = 'debug';
155
+ * const logger2 = createLogger(true); // Uses 'debug' from env
156
+ *
157
+ * // No-op logger (all log calls are discarded)
158
+ * const logger3 = createLogger(false);
159
+ *
160
+ * // Console logger with 'debug' level (overrides env)
161
+ * const logger4 = createLogger({ level: 'debug' });
162
+ *
163
+ * // Custom log level (user options take precedence)
164
+ * process.env.HAVE_LOGGER_LEVEL = 'info';
165
+ * const logger5 = createLogger({ level: 'warn' }); // Uses 'warn' not 'info'
166
+ * ```
167
+ */
127
168
  function createLogger(config) {
128
- if (typeof config === "boolean") {
129
- if (!config) {
130
- return new NoopLogger();
131
- }
132
- const envConfig = loadEnvConfig(
133
- {},
134
- {
135
- packageName: "logger",
136
- schema: { level: "string" }
137
- }
138
- );
139
- return new ConsoleLogger(envConfig.level || "info");
140
- }
141
- const mergedConfig = loadEnvConfig(config, {
142
- packageName: "logger",
143
- schema: { level: "string" }
144
- });
145
- const level = mergedConfig.level || "info";
146
- return new ConsoleLogger(level);
169
+ if (typeof config === "boolean") {
170
+ if (!config) return new NoopLogger();
171
+ return new ConsoleLogger(loadEnvConfig({}, {
172
+ packageName: "logger",
173
+ schema: { level: "string" }
174
+ }).level || "info");
175
+ }
176
+ return new ConsoleLogger(loadEnvConfig(config, {
177
+ packageName: "logger",
178
+ schema: { level: "string" }
179
+ }).level || "info");
147
180
  }
148
- const PACKAGE_VERSION_INITIALIZED = true;
149
- export {
150
- ConsoleLogger,
151
- LoggerAdapter,
152
- PACKAGE_VERSION_INITIALIZED,
153
- createLogger
154
- };
155
- //# sourceMappingURL=index.js.map
181
+ /** @internal */
182
+ var PACKAGE_VERSION_INITIALIZED = true;
183
+ //#endregion
184
+ export { ConsoleLogger, LoggerAdapter, PACKAGE_VERSION_INITIALIZED, createLogger };
185
+
186
+ //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/adapter.ts","../src/console.ts","../src/index.ts"],"sourcesContent":["/**\n * Signal adapter for structured logging\n */\n\nimport type { Logger } from './logger.js';\nimport type { Signal, SignalAdapter } from './signal-types.js';\n\n/**\n * Logger Adapter - Converts signals to structured log messages\n *\n * Transforms signals from the SMRT framework into structured log entries.\n * Each signal type is mapped to an appropriate log level:\n * - start → debug\n * - step → debug\n * - end → info\n * - error → error\n *\n * @example\n * ```typescript\n * const logger = new ConsoleLogger('info');\n * const adapter = new LoggerAdapter(logger);\n * signalBus.register(adapter);\n * ```\n */\nexport class LoggerAdapter implements SignalAdapter {\n constructor(private logger: Logger) {}\n\n /**\n * Handle a signal and log appropriately\n *\n * @param signal - Signal to log\n */\n async handle(signal: Signal): Promise<void> {\n const context: Record<string, unknown> = {\n id: signal.id,\n objectId: signal.objectId,\n className: signal.className,\n method: signal.method,\n timestamp: signal.timestamp,\n };\n\n // Add optional fields if present\n if (signal.duration !== undefined) {\n context.duration = signal.duration;\n }\n\n if (signal.metadata) {\n context.metadata = signal.metadata;\n }\n\n switch (signal.type) {\n case 'start':\n this.logger.debug(\n `${signal.className}.${signal.method}() started`,\n context,\n );\n break;\n\n case 'step':\n this.logger.debug(\n `${signal.className}.${signal.method}() step: ${signal.step || 'unknown'}`,\n context,\n );\n break;\n\n case 'end':\n this.logger.info(\n `${signal.className}.${signal.method}() completed in ${signal.duration}ms`,\n {\n ...context,\n result: signal.result !== undefined ? 'present' : 'none',\n },\n );\n break;\n\n case 'error':\n this.logger.error(\n `${signal.className}.${signal.method}() failed: ${signal.error?.message || 'Unknown error'}`,\n {\n ...context,\n error: signal.error\n ? {\n message: signal.error.message,\n name: signal.error.name,\n stack: signal.error.stack,\n }\n : undefined,\n },\n );\n break;\n }\n }\n}\n","/**\n * Console logger implementation with configurable log levels\n */\n\nimport type { Logger, LogLevel } from './logger.js';\n\n/**\n * Console-based logger with level filtering\n *\n * Logs are written to console with appropriate severity levels.\n * Messages are only output if they meet the configured log level threshold.\n *\n * @example\n * ```typescript\n * const logger = new ConsoleLogger('info');\n * logger.debug('Debug message'); // Not output (below 'info')\n * logger.info('Info message'); // Output\n * logger.error('Error message'); // Output\n * ```\n */\nexport class ConsoleLogger implements Logger {\n private static readonly LEVELS: ReadonlyArray<LogLevel> = [\n 'debug',\n 'info',\n 'warn',\n 'error',\n ];\n\n constructor(private level: LogLevel = 'info') {}\n\n /**\n * Check if a log level should be output\n *\n * @param level - Log level to check\n * @returns True if level meets threshold\n */\n private shouldLog(level: LogLevel): boolean {\n const currentIndex = ConsoleLogger.LEVELS.indexOf(this.level);\n const messageIndex = ConsoleLogger.LEVELS.indexOf(level);\n return messageIndex >= currentIndex;\n }\n\n /**\n * Format context for console output\n *\n * @param context - Structured metadata\n * @returns Formatted context string\n */\n private formatContext(context?: Record<string, unknown>): string {\n if (!context || Object.keys(context).length === 0) {\n return '';\n }\n return ` ${JSON.stringify(context)}`;\n }\n\n debug(message: string, context?: Record<string, unknown>): void {\n if (this.shouldLog('debug')) {\n console.debug(`[DEBUG] ${message}${this.formatContext(context)}`);\n }\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n if (this.shouldLog('info')) {\n console.info(`[INFO] ${message}${this.formatContext(context)}`);\n }\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n if (this.shouldLog('warn')) {\n console.warn(`[WARN] ${message}${this.formatContext(context)}`);\n }\n }\n\n error(message: string, context?: Record<string, unknown>): void {\n if (this.shouldLog('error')) {\n console.error(`[ERROR] ${message}${this.formatContext(context)}`);\n }\n }\n}\n","/**\n * @happyvertical/logger - Structured logging for HAVE SDK\n *\n * Provides a structured logging interface with signal adapter integration.\n * Supports configurable log levels and console output.\n *\n * @example\n * ```typescript\n * import { createLogger, LoggerAdapter } from '@happyvertical/logger';\n *\n * // Create logger\n * const logger = createLogger({ level: 'info' });\n * logger.info('Application started');\n *\n * // Integrate with signals\n * const adapter = new LoggerAdapter(logger);\n * ```\n */\n\nexport { LoggerAdapter } from './adapter.js';\nexport { ConsoleLogger } from './console.js';\nexport type { Logger, LoggerConfig, LogLevel } from './logger.js';\nexport type { Signal, SignalAdapter, SignalType } from './signal-types.js';\n\nimport { loadEnvConfig } from '@happyvertical/utils';\nimport { ConsoleLogger } from './console.js';\nimport type { Logger, LoggerConfig, LogLevel } from './logger.js';\n\n/**\n * No-op logger that discards all log messages\n *\n * Used when logging is disabled (config: false)\n */\nclass NoopLogger implements Logger {\n debug(_message: string, _context?: Record<string, unknown>): void {\n // No-op\n }\n\n info(_message: string, _context?: Record<string, unknown>): void {\n // No-op\n }\n\n warn(_message: string, _context?: Record<string, unknown>): void {\n // No-op\n }\n\n error(_message: string, _context?: Record<string, unknown>): void {\n // No-op\n }\n}\n\n/**\n * Create a logger from configuration\n *\n * Supports environment variable configuration via HAVE_LOGGER_LEVEL.\n * User-provided options take precedence over environment variables.\n *\n * @param config - Logger configuration (boolean or object)\n * @returns Configured logger instance\n *\n * @example\n * ```typescript\n * // Console logger with 'info' level (default)\n * const logger1 = createLogger(true);\n *\n * // Console logger with level from HAVE_LOGGER_LEVEL env var\n * process.env.HAVE_LOGGER_LEVEL = 'debug';\n * const logger2 = createLogger(true); // Uses 'debug' from env\n *\n * // No-op logger (all log calls are discarded)\n * const logger3 = createLogger(false);\n *\n * // Console logger with 'debug' level (overrides env)\n * const logger4 = createLogger({ level: 'debug' });\n *\n * // Custom log level (user options take precedence)\n * process.env.HAVE_LOGGER_LEVEL = 'info';\n * const logger5 = createLogger({ level: 'warn' }); // Uses 'warn' not 'info'\n * ```\n */\nexport function createLogger(config: LoggerConfig): Logger {\n if (typeof config === 'boolean') {\n // When false, return no-op logger that discards all messages\n if (!config) {\n return new NoopLogger();\n }\n\n // Check HAVE_LOGGER_LEVEL env var when config is true\n const envConfig = loadEnvConfig<{ level?: LogLevel }>(\n {},\n {\n packageName: 'logger',\n schema: { level: 'string' },\n },\n );\n\n return new ConsoleLogger(envConfig.level || 'info');\n }\n\n // Merge user config with env vars (user options take precedence)\n const mergedConfig = loadEnvConfig<{ level?: LogLevel }>(config, {\n packageName: 'logger',\n schema: { level: 'string' },\n });\n\n const level = mergedConfig.level || 'info';\n return new ConsoleLogger(level);\n}\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"names":["ConsoleLogger"],"mappings":";AAwBO,MAAM,cAAuC;AAAA,EAClD,YAAoB,QAAgB;AAAhB,SAAA,SAAA;AAAA,EAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrC,MAAM,OAAO,QAA+B;AAC1C,UAAM,UAAmC;AAAA,MACvC,IAAI,OAAO;AAAA,MACX,UAAU,OAAO;AAAA,MACjB,WAAW,OAAO;AAAA,MAClB,QAAQ,OAAO;AAAA,MACf,WAAW,OAAO;AAAA,IAAA;AAIpB,QAAI,OAAO,aAAa,QAAW;AACjC,cAAQ,WAAW,OAAO;AAAA,IAC5B;AAEA,QAAI,OAAO,UAAU;AACnB,cAAQ,WAAW,OAAO;AAAA,IAC5B;AAEA,YAAQ,OAAO,MAAA;AAAA,MACb,KAAK;AACH,aAAK,OAAO;AAAA,UACV,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM;AAAA,UACpC;AAAA,QAAA;AAEF;AAAA,MAEF,KAAK;AACH,aAAK,OAAO;AAAA,UACV,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM,YAAY,OAAO,QAAQ,SAAS;AAAA,UACxE;AAAA,QAAA;AAEF;AAAA,MAEF,KAAK;AACH,aAAK,OAAO;AAAA,UACV,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM,mBAAmB,OAAO,QAAQ;AAAA,UACtE;AAAA,YACE,GAAG;AAAA,YACH,QAAQ,OAAO,WAAW,SAAY,YAAY;AAAA,UAAA;AAAA,QACpD;AAEF;AAAA,MAEF,KAAK;AACH,aAAK,OAAO;AAAA,UACV,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM,cAAc,OAAO,OAAO,WAAW,eAAe;AAAA,UAC1F;AAAA,YACE,GAAG;AAAA,YACH,OAAO,OAAO,QACV;AAAA,cACE,SAAS,OAAO,MAAM;AAAA,cACtB,MAAM,OAAO,MAAM;AAAA,cACnB,OAAO,OAAO,MAAM;AAAA,YAAA,IAEtB;AAAA,UAAA;AAAA,QACN;AAEF;AAAA,IAAA;AAAA,EAEN;AACF;ACxEO,MAAM,cAAgC;AAAA,EAQ3C,YAAoB,QAAkB,QAAQ;AAA1B,SAAA,QAAA;AAAA,EAA2B;AAAA,EAP/C,OAAwB,SAAkC;AAAA,IACxD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWM,UAAU,OAA0B;AAC1C,UAAM,eAAe,cAAc,OAAO,QAAQ,KAAK,KAAK;AAC5D,UAAM,eAAe,cAAc,OAAO,QAAQ,KAAK;AACvD,WAAO,gBAAgB;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,cAAc,SAA2C;AAC/D,QAAI,CAAC,WAAW,OAAO,KAAK,OAAO,EAAE,WAAW,GAAG;AACjD,aAAO;AAAA,IACT;AACA,WAAO,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,EACpC;AAAA,EAEA,MAAM,SAAiB,SAAyC;AAC9D,QAAI,KAAK,UAAU,OAAO,GAAG;AAC3B,cAAQ,MAAM,WAAW,OAAO,GAAG,KAAK,cAAc,OAAO,CAAC,EAAE;AAAA,IAClE;AAAA,EACF;AAAA,EAEA,KAAK,SAAiB,SAAyC;AAC7D,QAAI,KAAK,UAAU,MAAM,GAAG;AAC1B,cAAQ,KAAK,UAAU,OAAO,GAAG,KAAK,cAAc,OAAO,CAAC,EAAE;AAAA,IAChE;AAAA,EACF;AAAA,EAEA,KAAK,SAAiB,SAAyC;AAC7D,QAAI,KAAK,UAAU,MAAM,GAAG;AAC1B,cAAQ,KAAK,UAAU,OAAO,GAAG,KAAK,cAAc,OAAO,CAAC,EAAE;AAAA,IAChE;AAAA,EACF;AAAA,EAEA,MAAM,SAAiB,SAAyC;AAC9D,QAAI,KAAK,UAAU,OAAO,GAAG;AAC3B,cAAQ,MAAM,WAAW,OAAO,GAAG,KAAK,cAAc,OAAO,CAAC,EAAE;AAAA,IAClE;AAAA,EACF;AACF;AC7CA,MAAM,WAA6B;AAAA,EACjC,MAAM,UAAkB,UAA0C;AAAA,EAElE;AAAA,EAEA,KAAK,UAAkB,UAA0C;AAAA,EAEjE;AAAA,EAEA,KAAK,UAAkB,UAA0C;AAAA,EAEjE;AAAA,EAEA,MAAM,UAAkB,UAA0C;AAAA,EAElE;AACF;AA+BO,SAAS,aAAa,QAA8B;AACzD,MAAI,OAAO,WAAW,WAAW;AAE/B,QAAI,CAAC,QAAQ;AACX,aAAO,IAAI,WAAA;AAAA,IACb;AAGA,UAAM,YAAY;AAAA,MAChB,CAAA;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,OAAO,SAAA;AAAA,MAAS;AAAA,IAC5B;AAGF,WAAO,IAAIA,cAAc,UAAU,SAAS,MAAM;AAAA,EACpD;AAGA,QAAM,eAAe,cAAoC,QAAQ;AAAA,IAC/D,aAAa;AAAA,IACb,QAAQ,EAAE,OAAO,SAAA;AAAA,EAAS,CAC3B;AAED,QAAM,QAAQ,aAAa,SAAS;AACpC,SAAO,IAAIA,cAAc,KAAK;AAChC;AAGO,MAAM,8BAA8B;"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/adapter.ts","../src/console.ts","../src/index.ts"],"sourcesContent":["/**\n * Signal adapter for structured logging\n */\n\nimport type { Logger } from './logger.js';\nimport type { Signal, SignalAdapter } from './signal-types.js';\n\n/**\n * Logger Adapter - Converts signals to structured log messages\n *\n * Transforms signals from the SMRT framework into structured log entries.\n * Each signal type is mapped to an appropriate log level:\n * - start → debug\n * - step → debug\n * - end → info\n * - error → error\n *\n * @example\n * ```typescript\n * const logger = new ConsoleLogger('info');\n * const adapter = new LoggerAdapter(logger);\n * signalBus.register(adapter);\n * ```\n */\nexport class LoggerAdapter implements SignalAdapter {\n constructor(private logger: Logger) {}\n\n /**\n * Handle a signal and log appropriately\n *\n * @param signal - Signal to log\n */\n async handle(signal: Signal): Promise<void> {\n const context: Record<string, unknown> = {\n id: signal.id,\n objectId: signal.objectId,\n className: signal.className,\n method: signal.method,\n timestamp: signal.timestamp,\n };\n\n // Add optional fields if present\n if (signal.duration !== undefined) {\n context.duration = signal.duration;\n }\n\n if (signal.metadata) {\n context.metadata = signal.metadata;\n }\n\n switch (signal.type) {\n case 'start':\n this.logger.debug(\n `${signal.className}.${signal.method}() started`,\n context,\n );\n break;\n\n case 'step':\n this.logger.debug(\n `${signal.className}.${signal.method}() step: ${signal.step || 'unknown'}`,\n context,\n );\n break;\n\n case 'end':\n this.logger.info(\n `${signal.className}.${signal.method}() completed in ${signal.duration}ms`,\n {\n ...context,\n result: signal.result !== undefined ? 'present' : 'none',\n },\n );\n break;\n\n case 'error':\n this.logger.error(\n `${signal.className}.${signal.method}() failed: ${signal.error?.message || 'Unknown error'}`,\n {\n ...context,\n error: signal.error\n ? {\n message: signal.error.message,\n name: signal.error.name,\n stack: signal.error.stack,\n }\n : undefined,\n },\n );\n break;\n }\n }\n}\n","/**\n * Console logger implementation with configurable log levels\n */\n\nimport type { Logger, LogLevel } from './logger.js';\n\n/**\n * Console-based logger with level filtering\n *\n * Logs are written to console with appropriate severity levels.\n * Messages are only output if they meet the configured log level threshold.\n *\n * @example\n * ```typescript\n * const logger = new ConsoleLogger('info');\n * logger.debug('Debug message'); // Not output (below 'info')\n * logger.info('Info message'); // Output\n * logger.error('Error message'); // Output\n * ```\n */\nexport class ConsoleLogger implements Logger {\n private static readonly LEVELS: ReadonlyArray<LogLevel> = [\n 'debug',\n 'info',\n 'warn',\n 'error',\n ];\n\n constructor(private level: LogLevel = 'info') {}\n\n /**\n * Check if a log level should be output\n *\n * @param level - Log level to check\n * @returns True if level meets threshold\n */\n private shouldLog(level: LogLevel): boolean {\n const currentIndex = ConsoleLogger.LEVELS.indexOf(this.level);\n const messageIndex = ConsoleLogger.LEVELS.indexOf(level);\n return messageIndex >= currentIndex;\n }\n\n /**\n * Format context for console output\n *\n * @param context - Structured metadata\n * @returns Formatted context string\n */\n private formatContext(context?: Record<string, unknown>): string {\n if (!context || Object.keys(context).length === 0) {\n return '';\n }\n return ` ${JSON.stringify(context)}`;\n }\n\n debug(message: string, context?: Record<string, unknown>): void {\n if (this.shouldLog('debug')) {\n console.debug(`[DEBUG] ${message}${this.formatContext(context)}`);\n }\n }\n\n info(message: string, context?: Record<string, unknown>): void {\n if (this.shouldLog('info')) {\n console.info(`[INFO] ${message}${this.formatContext(context)}`);\n }\n }\n\n warn(message: string, context?: Record<string, unknown>): void {\n if (this.shouldLog('warn')) {\n console.warn(`[WARN] ${message}${this.formatContext(context)}`);\n }\n }\n\n error(message: string, context?: Record<string, unknown>): void {\n if (this.shouldLog('error')) {\n console.error(`[ERROR] ${message}${this.formatContext(context)}`);\n }\n }\n}\n","/**\n * @happyvertical/logger - Structured logging for HAVE SDK\n *\n * Provides a structured logging interface with signal adapter integration.\n * Supports configurable log levels and console output.\n *\n * @example\n * ```typescript\n * import { createLogger, LoggerAdapter } from '@happyvertical/logger';\n *\n * // Create logger\n * const logger = createLogger({ level: 'info' });\n * logger.info('Application started');\n *\n * // Integrate with signals\n * const adapter = new LoggerAdapter(logger);\n * ```\n */\n\nexport { LoggerAdapter } from './adapter.js';\nexport { ConsoleLogger } from './console.js';\nexport type { Logger, LoggerConfig, LogLevel } from './logger.js';\nexport type { Signal, SignalAdapter, SignalType } from './signal-types.js';\n\nimport { loadEnvConfig } from '@happyvertical/utils';\nimport { ConsoleLogger } from './console.js';\nimport type { Logger, LoggerConfig, LogLevel } from './logger.js';\n\n/**\n * No-op logger that discards all log messages\n *\n * Used when logging is disabled (config: false)\n */\nclass NoopLogger implements Logger {\n debug(_message: string, _context?: Record<string, unknown>): void {\n // No-op\n }\n\n info(_message: string, _context?: Record<string, unknown>): void {\n // No-op\n }\n\n warn(_message: string, _context?: Record<string, unknown>): void {\n // No-op\n }\n\n error(_message: string, _context?: Record<string, unknown>): void {\n // No-op\n }\n}\n\n/**\n * Create a logger from configuration\n *\n * Supports environment variable configuration via HAVE_LOGGER_LEVEL.\n * User-provided options take precedence over environment variables.\n *\n * @param config - Logger configuration (boolean or object)\n * @returns Configured logger instance\n *\n * @example\n * ```typescript\n * // Console logger with 'info' level (default)\n * const logger1 = createLogger(true);\n *\n * // Console logger with level from HAVE_LOGGER_LEVEL env var\n * process.env.HAVE_LOGGER_LEVEL = 'debug';\n * const logger2 = createLogger(true); // Uses 'debug' from env\n *\n * // No-op logger (all log calls are discarded)\n * const logger3 = createLogger(false);\n *\n * // Console logger with 'debug' level (overrides env)\n * const logger4 = createLogger({ level: 'debug' });\n *\n * // Custom log level (user options take precedence)\n * process.env.HAVE_LOGGER_LEVEL = 'info';\n * const logger5 = createLogger({ level: 'warn' }); // Uses 'warn' not 'info'\n * ```\n */\nexport function createLogger(config: LoggerConfig): Logger {\n if (typeof config === 'boolean') {\n // When false, return no-op logger that discards all messages\n if (!config) {\n return new NoopLogger();\n }\n\n // Check HAVE_LOGGER_LEVEL env var when config is true\n const envConfig = loadEnvConfig<{ level?: LogLevel }>(\n {},\n {\n packageName: 'logger',\n schema: { level: 'string' },\n },\n );\n\n return new ConsoleLogger(envConfig.level || 'info');\n }\n\n // Merge user config with env vars (user options take precedence)\n const mergedConfig = loadEnvConfig<{ level?: LogLevel }>(config, {\n packageName: 'logger',\n schema: { level: 'string' },\n });\n\n const level = mergedConfig.level || 'info';\n return new ConsoleLogger(level);\n}\n\n/** @internal */\nexport const PACKAGE_VERSION_INITIALIZED = true;\n"],"mappings":"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"}
package/dist/sentry.js CHANGED
@@ -1,79 +1,104 @@
1
1
  import * as Sentry from "@sentry/node";
2
- class SentryAdapter {
3
- category;
4
- constructor(config) {
5
- this.category = config?.category ?? "smrt";
6
- }
7
- /**
8
- * Handle a signal by routing to Sentry
9
- *
10
- * Error signals are captured as exceptions. All other signal types
11
- * are added as breadcrumbs.
12
- *
13
- * @param signal - The signal to handle
14
- */
15
- async handle(signal) {
16
- if (signal.type === "error") {
17
- this.captureError(signal);
18
- } else {
19
- this.addBreadcrumb(signal);
20
- }
21
- }
22
- captureError(signal) {
23
- const error = signal.error ?? new Error("Unknown SMRT error");
24
- Sentry.withScope((scope) => {
25
- scope.setTag("smrt.class", signal.className);
26
- scope.setTag("smrt.method", signal.method);
27
- scope.setContext("smrt", {
28
- signalId: signal.id,
29
- objectId: signal.objectId,
30
- duration: signal.duration,
31
- ...signal.metadata
32
- });
33
- Sentry.captureException(error);
34
- });
35
- }
36
- addBreadcrumb(signal) {
37
- const levelMap = {
38
- start: "debug",
39
- step: "debug",
40
- end: "info",
41
- error: "error"
42
- };
43
- let message;
44
- switch (signal.type) {
45
- case "start":
46
- message = `${signal.className}.${signal.method}() started`;
47
- break;
48
- case "step":
49
- message = `${signal.className}.${signal.method}() step: ${signal.step || "unknown"}`;
50
- break;
51
- case "end":
52
- message = `${signal.className}.${signal.method}() completed in ${signal.duration}ms`;
53
- break;
54
- default:
55
- message = `${signal.className}.${signal.method}() ${signal.type}`;
56
- }
57
- Sentry.addBreadcrumb({
58
- category: this.category,
59
- message,
60
- level: levelMap[signal.type] || "info",
61
- data: {
62
- signalId: signal.id,
63
- objectId: signal.objectId,
64
- className: signal.className,
65
- method: signal.method,
66
- ...signal.duration !== void 0 && { duration: signal.duration },
67
- ...signal.metadata
68
- }
69
- });
70
- }
71
- }
2
+ //#region src/sentry-adapter.ts
3
+ /**
4
+ * Sentry signal adapter for error tracking
5
+ *
6
+ * Routes SMRT framework signals to Sentry/GlitchTip:
7
+ * - Error signals → Sentry.captureException with scope tags and context
8
+ * - Non-error signals Sentry.addBreadcrumb for debugging trail
9
+ *
10
+ * Consumer must install @sentry/node and call Sentry.init() before use.
11
+ */
12
+ /**
13
+ * Sentry signal adapter routes SMRT signals to Sentry/GlitchTip
14
+ *
15
+ * Error signals are captured as exceptions with scope tags and context.
16
+ * All other signal types are added as breadcrumbs for debugging trail.
17
+ * Requires `@sentry/node` to be installed and `Sentry.init()` called before use.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { createSentryAdapter } from '@happyvertical/logger/sentry';
22
+ *
23
+ * const adapter = createSentryAdapter({ category: 'myapp' });
24
+ * ```
25
+ */
26
+ var SentryAdapter = class {
27
+ category;
28
+ constructor(config) {
29
+ this.category = config?.category ?? "smrt";
30
+ }
31
+ /**
32
+ * Handle a signal by routing to Sentry
33
+ *
34
+ * Error signals are captured as exceptions. All other signal types
35
+ * are added as breadcrumbs.
36
+ *
37
+ * @param signal - The signal to handle
38
+ */
39
+ async handle(signal) {
40
+ if (signal.type === "error") this.captureError(signal);
41
+ else this.addBreadcrumb(signal);
42
+ }
43
+ captureError(signal) {
44
+ const error = signal.error ?? /* @__PURE__ */ new Error("Unknown SMRT error");
45
+ Sentry.withScope((scope) => {
46
+ scope.setTag("smrt.class", signal.className);
47
+ scope.setTag("smrt.method", signal.method);
48
+ scope.setContext("smrt", {
49
+ signalId: signal.id,
50
+ objectId: signal.objectId,
51
+ duration: signal.duration,
52
+ ...signal.metadata
53
+ });
54
+ Sentry.captureException(error);
55
+ });
56
+ }
57
+ addBreadcrumb(signal) {
58
+ const levelMap = {
59
+ start: "debug",
60
+ step: "debug",
61
+ end: "info",
62
+ error: "error"
63
+ };
64
+ let message;
65
+ switch (signal.type) {
66
+ case "start":
67
+ message = `${signal.className}.${signal.method}() started`;
68
+ break;
69
+ case "step":
70
+ message = `${signal.className}.${signal.method}() step: ${signal.step || "unknown"}`;
71
+ break;
72
+ case "end":
73
+ message = `${signal.className}.${signal.method}() completed in ${signal.duration}ms`;
74
+ break;
75
+ default: message = `${signal.className}.${signal.method}() ${signal.type}`;
76
+ }
77
+ Sentry.addBreadcrumb({
78
+ category: this.category,
79
+ message,
80
+ level: levelMap[signal.type] || "info",
81
+ data: {
82
+ signalId: signal.id,
83
+ objectId: signal.objectId,
84
+ className: signal.className,
85
+ method: signal.method,
86
+ ...signal.duration !== void 0 && { duration: signal.duration },
87
+ ...signal.metadata
88
+ }
89
+ });
90
+ }
91
+ };
92
+ /**
93
+ * Create a Sentry signal adapter
94
+ *
95
+ * @param config - Optional configuration (breadcrumb category)
96
+ * @returns Configured SentryAdapter instance
97
+ */
72
98
  function createSentryAdapter(config) {
73
- return new SentryAdapter(config);
99
+ return new SentryAdapter(config);
74
100
  }
75
- export {
76
- SentryAdapter,
77
- createSentryAdapter
78
- };
79
- //# sourceMappingURL=sentry.js.map
101
+ //#endregion
102
+ export { SentryAdapter, createSentryAdapter };
103
+
104
+ //# sourceMappingURL=sentry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sentry.js","sources":["../src/sentry-adapter.ts"],"sourcesContent":["/**\n * Sentry signal adapter for error tracking\n *\n * Routes SMRT framework signals to Sentry/GlitchTip:\n * - Error signals → Sentry.captureException with scope tags and context\n * - Non-error signals → Sentry.addBreadcrumb for debugging trail\n *\n * Consumer must install @sentry/node and call Sentry.init() before use.\n */\n\nimport * as Sentry from '@sentry/node';\nimport type { Signal, SignalAdapter } from './signal-types.js';\n\n/**\n * Configuration for the Sentry signal adapter\n */\nexport interface SentryAdapterConfig {\n /** Breadcrumb category (default: 'smrt') */\n category?: string;\n}\n\n/**\n * Sentry signal adapter — routes SMRT signals to Sentry/GlitchTip\n *\n * Error signals are captured as exceptions with scope tags and context.\n * All other signal types are added as breadcrumbs for debugging trail.\n * Requires `@sentry/node` to be installed and `Sentry.init()` called before use.\n *\n * @example\n * ```typescript\n * import { createSentryAdapter } from '@happyvertical/logger/sentry';\n *\n * const adapter = createSentryAdapter({ category: 'myapp' });\n * ```\n */\nexport class SentryAdapter implements SignalAdapter {\n private category: string;\n\n constructor(config?: SentryAdapterConfig) {\n this.category = config?.category ?? 'smrt';\n }\n\n /**\n * Handle a signal by routing to Sentry\n *\n * Error signals are captured as exceptions. All other signal types\n * are added as breadcrumbs.\n *\n * @param signal - The signal to handle\n */\n async handle(signal: Signal): Promise<void> {\n if (signal.type === 'error') {\n this.captureError(signal);\n } else {\n this.addBreadcrumb(signal);\n }\n }\n\n private captureError(signal: Signal): void {\n const error = signal.error ?? new Error('Unknown SMRT error');\n\n Sentry.withScope((scope) => {\n scope.setTag('smrt.class', signal.className);\n scope.setTag('smrt.method', signal.method);\n\n scope.setContext('smrt', {\n signalId: signal.id,\n objectId: signal.objectId,\n duration: signal.duration,\n ...signal.metadata,\n });\n\n Sentry.captureException(error);\n });\n }\n\n private addBreadcrumb(signal: Signal): void {\n const levelMap: Record<string, Sentry.SeverityLevel> = {\n start: 'debug',\n step: 'debug',\n end: 'info',\n error: 'error',\n };\n\n let message: string;\n switch (signal.type) {\n case 'start':\n message = `${signal.className}.${signal.method}() started`;\n break;\n case 'step':\n message = `${signal.className}.${signal.method}() step: ${signal.step || 'unknown'}`;\n break;\n case 'end':\n message = `${signal.className}.${signal.method}() completed in ${signal.duration}ms`;\n break;\n default:\n message = `${signal.className}.${signal.method}() ${signal.type}`;\n }\n\n Sentry.addBreadcrumb({\n category: this.category,\n message,\n level: levelMap[signal.type] || 'info',\n data: {\n signalId: signal.id,\n objectId: signal.objectId,\n className: signal.className,\n method: signal.method,\n ...(signal.duration !== undefined && { duration: signal.duration }),\n ...signal.metadata,\n },\n });\n }\n}\n\n/**\n * Create a Sentry signal adapter\n *\n * @param config - Optional configuration (breadcrumb category)\n * @returns Configured SentryAdapter instance\n */\nexport function createSentryAdapter(\n config?: SentryAdapterConfig,\n): SentryAdapter {\n return new SentryAdapter(config);\n}\n"],"names":[],"mappings":";AAmCO,MAAM,cAAuC;AAAA,EAC1C;AAAA,EAER,YAAY,QAA8B;AACxC,SAAK,WAAW,QAAQ,YAAY;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,OAAO,QAA+B;AAC1C,QAAI,OAAO,SAAS,SAAS;AAC3B,WAAK,aAAa,MAAM;AAAA,IAC1B,OAAO;AACL,WAAK,cAAc,MAAM;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,aAAa,QAAsB;AACzC,UAAM,QAAQ,OAAO,SAAS,IAAI,MAAM,oBAAoB;AAE5D,WAAO,UAAU,CAAC,UAAU;AAC1B,YAAM,OAAO,cAAc,OAAO,SAAS;AAC3C,YAAM,OAAO,eAAe,OAAO,MAAM;AAEzC,YAAM,WAAW,QAAQ;AAAA,QACvB,UAAU,OAAO;AAAA,QACjB,UAAU,OAAO;AAAA,QACjB,UAAU,OAAO;AAAA,QACjB,GAAG,OAAO;AAAA,MAAA,CACX;AAED,aAAO,iBAAiB,KAAK;AAAA,IAC/B,CAAC;AAAA,EACH;AAAA,EAEQ,cAAc,QAAsB;AAC1C,UAAM,WAAiD;AAAA,MACrD,OAAO;AAAA,MACP,MAAM;AAAA,MACN,KAAK;AAAA,MACL,OAAO;AAAA,IAAA;AAGT,QAAI;AACJ,YAAQ,OAAO,MAAA;AAAA,MACb,KAAK;AACH,kBAAU,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM;AAC9C;AAAA,MACF,KAAK;AACH,kBAAU,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM,YAAY,OAAO,QAAQ,SAAS;AAClF;AAAA,MACF,KAAK;AACH,kBAAU,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM,mBAAmB,OAAO,QAAQ;AAChF;AAAA,MACF;AACE,kBAAU,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM,MAAM,OAAO,IAAI;AAAA,IAAA;AAGnE,WAAO,cAAc;AAAA,MACnB,UAAU,KAAK;AAAA,MACf;AAAA,MACA,OAAO,SAAS,OAAO,IAAI,KAAK;AAAA,MAChC,MAAM;AAAA,QACJ,UAAU,OAAO;AAAA,QACjB,UAAU,OAAO;AAAA,QACjB,WAAW,OAAO;AAAA,QAClB,QAAQ,OAAO;AAAA,QACf,GAAI,OAAO,aAAa,UAAa,EAAE,UAAU,OAAO,SAAA;AAAA,QACxD,GAAG,OAAO;AAAA,MAAA;AAAA,IACZ,CACD;AAAA,EACH;AACF;AAQO,SAAS,oBACd,QACe;AACf,SAAO,IAAI,cAAc,MAAM;AACjC;"}
1
+ {"version":3,"file":"sentry.js","names":[],"sources":["../src/sentry-adapter.ts"],"sourcesContent":["/**\n * Sentry signal adapter for error tracking\n *\n * Routes SMRT framework signals to Sentry/GlitchTip:\n * - Error signals → Sentry.captureException with scope tags and context\n * - Non-error signals → Sentry.addBreadcrumb for debugging trail\n *\n * Consumer must install @sentry/node and call Sentry.init() before use.\n */\n\nimport * as Sentry from '@sentry/node';\nimport type { Signal, SignalAdapter } from './signal-types.js';\n\n/**\n * Configuration for the Sentry signal adapter\n */\nexport interface SentryAdapterConfig {\n /** Breadcrumb category (default: 'smrt') */\n category?: string;\n}\n\n/**\n * Sentry signal adapter — routes SMRT signals to Sentry/GlitchTip\n *\n * Error signals are captured as exceptions with scope tags and context.\n * All other signal types are added as breadcrumbs for debugging trail.\n * Requires `@sentry/node` to be installed and `Sentry.init()` called before use.\n *\n * @example\n * ```typescript\n * import { createSentryAdapter } from '@happyvertical/logger/sentry';\n *\n * const adapter = createSentryAdapter({ category: 'myapp' });\n * ```\n */\nexport class SentryAdapter implements SignalAdapter {\n private category: string;\n\n constructor(config?: SentryAdapterConfig) {\n this.category = config?.category ?? 'smrt';\n }\n\n /**\n * Handle a signal by routing to Sentry\n *\n * Error signals are captured as exceptions. All other signal types\n * are added as breadcrumbs.\n *\n * @param signal - The signal to handle\n */\n async handle(signal: Signal): Promise<void> {\n if (signal.type === 'error') {\n this.captureError(signal);\n } else {\n this.addBreadcrumb(signal);\n }\n }\n\n private captureError(signal: Signal): void {\n const error = signal.error ?? new Error('Unknown SMRT error');\n\n Sentry.withScope((scope) => {\n scope.setTag('smrt.class', signal.className);\n scope.setTag('smrt.method', signal.method);\n\n scope.setContext('smrt', {\n signalId: signal.id,\n objectId: signal.objectId,\n duration: signal.duration,\n ...signal.metadata,\n });\n\n Sentry.captureException(error);\n });\n }\n\n private addBreadcrumb(signal: Signal): void {\n const levelMap: Record<string, Sentry.SeverityLevel> = {\n start: 'debug',\n step: 'debug',\n end: 'info',\n error: 'error',\n };\n\n let message: string;\n switch (signal.type) {\n case 'start':\n message = `${signal.className}.${signal.method}() started`;\n break;\n case 'step':\n message = `${signal.className}.${signal.method}() step: ${signal.step || 'unknown'}`;\n break;\n case 'end':\n message = `${signal.className}.${signal.method}() completed in ${signal.duration}ms`;\n break;\n default:\n message = `${signal.className}.${signal.method}() ${signal.type}`;\n }\n\n Sentry.addBreadcrumb({\n category: this.category,\n message,\n level: levelMap[signal.type] || 'info',\n data: {\n signalId: signal.id,\n objectId: signal.objectId,\n className: signal.className,\n method: signal.method,\n ...(signal.duration !== undefined && { duration: signal.duration }),\n ...signal.metadata,\n },\n });\n }\n}\n\n/**\n * Create a Sentry signal adapter\n *\n * @param config - Optional configuration (breadcrumb category)\n * @returns Configured SentryAdapter instance\n */\nexport function createSentryAdapter(\n config?: SentryAdapterConfig,\n): SentryAdapter {\n return new SentryAdapter(config);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,IAAa,gBAAb,MAAoD;CAClD;CAEA,YAAY,QAA8B;EACxC,KAAK,WAAW,QAAQ,YAAY;CACtC;;;;;;;;;CAUA,MAAM,OAAO,QAA+B;EAC1C,IAAI,OAAO,SAAS,SAClB,KAAK,aAAa,MAAM;OAExB,KAAK,cAAc,MAAM;CAE7B;CAEA,aAAqB,QAAsB;EACzC,MAAM,QAAQ,OAAO,yBAAS,IAAI,MAAM,oBAAoB;EAE5D,OAAO,WAAW,UAAU;GAC1B,MAAM,OAAO,cAAc,OAAO,SAAS;GAC3C,MAAM,OAAO,eAAe,OAAO,MAAM;GAEzC,MAAM,WAAW,QAAQ;IACvB,UAAU,OAAO;IACjB,UAAU,OAAO;IACjB,UAAU,OAAO;IACjB,GAAG,OAAO;GACZ,CAAC;GAED,OAAO,iBAAiB,KAAK;EAC/B,CAAC;CACH;CAEA,cAAsB,QAAsB;EAC1C,MAAM,WAAiD;GACrD,OAAO;GACP,MAAM;GACN,KAAK;GACL,OAAO;EACT;EAEA,IAAI;EACJ,QAAQ,OAAO,MAAf;GACE,KAAK;IACH,UAAU,GAAG,OAAO,UAAU,GAAG,OAAO,OAAO;IAC/C;GACF,KAAK;IACH,UAAU,GAAG,OAAO,UAAU,GAAG,OAAO,OAAO,WAAW,OAAO,QAAQ;IACzE;GACF,KAAK;IACH,UAAU,GAAG,OAAO,UAAU,GAAG,OAAO,OAAO,kBAAkB,OAAO,SAAS;IACjF;GACF,SACE,UAAU,GAAG,OAAO,UAAU,GAAG,OAAO,OAAO,KAAK,OAAO;EAC/D;EAEA,OAAO,cAAc;GACnB,UAAU,KAAK;GACf;GACA,OAAO,SAAS,OAAO,SAAS;GAChC,MAAM;IACJ,UAAU,OAAO;IACjB,UAAU,OAAO;IACjB,WAAW,OAAO;IAClB,QAAQ,OAAO;IACf,GAAI,OAAO,aAAa,KAAA,KAAa,EAAE,UAAU,OAAO,SAAS;IACjE,GAAG,OAAO;GACZ;EACF,CAAC;CACH;AACF;;;;;;;AAQA,SAAgB,oBACd,QACe;CACf,OAAO,IAAI,cAAc,MAAM;AACjC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/logger",
3
- "version": "0.80.0",
3
+ "version": "0.80.1",
4
4
  "description": "Structured logging for HAVE SDK with signal adapter",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -38,14 +38,14 @@
38
38
  "access": "public"
39
39
  },
40
40
  "dependencies": {
41
- "@happyvertical/utils": "0.80.0"
41
+ "@happyvertical/utils": "0.80.1"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@sentry/node": "^10.49.0",
45
45
  "@types/node": "25.0.10",
46
- "typescript": "^5.9.3",
47
- "vite": "7.3.2",
48
- "vitest": "^4.1.5"
46
+ "typescript": "5.9.3",
47
+ "vite": "8.1.4",
48
+ "vitest": "4.1.10"
49
49
  },
50
50
  "keywords": [
51
51
  "logging",