{"_id":"@auriclabs/metrics","_rev":"2-778daac912dc780642236f449d825690","name":"@auriclabs/metrics","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.1":{"name":"@auriclabs/metrics","version":"0.0.1","keywords":[],"author":"","license":"ISC","_id":"@auriclabs/metrics@0.0.1","maintainers":[{"name":"kevupton","email":"kevin@upton.tech"}],"homepage":"https://github.com/auriclabs/packages#readme","bugs":{"url":"https://github.com/auriclabs/packages/issues"},"dist":{"shasum":"702c01262604df321a54cfed04c0367a2de96615","tarball":"https://registry.npmjs.org/@auriclabs/metrics/-/metrics-0.0.1.tgz","fileCount":25,"integrity":"sha512-xabYwSxMH1V3DzEcMzCqeA6ZefeCQVFST9eQwW+Y1NXJLNosJ+hm9SeyPO1AoM4cu/O0XeODqmhTl4ubEwtzlQ==","signatures":[{"sig":"MEUCIQD+o29fYU00PqirE8dO8vddzrRKRpITJhVdCj/fLTEB7gIgXhfKJeNMeainIcEXRzEH+FQ2WHmubUo/USR0Lnuxgvs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":84902},"main":"dist/index.cjs","type":"module","_from":"file:auriclabs-metrics-0.0.1.tgz","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"dev":"concurrently \"pnpm build --watch\" \"pnpm:y:watch\"","lint":"eslint .","test":"vitest run","build":"tsdown src/index.ts --format cjs,esm --dts","y:watch":"chokidar dist --initial --silent -c \"yalc publish --push\"","lint:fix":"eslint . --fix","typecheck":"ts-config-typecheck","test:watch":"vitest"},"_npmUser":{"name":"kevupton","email":"kevin@upton.tech"},"prettier":"@auriclabs/prettier-config","_resolved":"/tmp/55224226a6c7fd67f544abd66023d635/auriclabs-metrics-0.0.1.tgz","_integrity":"sha512-xabYwSxMH1V3DzEcMzCqeA6ZefeCQVFST9eQwW+Y1NXJLNosJ+hm9SeyPO1AoM4cu/O0XeODqmhTl4ubEwtzlQ==","repository":{"url":"git+https://github.com/auriclabs/packages.git","type":"git","directory":"packages/metrics"},"_npmVersion":"10.8.2","description":"Metrics for AuricLabs projects","directories":{},"_nodeVersion":"20.19.5","dependencies":{"chalk":"5.6.2","lodash-es":"4.17.21","cli-table3":"0.6.5"},"publishConfig":{"registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/metrics_0.0.1_1760491472536_0.23996045234032892","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@auriclabs/metrics","version":"0.0.2","description":"Metrics for AuricLabs projects","prettier":"@auriclabs/prettier-config","type":"module","main":"dist/index.cjs","module":"dist/index.mjs","types":"dist/index.d.mts","exports":{".":{"types":"./dist/index.d.mts","import":"./dist/index.mjs","require":"./dist/index.cjs"}},"keywords":[],"author":"","license":"ISC","publishConfig":{"registry":"https://registry.npmjs.org/"},"repository":{"type":"git","url":"git+https://github.com/auriclabs/packages.git","directory":"packages/metrics"},"dependencies":{"chalk":"5.6.2","cli-table3":"0.6.5","lodash-es":"4.17.21"},"scripts":{"build":"tsdown src/index.ts --format cjs,esm --dts --no-hash","dev":"concurrently \"pnpm build --watch\" \"pnpm:y:watch\"","y:watch":"chokidar dist --initial --silent -c \"yalc publish --push\"","lint":"eslint .","lint:fix":"eslint . --fix","typecheck":"ts-config-typecheck","test":"vitest run","test:watch":"vitest"},"_id":"@auriclabs/metrics@0.0.2","bugs":{"url":"https://github.com/auriclabs/packages/issues"},"homepage":"https://github.com/auriclabs/packages#readme","_integrity":"sha512-w15XC3IhJpLEKilUIwwP4XSKFm1gRhEVz9GbbxYDHZHyLFYFsdtS6mD+lvv7HNipAiUJk8dhxeM/wPDxOEV7uQ==","_resolved":"/tmp/db6135acfb5a33878ddbc130155c1566/auriclabs-metrics-0.0.2.tgz","_from":"file:auriclabs-metrics-0.0.2.tgz","_nodeVersion":"20.20.1","_npmVersion":"10.8.2","dist":{"integrity":"sha512-w15XC3IhJpLEKilUIwwP4XSKFm1gRhEVz9GbbxYDHZHyLFYFsdtS6mD+lvv7HNipAiUJk8dhxeM/wPDxOEV7uQ==","shasum":"9b6178061af2ecd87229de9763881771d9e31ca0","tarball":"https://registry.npmjs.org/@auriclabs/metrics/-/metrics-0.0.2.tgz","fileCount":25,"unpackedSize":85361,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCID8pRJk/zJrq4LLDenJfSQxpcsfJbj0pXJELWaq5xVExAiBdVuLfSovxNO1JRisHN5uGLYBkmDH0Ef80rosCk/A04Q=="}]},"_npmUser":{"name":"kevupton","email":"kevin@upton.tech"},"directories":{},"maintainers":[{"name":"kevupton","email":"kevin@upton.tech"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/metrics_0.0.2_1774290597849_0.4051529197402546"},"_hasShrinkwrap":false}},"time":{"created":"2025-10-15T01:24:32.427Z","modified":"2026-03-23T18:29:58.247Z","0.0.1":"2025-10-15T01:24:32.712Z","0.0.2":"2026-03-23T18:29:58.065Z"},"bugs":{"url":"https://github.com/auriclabs/packages/issues"},"license":"ISC","homepage":"https://github.com/auriclabs/packages#readme","keywords":[],"repository":{"type":"git","url":"git+https://github.com/auriclabs/packages.git","directory":"packages/metrics"},"description":"Metrics for AuricLabs projects","maintainers":[{"name":"kevupton","email":"kevin@upton.tech"}],"readme":"# @auriclabs/metrics\n\nA TypeScript metrics collection and visualization package for tracking function execution times, errors, and performance statistics with beautiful hierarchical tree displays.\n\n## Features\n\n- 📊 **Hierarchical Metrics**: Track metrics with dot-separated namespaces (e.g., `api.users.get`)\n- 🎨 **Beautiful Visualization**: Display metrics using:\n  - **cli-table3**: Professional tables with borders showing hierarchical tree structure and all metrics\n  - **Unicode box-drawing characters**: Tree visualization (├──, └──, │) in table format\n  - **chalk**: Color-coded statistics for better readability\n- ⏱️ **Performance Tracking**: Automatically record min, max, average, and total duration\n- 🚨 **Error Tracking**: Track and display error counts\n- 🔄 **Sync & Async Support**: Built-in `span` helper for both sync and async function tracking\n- 🎯 **Return Value Preservation**: Span functions preserve and return values from wrapped functions\n\n## Installation\n\n```bash\npnpm add @auriclabs/metrics\n```\n\n## Dependencies\n\n- `cli-table3` - For professional table formatting with borders\n- `chalk` - For color-coded output\n- `lodash-es` - For utility functions\n\n## Usage\n\n### Basic Metrics Recording\n\n```typescript\nimport { recordMetrics, displayMetrics } from '@auriclabs/metrics';\n\n// Record a metric manually\nrecordMetrics('api.users.get', 145.23);\n\n// Record with error\nrecordMetrics('api.users.create', 230.45, new Error('Validation failed'));\n\n// Display all metrics\ndisplayMetrics();\n```\n\n### Using Spans for Automatic Tracking\n\n```typescript\nimport { span, displayMetrics } from '@auriclabs/metrics';\n\nasync function getUserData(userId: string) {\n  return await span('api.users.get', async () => {\n    // Your async code here\n    const user = await fetchUserFromDatabase(userId);\n    return user; // Return values are preserved\n  });\n}\n\nasync function createUser(userData: UserData) {\n  return await span('api.users.create', async () => {\n    // Your async code here\n    const newUser = await saveUserToDatabase(userData);\n    return newUser; // Return values are preserved\n  });\n}\n\n// After execution\ndisplayMetrics();\n```\n\n### Return Value Preservation\n\nThe `span` function automatically preserves return values from both sync and async functions:\n\n```typescript\nimport { span } from '@auriclabs/metrics';\n\n// Synchronous function with return value\nconst result = span('calculate', () => {\n  return 42 * 2;\n});\nconsole.log(result); // 84\n\n// Async function with return value\nconst user = await span('fetchUser', async () => {\n  return await db.users.findById('123');\n});\nconsole.log(user); // User object\n\n// Mixed sync and async spans\nconst total = await span('processOrder', async () => {\n  const price = span('calculatePrice', () => 100); // Sync\n  const tax = await span('fetchTax', async () => 10); // Async\n  return price + tax;\n});\nconsole.log(total); // 110\n```\n\n### Hierarchical Metrics\n\n```typescript\nimport { span } from '@auriclabs/metrics';\n\nasync function handleRequest() {\n  await span('api', async () => {\n    await span('request', async () => {\n      await span('auth', async () => {\n        // Authentication logic\n        // This creates metric: api.request.auth\n      });\n\n      await span('validation', async () => {\n        // Validation logic\n        // This creates metric: api.request.validation\n      });\n\n      await span('processing', async () => {\n        // Processing logic\n        // This creates metric: api.request.processing\n      });\n    });\n  });\n}\n```\n\n## Display Output\n\nThe `displayMetrics()` function produces two sections:\n\n### 1. Summary Table\n\n```\n═══ Metrics Summary ═══\n┌─────────────┬────────────────┬──────────────┬──────────────┐\n│ Total Calls │ Total Duration │ Total Errors │ Avg Duration │\n├─────────────┼────────────────┼──────────────┼──────────────┤\n│ 42          │ 1234.56ms      │ 3            │ 29.39ms      │\n└─────────────┴────────────────┴──────────────┴──────────────┘\n```\n\n### 2. Hierarchical Metrics Tree Table\n\nTree structure with Unicode box-drawing characters in the first column, followed by all metrics:\n\n```\n═══ Metrics Tree ═══\n┌──────────────────────────────────┬────────┬────────────┬────────────┬────────────┬────────────┬────────┐\n│ Span                             │ Calls  │ Avg        │ Min        │ Max        │ Total      │ Errors │\n├──────────────────────────────────┼────────┼────────────┼────────────┼────────────┼────────────┼────────┤\n│ ├── api                          │ 15     │ 145.23ms   │ 100.00ms   │ 200.00ms   │ 2178.45ms  │ 0      │\n│ │   ├── users                    │ 8      │ 230.45ms   │ 180.00ms   │ 300.00ms   │ 1843.60ms  │ 2      │\n│ │   │   ├── get                  │ 7      │ 142.50ms   │ 100.00ms   │ 200.00ms   │ 997.50ms   │ 0      │\n│ │   │   └── create               │ 1      │ 846.10ms   │ 846.10ms   │ 846.10ms   │ 846.10ms   │ 2      │\n│ │   └── posts                    │ 5      │ 150.00ms   │ 130.00ms   │ 180.00ms   │ 750.00ms   │ 0      │\n│ │       ├── list                 │ 3      │ 80.12ms    │ 60.00ms    │ 120.00ms   │ 240.36ms   │ 0      │\n│ │       └── create               │ 2      │ 254.82ms   │ 180.00ms   │ 329.64ms   │ 509.64ms   │ 0      │\n│ └── database                     │ 4      │ 50.00ms    │ 40.00ms    │ 65.00ms    │ 200.00ms   │ 0      │\n│     └── query                    │ 4      │ 50.00ms    │ 40.00ms    │ 65.00ms    │ 200.00ms   │ 0      │\n└──────────────────────────────────┴────────┴────────────┴────────────┴────────────┴────────────┴────────┘\n```\n\n## API Reference\n\n### `recordMetrics(name: string, duration: number, error?: unknown)`\n\nManually record a metric.\n\n**Parameters:**\n- `name`: Dot-separated metric name (e.g., 'api.users.get')\n- `duration`: Execution duration in milliseconds\n- `error` (optional): Error object if the operation failed\n\n### `getMetrics(): Record<string, MetricsRecord>`\n\nGet all recorded metrics as a plain object.\n\n**Returns:** Object with metric names as keys and `MetricsRecord` objects as values.\n\n### `displayMetrics()`\n\nDisplay all metrics in a beautiful formatted output with:\n- Summary table showing totals and averages\n- Hierarchical tree showing all metrics with their statistics\n\n### `span<T>(name: string, fn: () => T | Promise<T>): T | Promise<T>`\n\nExecute a synchronous or asynchronous function and automatically record its execution time.\n\n**Parameters:**\n- `name`: Metric name for this span\n- `fn`: Function to execute (can be sync or async)\n\n**Returns:** The return value from the wrapped function (preserves both sync and async returns)\n\n**Features:**\n- ✅ **Sync & Async Support**: Works with both synchronous and asynchronous functions\n- ✅ **Return Value Preservation**: Returns whatever your function returns\n- ✅ **Automatic Duration Tracking**: Captures execution time automatically\n- ✅ **Error Handling**: Records errors while still throwing them for proper error propagation\n- ✅ **Nested Spans**: Supports hierarchical metrics with dot-notation (e.g., `parent.child.grandchild`)\n- ✅ **Type Safety**: Full TypeScript support with generic type parameter\n\n**Examples:**\n\n```typescript\n// Synchronous function\nconst result = span('math.add', () => 1 + 1); // Returns: 2\n\n// Asynchronous function\nconst data = await span('api.fetch', async () => {\n  return await fetch('/api/data');\n}); // Returns: Response object\n\n// Nested spans with return values\nconst user = await span('users', async () => {\n  const id = span('generateId', () => uuid()); // Sync\n  return await span('save', async () => {\n    return await db.users.insert({ id }); // Async\n  });\n});\n```\n\n## MetricsRecord Interface\n\n```typescript\ninterface MetricsRecord {\n  totalRecords: number;      // Number of times this metric was recorded\n  totalDuration: number;     // Sum of all durations\n  averageDuration: number;   // Average duration per call\n  maxDuration: number;       // Maximum duration observed\n  minDuration: number;       // Minimum duration observed\n  duration: number;          // Last recorded duration\n  totalErrors: number;       // Count of errors\n  lastError?: unknown;       // Last error object\n}\n```\n\n## Color Coding\n\nThe display uses color coding for better readability:\n\n- **Cyan**: Metric names\n- **White**: Call counts\n- **Yellow**: Average duration\n- **Green**: Minimum duration\n- **Red**: Maximum duration and errors\n- **Magenta**: Total duration\n- **Gray**: Labels and borders\n\n## Examples\n\n### Express.js Middleware\n\n```typescript\nimport { span, displayMetrics } from '@auriclabs/metrics';\nimport express from 'express';\n\nconst app = express();\n\napp.use(async (req, res, next) => {\n  await span('http', async () => {\n    await span(req.method, async () => {\n      await span(req.path, async () => {\n        next();\n      });\n    });\n  });\n});\n\n// Display metrics on shutdown\nprocess.on('SIGTERM', () => {\n  displayMetrics();\n  process.exit(0);\n});\n```\n\n### Database Operations\n\n```typescript\nimport { span } from '@auriclabs/metrics';\n\nclass UserRepository {\n  async findById(id: string): Promise<User | null> {\n    return await span('database', async () => {\n      return await span('users', async () => {\n        return await span('findById', async () => {\n          // Return value is preserved through all nested spans\n          return await this.db.users.findOne({ id });\n        });\n      });\n    });\n  }\n\n  async create(user: User): Promise<User> {\n    return await span('database', async () => {\n      return await span('users', async () => {\n        return await span('create', async () => {\n          // Validates before inserting\n          const isValid = span('validate', () => this.validate(user)); // Sync span\n          if (!isValid) throw new Error('Invalid user');\n          \n          // Return the created user\n          return await this.db.users.insert(user);\n        });\n      });\n    });\n  }\n}\n\n// Usage - all return values work as expected\nconst user = await userRepo.findById('123');\nif (user) {\n  console.log(user.name);\n}\n\nconst newUser = await userRepo.create({ name: 'Alice', email: 'alice@example.com' });\nconsole.log(newUser.id); // Newly created user ID\n```\n\n## License\n\nISC\n\n","readmeFilename":"README.md"}