Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
# instruments
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Bundle of OpenTelemetry instruments emitted by `@suaveplan/macos-contacts`. The accessor `getMacOSContactsInstruments()` lazily binds against the current global `MeterProvider`, caches the bundle, and returns the same object on every subsequent call. Tests call `resetMacOSContactsInstruments()` after installing a fresh `MeterProvider` to force a rebind; production callers never need to touch the reset path.
## Features
- Six counters partitioned by `outcome` (`success` | `permission_denied` | `access_failed`):
- `macos_contacts.list_total` — `MacOSContactsBridge.getAllContacts`
- `macos_contacts.search_total` — `searchContacts`
- `macos_contacts.get_total` — `getContactById`
- `macos_contacts.groups_list_total` — `getGroups`
- `macos_contacts.group_members_total` — `getGroupMembers`
- `macos_contacts.sync_total` — `sync`
- Two histograms (unit: ms):
- `macos_contacts.applescript_duration_ms` — wraps every runner invocation
- `macos_contacts.sync_duration_ms` — wraps `sync` end-to-end
- Lazy + cached — the bundle is built on first access against the global `MeterProvider`. Subsequent calls return the same reference (`===`-equal).
## Usage: Basic
```ts
import {
getMacOSContactsInstruments,
} from "@suaveplan/macos-contacts";
const instruments = getMacOSContactsInstruments();
instruments.listTotal.add(1, { outcome: "success" });
```
## Usage: Advanced
```ts
import { metrics } from "@opentelemetry/api";
import {
InMemoryMetricExporter,
MeterProvider,
PeriodicExportingMetricReader,
} from "@opentelemetry/sdk-metrics";
import {
getMacOSContactsInstruments,
resetMacOSContactsInstruments,
} from "@suaveplan/macos-contacts";
// Wire a real in-memory MeterProvider for tests.
const exporter = new InMemoryMetricExporter(0);
const reader = new PeriodicExportingMetricReader({
exporter,
exportIntervalMillis: 60_000,
});
const provider = new MeterProvider({ readers: [reader] });
metrics.disable();
metrics.setGlobalMeterProvider(provider);
// Force the bundle to rebind against the new provider.
resetMacOSContactsInstruments();
const instruments = getMacOSContactsInstruments();
```
## API Reference
### `MacOSContactsInstruments`
```ts
interface MacOSContactsInstruments {
readonly listTotal: Counter;
readonly searchTotal: Counter;
readonly getTotal: Counter;
readonly groupsListTotal: Counter;
readonly groupMembersTotal: Counter;
readonly syncTotal: Counter;
readonly applescriptDurationMs: Histogram;
readonly syncDurationMs: Histogram;
}
```
### `getMacOSContactsInstruments(): MacOSContactsInstruments`
Lazily builds and caches the bundle. Returns the same object reference on every subsequent call until `resetMacOSContactsInstruments()` is called.
### `resetMacOSContactsInstruments(): void`
Drops the cached bundle. The next `getMacOSContactsInstruments()` call rebuilds it against whatever global `MeterProvider` is in effect.
## Implementation Notes
The bundle is built against the global `MeterProvider` rather than a parameter so consumers do not need to thread a meter through every layer. Tests reset the cache after installing a new provider so each test sees a fresh instrument set bound to its own in-memory exporter.
Counters partition by `outcome` so dashboards can distinguish revoked-permission incidents from transient `osascript` failures. Histograms record duration in milliseconds (`unit: "ms"`); the bridge wraps the runner call so the sample is recorded even when the runner rejects — observability survives platform-level failures.
Instrument names use `snake_case` per OpenTelemetry semantic conventions. The `macos_contacts.` prefix mirrors the package name so multi-package dashboards can filter by namespace.