Claim registry
How a lib-specific watcher claims a diagnostics channel so the generic OTel bridge dedups — recording each event once, not twice.
Some libraries observe their own events with a first-class, typed shape — a lib-specific OTel span for agora:agent:*, or a Telescope-style watcher for agora:media:*. But the generic OTel bridge auto-subscribes to every registered channel. Left alone, every event such a library emits would be handled twice: once as its own typed observation, once as a generic one.
The claim registry is how a library says "I already handle this — skip it by default." It's a process-wide record of which lib:event pairs have a dedicated consumer, and the generic bridge consults it to dedup.
claimDiagnostics — stake a claim
Call claimDiagnostics(lib, events) once when your typed consumer starts, passing every event it handles first-class. It returns a release function that un-claims exactly those keys:
import { claimDiagnostics } from '@adonis-agora/diagnostics'
// The media watcher records agora:media:upload.complete as its own typed span,
// so tell the generic bridge to skip it:
const release = claimDiagnostics('media', ['upload.complete'])
// … later, on consumer teardown:
release()While media:upload.complete is claimed, the generic bridge no longer produces a duplicate OTel span/event for it — the media watcher's typed observation is the only one.
Both functions live on the root entry
claimDiagnostics and isDiagnosticClaimed are exported from @adonis-agora/diagnostics (not the /otel subpath). Claiming is a producer-side concern — the bridge only reads the registry.
isDiagnosticClaimed — the bridge's dedup check
A generic observer asks isDiagnosticClaimed(lib, event) at record time — when an event is actually published — and skips events that are claimed:
import { isDiagnosticClaimed } from '@adonis-agora/diagnostics'
if (isDiagnosticClaimed('media', 'upload.complete')) {
// a lib-specific consumer already handles this — don't double-record
}Checking at record time (rather than once at subscribe time) is what makes claiming order-independent: a lib may call claimDiagnostics before or after the bridge's start(), and every event handled after the claim exists is skipped either way. It also means a released claim immediately un-skips.
Reference counting
Claims are reference-counted, so two independent consumers can claim the same key without one's release un-claiming it for the other:
- Claiming an already-claimed key increments its count.
- The
release()function decrements only the keys that call added, deleting a key only once its count reaches0. release()is idempotent — calling it more than once has no additional effect.
const releaseA = claimDiagnostics('media', ['upload.complete'])
const releaseB = claimDiagnostics('media', ['upload.complete'])
releaseA()
isDiagnosticClaimed('media', 'upload.complete') // → true (B still holds it)
releaseB()
isDiagnosticClaimed('media', 'upload.complete') // → falseOverriding the dedup: recordClaimed
The generic bridge skips claimed keys by default. Set recordClaimed: true in its BridgeOptions to bridge everything regardless of claims — useful to see the raw generic feed alongside the typed one while debugging:
import { start } from '@adonis-agora/diagnostics/otel'
// Bridge every event, even ones a lib-specific consumer claims:
start({ recordClaimed: true })With recordClaimed: true, a claimed event produces both the lib's typed observation and the generic OTel span/event. See the full bridge options.
The raw convention (no dependency required)
A package that doesn't want to depend on @adonis-agora/diagnostics can still participate by replicating the registry structure exactly — the same technique the channel registry uses:
Symbol.for('@agora/diagnostics:claims')resolves to aMap<string, number>stored onglobalThis.- Each key is
`${lib}:${event}`— the samelib:eventlabel the relay and bridge parse channel names into. - The value is a reference count, always
>= 1while the key is claimed by at least one caller; the key is deleted (unclaimed) only once the count reaches0. - A key is claimed iff the map has it; unclaimed iff absent.
Because the map lives on a cross-copy-stable global slot, claims made through any physical copy of the package — or by a package replicating the raw structure — are all shared.