{"_id":"@dstny/scp-metrics","name":"@dstny/scp-metrics","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@dstny/scp-metrics","version":"0.1.0","description":"Standalone OpenTelemetry Metrics SDK","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","scripts":{"start":"tsup --watch","build":"tsup","test":"jest","clean":"rm -rf node_modules dist debug.json coverage test-report.xml debug.json .turbo"},"author":"","license":"ISC","devDependencies":{"@swc/core":"^1.16.1","@swc/jest":"^0.2.39","@types/jest":"^29.5.6","jest":"^29.7.0","jest-junit":"^16.0.0","jest-sonar-reporter":"^2.0.0","tsup":"^8.5.1","typescript":"^5.9.2"},"dependencies":{"@opentelemetry/api":"^1.9.1","@opentelemetry/exporter-metrics-otlp-http":"^0.221.0","@opentelemetry/resources":"^2.8.0","@opentelemetry/sdk-metrics":"^2.10.0"},"gitHead":"31893dda6ec3eba4dd189db347f5dffe902e51d8","_id":"@dstny/scp-metrics@0.1.0","_nodeVersion":"24.21.0","_npmVersion":"11.19.0","dist":{"integrity":"sha512-kFTFyZGD0Gm/t1glguyiidJpbjhDFYvchG31KUJnIyIpU0Yr8o778ZMwnBdMkJBw2hXZhYjVF0awRI8ygjihEQ==","shasum":"062c2ce5da2c0b4472f1f0e7415e9dcf9db03661","tarball":"https://registry.npmjs.org/@dstny/scp-metrics/-/scp-metrics-0.1.0.tgz","fileCount":8,"unpackedSize":78132,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIGgUPje2vpfFH7yFxlze/xy3wwyXgeK3i+Y5Ho/iqsG6AiEAiBz5shguSjP+8xTc+KpTYbCeB8hLfrmqPbtnbNe8C2I="}]},"_npmUser":{"name":"gbo.dstny","email":"gerik.bonaert@dstny.com"},"directories":{},"maintainers":[{"name":"made.dstny","email":"maxime.destreel@dstny.com"},{"name":"gbo.dstny","email":"gerik.bonaert@dstny.com"},{"name":"kilinccagatay","email":"cagatay.kilinc@destiny.eu"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/scp-metrics_0.1.0_1789126562948_0.5130541708602894"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-11T11:36:02.790Z","0.1.0":"2026-09-11T11:36:03.071Z","modified":"2026-09-11T11:36:03.278Z"},"maintainers":[{"name":"made.dstny","email":"maxime.destreel@dstny.com"},{"name":"gbo.dstny","email":"gerik.bonaert@dstny.com"},{"name":"kilinccagatay","email":"cagatay.kilinc@destiny.eu"}],"description":"Standalone OpenTelemetry Metrics SDK","license":"ISC","readme":"# @dstny/scp-metrics\n\nStandalone wrapper around the OpenTelemetry Metrics SDK for OTLP/HTTP export to Alloy → Prometheus.\n\nAdds one thing the raw OTel JS API doesn't give you: a compile-time label schema per metric,\nso every call site for a given metric name is forced to pass the exact same label shape.\n\n## Usage\n\n```ts\nimport { ScpMetrics } from '@dstny/scp-metrics'\n\nconst scpMetrics = new ScpMetrics()\n\nawait scpMetrics.init({\n  url: 'https://alloy.example.com/v1/metrics',\n  token: accessToken,\n  resourceAttributes: { 'service.name': 'ScpSdk', 'service.version': '1.2.3' },\n  // Delays the very first export by rand(0, 5s) so a mass reconnect (deploy, outage\n  // recovery) doesn't make every client hit the collector at once. Defaults to true.\n  initialJitter: true,\n})\n\n// Labels type is declared once and enforced at every call site.\nconst requestCount = scpMetrics.defineCounter<{ method: string }>('http_requests_total')\nrequestCount.add(1, { method: 'GET' })\n// requestCount.add(1)                          // compile error: missing labels\n// requestCount.add(1, { method: 'GET', x: 1 }) // compile error: excess label key\n\nconst requestDuration = scpMetrics.defineHistogram<{ status: 'success' | 'failure' }>(\n  'http_request_duration_ms'\n)\nrequestDuration.record(842, { status: 'success' })\n```\n\nOn token refresh or logout, call `scpMetrics.setToken(newToken)` / `scpMetrics.shutdown()` -\nboth rebind that instance's `MeterProvider` live, no page reload needed.\n\n## Design notes\n\n- No shared/global state: each `new ScpMetrics()` instance owns its own `MeterProvider` and\n  instrument handles, so independent instances never contend for the same pipeline. This is\n  deliberate rather than an oversight - `@opentelemetry/api`'s own global meter-provider\n  registry only supports one registered provider per process, so this package bypasses it\n  entirely and holds a local `MeterProvider` reference per instance instead. In practice this\n  means different consumers (or the same consumer reporting to different destinations) can\n  each construct their own instance and `init()` it with its own `url`/`token`, and they will\n  never observe or interfere with each other's metrics.\n- `initialJitter` (default on) delays the very first export by `rand(0, 5s)` so a mass\n  reconnect (deploy, outage recovery) doesn't make every client hit the collector at once.\n- Export is purely interval-driven: a `PeriodicExportingMetricReader` samples current\n  instrument state on a fixed ~5s clock, independent of whether anything changed. Required\n  for Prometheus `rate()`/staleness semantics, and keeps request volume bounded by the clock\n  tick rather than by how many/how often instruments are updated.\n- Keep label attributes low-cardinality - see the linked Prometheus naming docs. A runtime\n  check in `registry.ts` warns (never throws) if the same metric name is later recorded with a\n  different label key set, since that's a real Prometheus problem TS generics alone can't catch\n  across separately-compiled call sites. This check is process-wide (not per-instance): it's a\n  cheap dev-time safety net for divergent `defineX<...>()` declarations, not part of the actual\n  metrics export path, so it doesn't reintroduce cross-instance coupling.\n","readmeFilename":"README.md","_rev":"1-7162da3a7f27bc5ecdf41526d2c1b0e5"}