@happyvertical/logger 0.74.8

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/AGENT.md ADDED
@@ -0,0 +1,33 @@
1
+ # @happyvertical/logger
2
+
3
+ <!-- BEGIN AGENT:GENERATED -->
4
+ ## Purpose
5
+ Structured logging for HAVE SDK with signal adapter
6
+
7
+ ## Package Map
8
+ - Package: `@happyvertical/logger`
9
+ - Hierarchy path: `@happyvertical/sdk > packages > logger`
10
+ - Workspace position: `18 of 30` local packages
11
+ - Internal dependencies: `@happyvertical/utils`
12
+ - Internal dependents: `@happyvertical/comfyui`, `@happyvertical/email`, `@happyvertical/encryption`, `@happyvertical/messages`, `@happyvertical/social`, `@happyvertical/video`
13
+ - Knowledge graph files: `AGENT.md`, `metadata.json`, `ecosystem-manifest.json`
14
+
15
+ ## Build & Test
16
+ ```bash
17
+ pnpm --filter @happyvertical/logger build
18
+ pnpm --filter @happyvertical/logger test
19
+ pnpm --filter @happyvertical/logger typecheck
20
+ ```
21
+
22
+ ## Agent Correction Loops
23
+ - If module resolution or export errors mention a workspace dependency, build the dependency first (`pnpm --filter @happyvertical/utils build`) and then rerun `pnpm --filter @happyvertical/logger build`.
24
+ - If you hit type-only regressions, run `pnpm --filter @happyvertical/logger typecheck` before rerunning the package build or tests to isolate the failing surface.
25
+ - If failures span multiple packages or Turborepo ordering looks wrong, run `pnpm build` and `pnpm typecheck` from the repo root before retrying package-scoped commands.
26
+
27
+ ## Ecosystem Relationships
28
+ - Provides: Structured logging for HAVE SDK with signal adapter
29
+ - Implements: none
30
+ - Requires: @happyvertical/utils, @sentry/node
31
+ - Stability: stable (Primary package surface is described as implemented and production-oriented.)
32
+ <!-- END AGENT:GENERATED -->
33
+
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright <2025> <Happy Vertical Corporation>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # @happyvertical/logger
2
+
3
+ Structured logging for HAVE SDK with signal adapter integration and optional Sentry error tracking. Provides a `Logger` interface with four severity levels, a console implementation with level filtering, and adapters that convert SMRT framework signals into log entries or Sentry breadcrumbs/exceptions.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @happyvertical/logger
9
+
10
+ # Optional: for Sentry integration
11
+ pnpm add @sentry/node
12
+ ```
13
+
14
+ Depends on `@happyvertical/utils`. Optional peer dependency on `@sentry/node` (for the `/sentry` subpath).
15
+
16
+ ## Basic Usage
17
+
18
+ ```typescript
19
+ import { createLogger } from '@happyvertical/logger';
20
+
21
+ // Console logger with default 'info' level
22
+ const logger = createLogger(true);
23
+
24
+ logger.info('Application started');
25
+ logger.debug('Verbose detail'); // Not output (below 'info')
26
+ logger.warn('High memory usage', { memoryMB: 512 });
27
+ logger.error('Connection failed', { host: 'localhost', port: 5432 });
28
+
29
+ // Configure a specific level
30
+ const debugLogger = createLogger({ level: 'debug' });
31
+
32
+ // No-op logger — all calls discarded, zero overhead
33
+ const silent = createLogger(false);
34
+ ```
35
+
36
+ ### Environment Variable
37
+
38
+ Set `HAVE_LOGGER_LEVEL` to `debug`, `info`, `warn`, or `error`. Applies when using `createLogger(true)` or `createLogger({})`. Explicit config takes precedence:
39
+
40
+ ```typescript
41
+ process.env.HAVE_LOGGER_LEVEL = 'debug';
42
+ const logger = createLogger(true); // uses 'debug' from env
43
+
44
+ const explicit = createLogger({ level: 'warn' }); // uses 'warn', ignores env
45
+ ```
46
+
47
+ ## Log Levels
48
+
49
+ `debug < info < warn < error` — messages below the configured threshold are discarded.
50
+
51
+ ```typescript
52
+ const logger = createLogger({ level: 'warn' });
53
+ logger.debug('ignored'); // not output
54
+ logger.info('ignored'); // not output
55
+ logger.warn('output'); // output
56
+ logger.error('output'); // output
57
+ ```
58
+
59
+ Output format: `[LEVEL] message {"key":"value"}`
60
+
61
+ ## Signal Adapter
62
+
63
+ `LoggerAdapter` converts SMRT framework signals into structured log entries:
64
+
65
+ ```typescript
66
+ import { createLogger, LoggerAdapter } from '@happyvertical/logger';
67
+
68
+ const logger = createLogger({ level: 'info' });
69
+ const adapter = new LoggerAdapter(logger);
70
+ // Register adapter with a signal bus to log operations automatically
71
+ ```
72
+
73
+ Signal type mapping: `start` → debug, `step` → debug, `end` → info, `error` → error.
74
+
75
+ ## Sentry Adapter
76
+
77
+ The `/sentry` subpath routes signals to Sentry/GlitchTip. Requires `@sentry/node` as a peer dependency.
78
+
79
+ ```typescript
80
+ import { createSentryAdapter } from '@happyvertical/logger/sentry';
81
+
82
+ const adapter = createSentryAdapter({ category: 'myapp' });
83
+ // Register adapter with a signal bus
84
+
85
+ // Error signals → Sentry.captureException with scope tags
86
+ // Non-error signals → Sentry.addBreadcrumb for debugging trail
87
+ ```
88
+
89
+ ## API Overview
90
+
91
+ ### Factory
92
+
93
+ | Function | Description |
94
+ |----------|-------------|
95
+ | `createLogger(config)` | Returns a `Logger` — `ConsoleLogger` when enabled, no-op when `false` |
96
+ | `createSentryAdapter(config?)` | Returns a `SentryAdapter` (from `/sentry` subpath) |
97
+
98
+ ### Classes
99
+
100
+ | Class | Description |
101
+ |-------|-------------|
102
+ | `ConsoleLogger` | Logger implementation with level filtering, writes to `console.*` |
103
+ | `LoggerAdapter` | Signal adapter that converts signals to structured log messages |
104
+ | `SentryAdapter` | Signal adapter that routes errors to Sentry and adds breadcrumbs |
105
+
106
+ ### Types
107
+
108
+ | Type | Description |
109
+ |------|-------------|
110
+ | `Logger` | Interface with `debug`, `info`, `warn`, `error` methods |
111
+ | `LogLevel` | `'debug' \| 'info' \| 'warn' \| 'error'` |
112
+ | `LoggerConfig` | `boolean \| { level?: LogLevel }` |
113
+ | `Signal` | Signal event from operation execution |
114
+ | `SignalAdapter` | Interface for handling signals |
115
+ | `SignalType` | `'start' \| 'step' \| 'end' \| 'error'` |
116
+ | `SentryAdapterConfig` | `{ category?: string }` |
117
+
118
+ ### Custom Logger
119
+
120
+ Implement the `Logger` interface for custom backends:
121
+
122
+ ```typescript
123
+ import type { Logger } from '@happyvertical/logger';
124
+
125
+ class MyLogger implements Logger {
126
+ debug(message: string, context?: Record<string, unknown>): void { /* ... */ }
127
+ info(message: string, context?: Record<string, unknown>): void { /* ... */ }
128
+ warn(message: string, context?: Record<string, unknown>): void { /* ... */ }
129
+ error(message: string, context?: Record<string, unknown>): void { /* ... */ }
130
+ }
131
+ ```
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,30 @@
1
+ import { Logger } from './logger.js';
2
+ import { Signal, SignalAdapter } from './signal-types.js';
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
+ export declare class LoggerAdapter implements SignalAdapter {
21
+ private logger;
22
+ constructor(logger: Logger);
23
+ /**
24
+ * Handle a signal and log appropriately
25
+ *
26
+ * @param signal - Signal to log
27
+ */
28
+ handle(signal: Signal): Promise<void>;
29
+ }
30
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE/D;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAc,YAAW,aAAa;IACrC,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAElC;;;;OAIG;IACG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA4D5C"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=claude-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-context.d.ts","sourceRoot":"","sources":["../../src/cli/claude-context.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync, mkdirSync, copyFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
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
+ }
20
+ console.log(`✓ Installed @happyvertical/${pkgName} context to .claude/`);
21
+ //# sourceMappingURL=claude-context.js.map
@@ -0,0 +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;"}
@@ -0,0 +1,39 @@
1
+ import { Logger, LogLevel } from './logger.js';
2
+ /**
3
+ * Console-based logger with level filtering
4
+ *
5
+ * Logs are written to console with appropriate severity levels.
6
+ * Messages are only output if they meet the configured log level threshold.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const logger = new ConsoleLogger('info');
11
+ * logger.debug('Debug message'); // Not output (below 'info')
12
+ * logger.info('Info message'); // Output
13
+ * logger.error('Error message'); // Output
14
+ * ```
15
+ */
16
+ export declare class ConsoleLogger implements Logger {
17
+ private level;
18
+ private static readonly LEVELS;
19
+ constructor(level?: LogLevel);
20
+ /**
21
+ * Check if a log level should be output
22
+ *
23
+ * @param level - Log level to check
24
+ * @returns True if level meets threshold
25
+ */
26
+ private shouldLog;
27
+ /**
28
+ * Format context for console output
29
+ *
30
+ * @param context - Structured metadata
31
+ * @returns Formatted context string
32
+ */
33
+ private formatContext;
34
+ debug(message: string, context?: Record<string, unknown>): void;
35
+ info(message: string, context?: Record<string, unknown>): void;
36
+ warn(message: string, context?: Record<string, unknown>): void;
37
+ error(message: string, context?: Record<string, unknown>): void;
38
+ }
39
+ //# sourceMappingURL=console.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../src/console.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAc,YAAW,MAAM;IAQ9B,OAAO,CAAC,KAAK;IAPzB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAK5B;gBAEkB,KAAK,GAAE,QAAiB;IAE5C;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAMjB;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAOrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM/D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM9D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAKhE"}
@@ -0,0 +1,56 @@
1
+ import { Logger, LoggerConfig } from './logger.js';
2
+ /**
3
+ * @happyvertical/logger - Structured logging for HAVE SDK
4
+ *
5
+ * Provides a structured logging interface with signal adapter integration.
6
+ * Supports configurable log levels and console output.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createLogger, LoggerAdapter } from '@happyvertical/logger';
11
+ *
12
+ * // Create logger
13
+ * const logger = createLogger({ level: 'info' });
14
+ * logger.info('Application started');
15
+ *
16
+ * // Integrate with signals
17
+ * const adapter = new LoggerAdapter(logger);
18
+ * ```
19
+ */
20
+ export { LoggerAdapter } from './adapter.js';
21
+ export { ConsoleLogger } from './console.js';
22
+ export type { Logger, LoggerConfig, LogLevel } from './logger.js';
23
+ export type { Signal, SignalAdapter, SignalType } from './signal-types.js';
24
+ /**
25
+ * Create a logger from configuration
26
+ *
27
+ * Supports environment variable configuration via HAVE_LOGGER_LEVEL.
28
+ * User-provided options take precedence over environment variables.
29
+ *
30
+ * @param config - Logger configuration (boolean or object)
31
+ * @returns Configured logger instance
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * // Console logger with 'info' level (default)
36
+ * const logger1 = createLogger(true);
37
+ *
38
+ * // Console logger with level from HAVE_LOGGER_LEVEL env var
39
+ * process.env.HAVE_LOGGER_LEVEL = 'debug';
40
+ * const logger2 = createLogger(true); // Uses 'debug' from env
41
+ *
42
+ * // No-op logger (all log calls are discarded)
43
+ * const logger3 = createLogger(false);
44
+ *
45
+ * // Console logger with 'debug' level (overrides env)
46
+ * const logger4 = createLogger({ level: 'debug' });
47
+ *
48
+ * // Custom log level (user options take precedence)
49
+ * process.env.HAVE_LOGGER_LEVEL = 'info';
50
+ * const logger5 = createLogger({ level: 'warn' }); // Uses 'warn' not 'info'
51
+ * ```
52
+ */
53
+ export declare function createLogger(config: LoggerConfig): Logger;
54
+ /** @internal */
55
+ export declare const PACKAGE_VERSION_INITIALIZED = true;
56
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAClE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAI3E,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAY,MAAM,aAAa,CAAC;AAyBlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CA2BzD;AAED,gBAAgB;AAChB,eAAO,MAAM,2BAA2B,OAAO,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,155 @@
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
+ }
127
+ 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);
147
+ }
148
+ const PACKAGE_VERSION_INITIALIZED = true;
149
+ export {
150
+ ConsoleLogger,
151
+ LoggerAdapter,
152
+ PACKAGE_VERSION_INITIALIZED,
153
+ createLogger
154
+ };
155
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Core logging interfaces and types
3
+ */
4
+ /**
5
+ * Log levels in order of severity
6
+ */
7
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
8
+ /**
9
+ * Logger configuration
10
+ * - boolean: true = console logger with 'info' level, false = disabled
11
+ * - object: configure logger with specific level
12
+ */
13
+ export type LoggerConfig = boolean | {
14
+ level?: LogLevel;
15
+ };
16
+ /**
17
+ * Structured logger interface
18
+ *
19
+ * All methods accept optional context for structured logging.
20
+ * Context should contain machine-readable metadata.
21
+ */
22
+ export interface Logger {
23
+ /**
24
+ * Log debug message (verbose development information)
25
+ *
26
+ * @param message - Human-readable log message
27
+ * @param context - Optional structured metadata
28
+ */
29
+ debug(message: string, context?: Record<string, unknown>): void;
30
+ /**
31
+ * Log informational message (normal operation)
32
+ *
33
+ * @param message - Human-readable log message
34
+ * @param context - Optional structured metadata
35
+ */
36
+ info(message: string, context?: Record<string, unknown>): void;
37
+ /**
38
+ * Log warning message (potential issues)
39
+ *
40
+ * @param message - Human-readable log message
41
+ * @param context - Optional structured metadata
42
+ */
43
+ warn(message: string, context?: Record<string, unknown>): void;
44
+ /**
45
+ * Log error message (failures and exceptions)
46
+ *
47
+ * @param message - Human-readable log message
48
+ * @param context - Optional structured metadata
49
+ */
50
+ error(message: string, context?: Record<string, unknown>): void;
51
+ }
52
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG;IAAE,KAAK,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB;;;;;OAKG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAEhE;;;;;OAKG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAE/D;;;;;OAKG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAE/D;;;;;OAKG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACjE"}
@@ -0,0 +1,45 @@
1
+ import { Signal, SignalAdapter } from './signal-types.js';
2
+ /**
3
+ * Configuration for the Sentry signal adapter
4
+ */
5
+ export interface SentryAdapterConfig {
6
+ /** Breadcrumb category (default: 'smrt') */
7
+ category?: string;
8
+ }
9
+ /**
10
+ * Sentry signal adapter — routes SMRT signals to Sentry/GlitchTip
11
+ *
12
+ * Error signals are captured as exceptions with scope tags and context.
13
+ * All other signal types are added as breadcrumbs for debugging trail.
14
+ * Requires `@sentry/node` to be installed and `Sentry.init()` called before use.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { createSentryAdapter } from '@happyvertical/logger/sentry';
19
+ *
20
+ * const adapter = createSentryAdapter({ category: 'myapp' });
21
+ * ```
22
+ */
23
+ export declare class SentryAdapter implements SignalAdapter {
24
+ private category;
25
+ constructor(config?: SentryAdapterConfig);
26
+ /**
27
+ * Handle a signal by routing to Sentry
28
+ *
29
+ * Error signals are captured as exceptions. All other signal types
30
+ * are added as breadcrumbs.
31
+ *
32
+ * @param signal - The signal to handle
33
+ */
34
+ handle(signal: Signal): Promise<void>;
35
+ private captureError;
36
+ private addBreadcrumb;
37
+ }
38
+ /**
39
+ * Create a Sentry signal adapter
40
+ *
41
+ * @param config - Optional configuration (breadcrumb category)
42
+ * @returns Configured SentryAdapter instance
43
+ */
44
+ export declare function createSentryAdapter(config?: SentryAdapterConfig): SentryAdapter;
45
+ //# sourceMappingURL=sentry-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry-adapter.d.ts","sourceRoot":"","sources":["../src/sentry-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAc,YAAW,aAAa;IACjD,OAAO,CAAC,QAAQ,CAAS;gBAEb,MAAM,CAAC,EAAE,mBAAmB;IAIxC;;;;;;;OAOG;IACG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3C,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,aAAa;CAqCtB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,CAAC,EAAE,mBAAmB,GAC3B,aAAa,CAEf"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Sentry adapter subpath entry point
3
+ *
4
+ * Import via: import { createSentryAdapter } from '@happyvertical/logger/sentry';
5
+ */
6
+ export type { SentryAdapterConfig } from './sentry-adapter.js';
7
+ export { createSentryAdapter, SentryAdapter } from './sentry-adapter.js';
8
+ //# sourceMappingURL=sentry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../src/sentry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/sentry.js ADDED
@@ -0,0 +1,79 @@
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
+ }
72
+ function createSentryAdapter(config) {
73
+ return new SentryAdapter(config);
74
+ }
75
+ export {
76
+ SentryAdapter,
77
+ createSentryAdapter
78
+ };
79
+ //# sourceMappingURL=sentry.js.map
@@ -0,0 +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;"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Local Signal type definitions for logger adapter
3
+ * These types are copied from @happyvertical/types to keep logger independent
4
+ */
5
+ /**
6
+ * Signal types representing different phases of operation execution
7
+ */
8
+ export type SignalType = 'start' | 'step' | 'end' | 'error';
9
+ /**
10
+ * Signal - Represents an event in operation execution
11
+ */
12
+ export interface Signal {
13
+ /** Unique signal ID */
14
+ id: string;
15
+ /** Signal type */
16
+ type: SignalType;
17
+ /** Object ID that emitted the signal */
18
+ objectId: string;
19
+ /** Class name of the emitting object */
20
+ className: string;
21
+ /** Method name that emitted the signal */
22
+ method: string;
23
+ /** Timestamp when signal was emitted */
24
+ timestamp: Date;
25
+ /** Step description (for 'step' type signals) */
26
+ step?: string;
27
+ /** Operation duration in milliseconds (for 'end' type signals) */
28
+ duration?: number;
29
+ /** Operation result (for 'end' type signals) */
30
+ result?: unknown;
31
+ /** Error information (for 'error' type signals) */
32
+ error?: Error;
33
+ /** Additional metadata */
34
+ metadata?: Record<string, unknown>;
35
+ }
36
+ /**
37
+ * Signal Adapter interface - Handles signals for various purposes
38
+ */
39
+ export interface SignalAdapter {
40
+ /**
41
+ * Handle a signal
42
+ * @param signal - The signal to handle
43
+ */
44
+ handle(signal: Signal): Promise<void>;
45
+ }
46
+ //# sourceMappingURL=signal-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal-types.d.ts","sourceRoot":"","sources":["../src/signal-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,uBAAuB;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB;IAClB,IAAI,EAAE,UAAU,CAAC;IACjB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,SAAS,EAAE,IAAI,CAAC;IAChB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC"}
@@ -0,0 +1,56 @@
1
+ import { Logger, LogLevel } from './logger.js';
2
+ /**
3
+ * Log entry captured by TestLogger
4
+ */
5
+ export interface LogEntry {
6
+ level: LogLevel;
7
+ message: string;
8
+ context?: Record<string, unknown>;
9
+ timestamp: number;
10
+ }
11
+ /**
12
+ * Test logger that captures log entries in memory
13
+ *
14
+ * Useful for testing code that uses loggers without:
15
+ * - Mocking logger methods with vi.fn()
16
+ * - Spying on console output
17
+ * - Writing to actual log files
18
+ *
19
+ * Provides a real Logger implementation that can be inspected in tests.
20
+ */
21
+ export declare class TestLogger implements Logger {
22
+ /**
23
+ * All log entries captured by this logger
24
+ */
25
+ readonly entries: LogEntry[];
26
+ /**
27
+ * Minimum log level to capture (optional filtering)
28
+ */
29
+ private level?;
30
+ constructor(level?: LogLevel);
31
+ /**
32
+ * Get entries of a specific level
33
+ */
34
+ getEntriesByLevel(level: LogLevel): LogEntry[];
35
+ /**
36
+ * Get the most recent entry
37
+ */
38
+ getLastEntry(): LogEntry | undefined;
39
+ /**
40
+ * Check if a log level should be captured
41
+ */
42
+ private shouldLog;
43
+ /**
44
+ * Capture a log entry
45
+ */
46
+ private log;
47
+ /**
48
+ * Clear all captured entries
49
+ */
50
+ clear(): void;
51
+ debug(message: string, context?: Record<string, unknown>): void;
52
+ info(message: string, context?: Record<string, unknown>): void;
53
+ warn(message: string, context?: Record<string, unknown>): void;
54
+ error(message: string, context?: Record<string, unknown>): void;
55
+ }
56
+ //# sourceMappingURL=test-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-logger.d.ts","sourceRoot":"","sources":["../src/test-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,qBAAa,UAAW,YAAW,MAAM;IACvC;;OAEG;IACH,SAAgB,OAAO,EAAE,QAAQ,EAAE,CAAM;IAEzC;;OAEG;IACH,OAAO,CAAC,KAAK,CAAC,CAAW;gBAEb,KAAK,CAAC,EAAE,QAAQ;IAI5B;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,QAAQ,EAAE;IAI9C;;OAEG;IACH,YAAY,IAAI,QAAQ,GAAG,SAAS;IAIpC;;OAEG;IACH,OAAO,CAAC,SAAS;IASjB;;OAEG;IACH,OAAO,CAAC,GAAG;IAeX;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI/D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI9D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAGhE"}
package/metadata.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@happyvertical/logger",
3
+ "path": "packages/logger",
4
+ "position": {
5
+ "index": 18,
6
+ "count": 30
7
+ },
8
+ "description": "Structured logging for HAVE SDK with signal adapter",
9
+ "provides": [
10
+ "Structured logging for HAVE SDK with signal adapter"
11
+ ],
12
+ "implements": [],
13
+ "requires": {
14
+ "workspace": [
15
+ "@happyvertical/utils"
16
+ ],
17
+ "externalHappyVertical": [],
18
+ "external": [
19
+ "@sentry/node"
20
+ ]
21
+ },
22
+ "dependents": [
23
+ "@happyvertical/comfyui",
24
+ "@happyvertical/email",
25
+ "@happyvertical/encryption",
26
+ "@happyvertical/messages",
27
+ "@happyvertical/social",
28
+ "@happyvertical/video"
29
+ ],
30
+ "stability": {
31
+ "level": "stable",
32
+ "reason": "Primary package surface is described as implemented and production-oriented."
33
+ },
34
+ "keywords": [
35
+ "logger"
36
+ ]
37
+ }
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@happyvertical/logger",
3
+ "version": "0.74.8",
4
+ "description": "Structured logging for HAVE SDK with signal adapter",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./sentry": {
14
+ "import": "./dist/sentry.js",
15
+ "types": "./dist/sentry.d.ts"
16
+ }
17
+ },
18
+ "peerDependencies": {
19
+ "@sentry/node": ">=10.49.0"
20
+ },
21
+ "peerDependenciesMeta": {
22
+ "@sentry/node": {
23
+ "optional": true
24
+ }
25
+ },
26
+ "bin": {
27
+ "have-logger-context": "./dist/cli/claude-context.js"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE",
33
+ "AGENT.md",
34
+ "metadata.json"
35
+ ],
36
+ "publishConfig": {
37
+ "registry": "https://registry.npmjs.org",
38
+ "access": "public"
39
+ },
40
+ "dependencies": {
41
+ "@happyvertical/utils": "0.74.8"
42
+ },
43
+ "devDependencies": {
44
+ "@sentry/node": "^10.49.0",
45
+ "@types/node": "25.0.10",
46
+ "typescript": "^5.9.3",
47
+ "vite": "7.3.2",
48
+ "vitest": "^4.1.5"
49
+ },
50
+ "keywords": [
51
+ "logging",
52
+ "structured-logging",
53
+ "signals",
54
+ "observability",
55
+ "have-sdk"
56
+ ],
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "https://github.com/happyvertical/sdk.git",
60
+ "directory": "packages/logger"
61
+ },
62
+ "bugs": {
63
+ "url": "https://github.com/happyvertical/sdk/issues"
64
+ },
65
+ "homepage": "https://github.com/happyvertical/sdk/tree/main/packages/logger#readme",
66
+ "license": "MIT",
67
+ "scripts": {
68
+ "build": "vite build",
69
+ "dev": "vite build --watch",
70
+ "test": "vitest run",
71
+ "test:watch": "vitest",
72
+ "typecheck": "tsc --noEmit"
73
+ }
74
+ }