{"_id":"@andretimm/pharus","_rev":"3-32a610a518c23b452f12ce43a8d3dce2","name":"@andretimm/pharus","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@andretimm/pharus","version":"1.0.0","keywords":["metrics","nodejs","express","fastify","nestjs","observability","api-metrics","api-stats","api-monitoring"],"author":{"name":"andretimm"},"license":"ISC","_id":"@andretimm/pharus@1.0.0","maintainers":[{"name":"andretimm","email":"andretimm2012@gmail.com"}],"homepage":"https://github.com/navship/metrics#readme","bugs":{"url":"https://github.com/navship/metrics/issues"},"dist":{"shasum":"c562bafa9527ae69698a21bc77072e9c1fbcc28d","tarball":"https://registry.npmjs.org/@andretimm/pharus/-/pharus-1.0.0.tgz","fileCount":42,"integrity":"sha512-OeR9sLkB1so0fyaa7qD5heAlPjRniHUtJ9XAEJq2YwqG4+L08iBZvwNFXsPVmL5JCK6idYqZxbEn+s5nwj9Wzg==","signatures":[{"sig":"MEUCIQCJwFKMXMU1pFdOe+R9fJ4fIgHMWHK08CHt/YME1yAuWAIgBYDecoiZ23Z8KCnS/ikDBP57mm8FTc+0V3IwQ3KV55Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":240805},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.js","require":"./dist/nestjs/index.js"},"./express":{"types":"./dist/express/index.d.ts","import":"./dist/express/index.js","require":"./dist/express/index.js"},"./fastify":{"types":"./dist/fastify/index.d.ts","import":"./dist/fastify/index.js","require":"./dist/fastify/index.js"}},"gitHead":"270ba7e7d7785d3a1c3fa97751d5c74bb2d307e8","scripts":{"lint":"eslint src/**/*.ts","test":"jest","build":"tsc","format":"prettier --write \"src/**/*.ts\"","prepublishOnly":"npm run lint && npm run test && npm run build"},"_npmUser":{"name":"andretimm","email":"andretimm2012@gmail.com"},"repository":{"url":"git+https://github.com/navship/metrics.git","type":"git"},"_npmVersion":"10.8.2","description":"Framework-agnostic metrics collection library for Node.js applications.","directories":{},"_nodeVersion":"20.19.5","dependencies":{"uuid":"^13.0.0","axios":"^1.13.5"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","rxjs":"^7.8.2","eslint":"^10.0.2","express":"^5.2.1","fastify":"^5.7.4","ts-jest":"^29.4.6","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^25.2.2","@types/uuid":"^10.0.0","@nestjs/core":"^11.1.13","@nestjs/common":"^11.1.13","@types/express":"^5.0.6","reflect-metadata":"^0.2.2","typescript-eslint":"^8.56.1","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","@nestjs/platform-express":"^11.1.13","@nestjs/platform-fastify":"^11.1.13","@typescript-eslint/parser":"^8.56.1","@typescript-eslint/eslint-plugin":"^8.56.1"},"_npmOperationalInternal":{"tmp":"tmp/pharus_1.0.0_1771960256737_0.6809541179830734","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@andretimm/pharus","version":"1.0.1","keywords":["metrics","nodejs","express","fastify","nestjs","observability","api-metrics","api-stats","api-monitoring"],"author":{"name":"andretimm"},"license":"ISC","_id":"@andretimm/pharus@1.0.1","maintainers":[{"name":"andretimm","email":"andretimm2012@gmail.com"}],"homepage":"https://github.com/andretimm/pharus#readme","bugs":{"url":"https://github.com/andretimm/pharus/issues"},"dist":{"shasum":"a38728d9ed0edf09997478b5e4d30f3f6b89f155","tarball":"https://registry.npmjs.org/@andretimm/pharus/-/pharus-1.0.1.tgz","fileCount":42,"integrity":"sha512-c6go1m8rKdVg3Jm0FnhAx0Gd+mTrjbE+ggJcYnTBiNFKe7hRZqi/RhoAu4dzhAOLW9mpJWv7J3zHfpYhRHPIbw==","signatures":[{"sig":"MEYCIQDBAZYB5NQGocT4M596qGY8vOOLDSwuP9FLztgWOMKelQIhAOZJvS5WtkD4RCJKFAC9Uwu6SMjG5TafpcrGLaezmDbV","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":240698},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.js","require":"./dist/nestjs/index.js"},"./express":{"types":"./dist/express/index.d.ts","import":"./dist/express/index.js","require":"./dist/express/index.js"},"./fastify":{"types":"./dist/fastify/index.d.ts","import":"./dist/fastify/index.js","require":"./dist/fastify/index.js"}},"gitHead":"4822f36a62e8e3b861593498efe53efe38a8344e","scripts":{"lint":"eslint src/**/*.ts","test":"jest","build":"tsc","format":"prettier --write \"src/**/*.ts\"","prepublishOnly":"npm run lint && npm run test && npm run build"},"_npmUser":{"name":"andretimm","email":"andretimm2012@gmail.com"},"repository":{"url":"git+https://github.com/andretimm/pharus.git","type":"git"},"_npmVersion":"10.8.2","description":"Framework-agnostic metrics collection library for Node.js applications.","directories":{},"_nodeVersion":"20.19.5","dependencies":{"uuid":"^13.0.0","axios":"^1.13.5"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","rxjs":"^7.8.2","eslint":"^10.0.2","express":"^5.2.1","fastify":"^5.7.4","ts-jest":"^29.4.6","prettier":"^3.8.1","@eslint/js":"^10.0.1","typescript":"^5.9.3","@types/jest":"^30.0.0","@types/node":"^25.2.2","@types/uuid":"^10.0.0","@nestjs/core":"^11.1.13","@nestjs/common":"^11.1.13","@types/express":"^5.0.6","reflect-metadata":"^0.2.2","typescript-eslint":"^8.56.1","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","@nestjs/platform-express":"^11.1.13","@nestjs/platform-fastify":"^11.1.13","@typescript-eslint/parser":"^8.56.1","@typescript-eslint/eslint-plugin":"^8.56.1"},"_npmOperationalInternal":{"tmp":"tmp/pharus_1.0.1_1772042304350_0.8699818192849573","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@andretimm/pharus","version":"1.0.2","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.js","require":"./dist/nestjs/index.js"},"./fastify":{"types":"./dist/fastify/index.d.ts","import":"./dist/fastify/index.js","require":"./dist/fastify/index.js"},"./express":{"types":"./dist/express/index.d.ts","import":"./dist/express/index.js","require":"./dist/express/index.js"}},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"repository":{"type":"git","url":"git+https://github.com/andretimm/pharus.git"},"scripts":{"build":"tsc","prepublishOnly":"npm run lint && npm run test && npm run build","test":"jest","lint":"eslint src/**/*.ts","format":"prettier --write \"src/**/*.ts\""},"keywords":["metrics","nodejs","express","fastify","nestjs","observability","api-metrics","api-stats","api-monitoring"],"author":{"name":"andretimm"},"license":"ISC","description":"Framework-agnostic metrics collection library for Node.js applications.","dependencies":{"axios":"^1.13.5","uuid":"^13.0.0"},"devDependencies":{"@eslint/js":"^10.0.1","@nestjs/common":"^11.1.13","@nestjs/core":"^11.1.13","@nestjs/platform-express":"^11.1.13","@nestjs/platform-fastify":"^11.1.13","@types/express":"^5.0.6","@types/jest":"^30.0.0","@types/node":"^25.2.2","@types/uuid":"^10.0.0","@typescript-eslint/eslint-plugin":"^8.56.1","@typescript-eslint/parser":"^8.56.1","eslint":"^10.0.2","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.5","express":"^5.2.1","fastify":"^5.7.4","jest":"^30.2.0","prettier":"^3.8.1","reflect-metadata":"^0.2.2","rxjs":"^7.8.2","ts-jest":"^29.4.6","typescript":"^5.9.3","typescript-eslint":"^8.56.1"},"_id":"@andretimm/pharus@1.0.2","gitHead":"55a3b48f99e44fc6d629c02038a9317b4f9e81a1","bugs":{"url":"https://github.com/andretimm/pharus/issues"},"homepage":"https://github.com/andretimm/pharus#readme","_nodeVersion":"20.19.5","_npmVersion":"10.8.2","dist":{"integrity":"sha512-8w+X45EYebbZ3EoqdXNIDTzfP4ezzHs2wZMWezg6j2O8hjOHyXlUH5j58+claXltpf6WgXYq6+zf0VIwzahM0Q==","shasum":"e2a172eab076ec98e7b61510b21afc3de6536cc7","tarball":"https://registry.npmjs.org/@andretimm/pharus/-/pharus-1.0.2.tgz","fileCount":41,"unpackedSize":46644,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIF2wLldY4NAdS13MZ3uwuBThBMH86oXFRGdG/8LWJpbLAiEAikfic5aH6RaqOz2nA8oO7cEBIzn0YT8Ee/C/e14Ek0c="}]},"_npmUser":{"name":"andretimm","email":"andretimm2012@gmail.com"},"directories":{},"maintainers":[{"name":"andretimm","email":"andretimm2012@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pharus_1.0.2_1772452589456_0.6103574820380357"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-24T19:10:56.587Z","modified":"2026-03-02T11:56:29.756Z","1.0.0":"2026-02-24T19:10:56.901Z","1.0.1":"2026-02-25T17:58:24.504Z","1.0.2":"2026-03-02T11:56:29.628Z"},"bugs":{"url":"https://github.com/andretimm/pharus/issues"},"author":{"name":"andretimm"},"license":"ISC","homepage":"https://github.com/andretimm/pharus#readme","keywords":["metrics","nodejs","express","fastify","nestjs","observability","api-metrics","api-stats","api-monitoring"],"repository":{"type":"git","url":"git+https://github.com/andretimm/pharus.git"},"description":"Framework-agnostic metrics collection library for Node.js applications.","maintainers":[{"name":"andretimm","email":"andretimm2012@gmail.com"}],"readme":"# @andretimm/pharus\n\nA framework-agnostic metrics collection library for Node.js applications. Designed to easily integrate with **Express**, **Fastify**, and **NestJS**. It automatically collects request and response metrics (such as latency, HTTP status codes, and payload sizes) and sends them to a configured ingestion endpoint.\n\n## Key Features\n\n- **Framework Agnostic:** Core logic separated from framework adapters (`express`, `fastify`, `nestjs`).\n- **Automatic Collection:** Captures response duration, HTTP status code, request/response sizes, route, and HTTP method.\n- **Optimized Batching:** Metrics are accumulated in memory and periodically sent in batches to reduce network overhead.\n- **Highly Configurable:** Full control over batch size, auto-flush intervals, and captured data (body, query params, route params).\n- **Native Security:** Built-in support for redacting sensitive data using regular expressions (e.g., passwords, tokens).\n\n---\n\n## 📦 Installation\n\nYou can install the package directly using npm or yarn:\n\n```bash\nnpm install @andretimm/pharus\n# or\nyarn add @andretimm/pharus\n```\n\n---\n\n## ⚙️ Configuration Options (`MetricsOptions`)\n\nAll integrations accept the same options interface:\n\n| Property             | Type      | Required | Default | Description                                                                 |\n| -------------------- | --------- | -------- | ------- | --------------------------------------------------------------------------- |\n| `projectId`          | `string`  | **Yes**  | -       | The ID of the project associated with the metrics being sent.               |\n| `ingestUrl`          | `string`  | **Yes**  | -       | The destination URL where metric batches will be ingested.                  |\n| `batchSize`          | `number`  | No       | `50`    | Number of metrics to accumulate in memory before flushing the batch.        |\n| `flushIntervalMs`    | `number`  | No       | `5000`  | Maximum time (in ms) to wait before automatically flushing the batch.       |\n| `enabled`            | `boolean` | No       | `true`  | Enables or disables metric collection globally.                             |\n| `captureBody`        | `boolean` | No       | `false` | Whether to capture the request `body` payload.                              |\n| `captureQuery`       | `boolean` | No       | `false` | Whether to capture URL `query string` parameters.                           |\n| `captureParams`      | `boolean` | No       | `false` | Whether to capture dynamic route parameters (e.g., `/user/:id`).            |\n| `sensitiveKeysRegex` | `RegExp`  | No       | -       | Regular expression to hide/redact (replace with `*****`) sensitive keys.    |\n\n---\n\n## 🚀 Usage Guide by Framework\n\nBelow is how to configure and plug `@andretimm/pharus` into each of the main supported Node.js frameworks.\n\n### 1. Express.js\n\nImport the `metricsMiddleware` from the specific Express entry point.\n\n**Important Notice**: If you want to capture request bodies (`captureBody: true`), you MUST register your body parsing middleware (e.g., `express.json()`) **before** adding the metrics middleware.\n\n```typescript\nimport express from 'express';\nimport { metricsMiddleware } from '@andretimm/pharus/express';\n\nconst app = express();\n\n// JSON Parsing (Required before metrics if using `captureBody: true`)\napp.use(express.json());\n\n// Registering the metrics middleware\napp.use(metricsMiddleware({\n  projectId: 'my-super-project',\n  ingestUrl: 'https://my-metrics-api.com/ingest',\n  captureBody: true,        // Optional: Captures request payloads\n  batchSize: 10,            // Optional: Sends a batch every 10 processed requests\n}));\n\napp.get('/', (req, res) => {\n  res.send('Metrics are being captured silently!');\n});\n\napp.listen(3000, () => {\n    console.log('Server is running on port 3000');\n});\n```\n\n### 2. Fastify\n\nThe package exposes a plugin fully compatible with Fastify's internal ecosystem, safely handling the request/response lifecycle (`onRequest` and `onResponse`).\n\n```typescript\nimport fastify from 'fastify';\nimport { metricsPlugin } from '@andretimm/pharus/fastify';\n\nconst app = fastify();\n\n// Register the metrics plugin in Fastify\napp.register(metricsPlugin, {\n  projectId: 'my-super-project',\n  ingestUrl: 'https://my-metrics-api.com/ingest',\n  flushIntervalMs: 2000, // Optional: Ensures metrics are sent at least every 2 seconds\n  captureQuery: true,    // Optional: Saves all query strings\n});\n\napp.get('/', async () => {\n  return { hello: 'world' };\n});\n\napp.listen({ port: 3000 }, (err) => {\n  if (err) {\n    console.error(err);\n    process.exit(1);\n  }\n  console.log('Fastify is running on port 3000');\n});\n```\n\n### 3. NestJS\n\nThe package provides a native NestJS `MetricsModule`. Simply import the root module via `forRoot()` in your application, and it will automatically inject the global execution time and payload size `Interceptors`.\n\n```typescript\nimport { Module } from '@nestjs/common';\nimport { MetricsModule } from '@andretimm/pharus/nestjs';\nimport { AppController } from './app.controller';\nimport { AppService } from './app.service';\n\n@Module({\n  imports: [\n    // Register the module globally\n    MetricsModule.forRoot({\n      projectId: 'my-super-project',\n      ingestUrl: 'https://my-metrics-api.com/ingest',\n      sensitiveKeysRegex: /password|token|secret|credit_card/i, // Redacts critical body data\n      captureBody: true, \n    }),\n  ],\n  controllers: [AppController],\n  providers: [AppService],\n})\nexport class AppModule {}\n```\n\n---\n\n## 🛡 Advanced Features\n\n### Masking / Redacting Sensitive Data\n\nOne of the most useful features when logging full payloads is masking passwords or credit card data. You can control this using the `sensitiveKeysRegex` option.\n\n```typescript\nmetricsMiddleware({\n  // ... other initial options\n  captureBody: true,\n  sensitiveKeysRegex: /password|credit_card|cvc|secret_key/i\n})\n```\n\nAny object or key in the extracted payload/parameters that matches the regular expression `/password|credit_card/i` will have its value replaced in memory with `\"*****\"` before being cached or sent to the metrics API.\n\n### Manual / Forced Flush\n\nIn serverless environments (e.g., AWS Lambda) or during a **graceful shutdown** (cloud interruptions), Node might terminate your process before the `flushIntervalMs` triggers organically.\n\nTo ensure no in-memory data loss, you can leverage the core singleton:\n\n```typescript\nimport { MetricsCollector } from '@andretimm/pharus';\n\nconst collector = MetricsCollector.getInstance();\nawait collector.flush(); // Forces an immediate push of all pending metrics\nawait collector.shutdown(); // And then, safely terminates the global dispatch loop\n```\n\n---\n\n## 📜 License\n\nThis software is licensed under the [ISC License](./LICENSE).\n\n","readmeFilename":"README.md"}