# telemetry _(no submodules)_ ## Purpose Exposes the OpenTelemetry instrument set published by `@suaveplan/email-templates`. Every public render or registry mutation emits a counter (and where useful, a duration histogram) so operators can build dashboards without instrumenting consumers themselves. The instrument set is constructed lazily on first access — importing this module does NOT create a meter or instruments, satisfying the "side-effects: false" contract in `package.json`. Once constructed, the set is cached and the same object reference is returned forever (the metric exporter receives stable instrument identity, which is required for cumulative-temporality counters). ## Features - Four instruments: `render_total` (Counter), `render_duration_ms` (Histogram, unit `ms`), `registry_register_total` (Counter), `validation_failure_total` (Counter). - Stable meter name: `@suaveplan/email-templates`. - `resetEmailTemplatesInstrumentsCacheForTest()` helper to drop the cache between test runs that swap the global `MeterProvider`. ## Usage: Basic ```ts import { getEmailTemplatesInstruments } from "@suaveplan/email-templates/otel"; const instruments = getEmailTemplatesInstruments(); instruments.renderCounter.add(1, { template: "welcome", outcome: "ok" }); ``` ## Usage: Advanced Wire a real metric exporter in test setup and assert the recorded series on every render call (the package uses this pattern internally): ```ts import { AggregationTemporality, InMemoryMetricExporter, MeterProvider, PeriodicExportingMetricReader, } from "@opentelemetry/sdk-metrics"; import { metrics } from "@opentelemetry/api"; const exporter = new InMemoryMetricExporter(AggregationTemporality.DELTA); const reader = new PeriodicExportingMetricReader({ exporter }); const provider = new MeterProvider({ readers: [reader] }); metrics.setGlobalMeterProvider(provider); // ... drive the renderer ... await reader.forceFlush(); const series = exporter.getMetrics(); // assert series contains email_templates.render_total ... ``` ## API Reference | Symbol | Kind | Description | | -------------------------------------------- | -------- | -------------------------------------------- | | `getEmailTemplatesInstruments()` | function | Returns the cached `EmailTemplatesInstruments`. | | `resetEmailTemplatesInstrumentsCacheForTest()` | function | Drops the module-local cache — test-only. | | `EmailTemplatesInstruments` | type | The shape of the instrument set. | ## Implementation Notes The accessor must remain idempotent. Repeated calls return the same object reference; the test corpus asserts that fact because cumulative counters lose their backing storage if a meter is re-created mid-flight. The module never reads `process.env`, `globalThis.window`, or any DOM-resident global — it is universal (`engines.bun`, `engines.node`, browser) without conditional code paths.