Consent
Headless and reactive. Declare what the app wants to track, tag emits with a category, and the facade gates delivery on the decisions. No UI ships here: a signal tells you exactly what to prompt for.
@mmstack/telemetry-corenpm
#Declaring requirements
Pass a consent config to provideTelemetry. Each requirement names a category and, optionally, the sink it applies to. An emit opts into a category through the last argument.
import { provideTelemetry } from '@mmstack/telemetry-core';
import { posthogSink } from '@mmstack/telemetry-posthog';
provideTelemetry({
sinks: [posthogSink({ posthog })],
consent: {
requirements: [
{ id: 'analytics', category: 'analytics', purpose: 'Anonymous product analytics' },
],
},
});
telemetry.event('checkout', { plan: 'pro' }, { category: 'analytics' });#Prompting and deciding
pending() is exactly the set of requirements without a decision yet, so it is what you render a prompt for. decide(id, granted) records an answer, and (with a store) persists it. Because requirements can be a signal, when a new section of the app needs new tracking, pending() becomes just the delta, and you re-prompt for only the new items.
// in your consent prompt component
const pending = telemetry.pending(); // requirements still needing an answer
telemetry.decide('analytics', true); // grant
telemetry.decide('analytics', false); // deny#How gating works
The default mode is 'required': a categorized emit needs an explicit grant, and an undecided or undeclared category is dropped. You cannot track something you did not ask consent for. Set mode: 'implicit' to flip that to opt-out. Uncategorized emits are never gated.
While an async store hydrates a returning visitor's decisions, an undecided emit is held briefly and then delivered or dropped according to what was stored, so a stored denial is never raced by an early emit.
#Persistence
localStorageConsentStore() persists decisions in the browser and is a noop on the server. The ConsentStore interface allows an async get/set, so a server-side store works the same way.
import { localStorageConsentStore, provideTelemetry } from '@mmstack/telemetry-core';
provideTelemetry({
sinks: [posthogSink({ posthog })],
consent: {
requirements: [{ id: 'analytics', category: 'analytics', purpose: '...' }],
store: localStorageConsentStore(),
},
});