autotel
Version:
Write Once, Observe Anywhere
208 lines (206 loc) • 7.86 kB
TypeScript
/**
* Event Subscriber Interface (Type-only)
*
* Import this interface to create custom subscribers without importing implementations.
* Keeps core package focused on OpenTelemetry with zero extra dependencies.
*
* For ready-made subscribers (PostHog, Mixpanel, Amplitude, Segment),
* see the separate `autotel-subscribers` package.
*
* @example Custom subscriber
* ```typescript
* import { EventSubscriber } from 'autotel/event-subscriber';
*
* class MyCustomSubscriber implements EventSubscriber {
* trackEvent(name: string, attributes?: Record<string, any>): void {
* // Send to your events platform
* }
* // ... implement other methods
* }
* ```
*
* @example Use pre-built subscribers
* ```typescript
* import { Events } from 'autotel/events';
* import { PostHogSubscriber } from 'autotel-subscribers/posthog';
* import { MixpanelSubscriber } from 'autotel-subscribers/mixpanel';
*
* const event =new Event('checkout', {
* subscribers: [
* new PostHogSubscriber({ apiKey: 'phc_...' }),
* new MixpanelSubscriber({ token: '...' })
* ]
* });
* ```
*/
/**
* Event attributes (supports any JSON-serializable values)
*
* Allows primitive types for flat attributes and unknown for flexibility
* with nested objects when using subscribers that support JSON payloads
* (e.g., WebhookSubscriber).
*/
type EventAttributes = Record<string, unknown>;
/**
* Permissive input type for event attributes
*
* Accepts undefined/null values which will be filtered out before sending.
* This improves DX when working with optional properties from objects.
*
* @example
* ```typescript
* // No need to filter out undefined values manually
* event.trackEvent('user.action', {
* userId: user.id,
* email: user.email, // might be undefined
* plan: user.subscription, // might be null
* });
* ```
*/
type EventAttributesInput = Record<string, string | number | boolean | undefined | null>;
/**
* Funnel step status
*/
type FunnelStatus = 'started' | 'completed' | 'abandoned' | 'failed';
/**
* Outcome status
*/
type OutcomeStatus = 'success' | 'failure' | 'partial';
/**
* Autotel event context for trace correlation
*
* This structured object contains trace context and correlation IDs.
* Subscribers decide how to map/flatten these for their platform.
*/
interface AutotelEventContext {
/** Trace ID (32 hex chars) - present when inside a trace */
trace_id?: string;
/** Span ID (16 hex chars) - present when inside a span */
span_id?: string;
/** Trace flags (2 hex chars, e.g., '01' for sampled) */
trace_flags?: string;
/** Raw tracestate string - present if tracestate exists */
trace_state?: string;
/** Clickable trace URL - present if traceUrl config is set */
trace_url?: string;
/** Correlation ID (always present, 16 hex chars) */
correlation_id: string;
/** Number of linked parent traces (batch/fan-in scenarios) */
linked_trace_id_count?: number;
/** Stable hash of linked trace IDs (default for batch/fan-in) */
linked_trace_id_hash?: string;
/** Full array of linked trace IDs (only if includeLinkedTraceIds: true) */
linked_trace_ids?: string[];
}
/**
* Options for event tracking methods
*/
interface EventTrackingOptions {
/** Autotel trace context to include in the event */
autotel?: AutotelEventContext;
}
/**
* Event subscriber interface
*
* Implement this to send events to any platform.
* Zero runtime dependencies - just types.
*
* All tracking methods are async to support:
* - Backpressure signaling (buffer full)
* - Streaming platforms (Kafka, Kinesis, Pub/Sub)
* - Await delivery confirmation
* - Proper error propagation
*/
interface EventSubscriber {
/**
* Track an event (e.g., "user.registered", "order.created")
*
* @param name - Event name
* @param attributes - Optional event attributes
* @param options - Optional tracking options including autotel context
* @returns Promise that resolves when event is sent (or buffered)
*/
trackEvent(name: string, attributes?: EventAttributes, options?: EventTrackingOptions): Promise<void>;
/**
* Track a funnel step (e.g., checkout: started → completed)
*
* @param funnelName - Funnel name
* @param step - Funnel step status
* @param attributes - Optional event attributes
* @param options - Optional tracking options including autotel context
* @returns Promise that resolves when event is sent (or buffered)
*/
trackFunnelStep(funnelName: string, step: FunnelStatus, attributes?: EventAttributes, options?: EventTrackingOptions): Promise<void>;
/**
* Track an outcome (e.g., "payment.processing" → success/failure)
*
* @param operationName - Operation name
* @param outcome - Outcome status
* @param attributes - Optional event attributes
* @param options - Optional tracking options including autotel context
* @returns Promise that resolves when event is sent (or buffered)
*/
trackOutcome(operationName: string, outcome: OutcomeStatus, attributes?: EventAttributes, options?: EventTrackingOptions): Promise<void>;
/**
* Track a value/metric (e.g., revenue, cart value)
*
* @param name - Metric name
* @param value - Numeric value
* @param attributes - Optional event attributes
* @param options - Optional tracking options including autotel context
* @returns Promise that resolves when event is sent (or buffered)
*/
trackValue(name: string, value: number, attributes?: EventAttributes, options?: EventTrackingOptions): Promise<void>;
/**
* Track funnel progression with custom step names
*
* Unlike trackFunnelStep which uses FunnelStatus enum values,
* this method allows any string as the step name for flexible funnel tracking.
*
* @param funnelName - Name of the funnel (e.g., "checkout", "onboarding")
* @param stepName - Custom step name (e.g., "cart_viewed", "payment_entered")
* @param stepNumber - Optional numeric position in the funnel
* @param attributes - Optional event attributes
* @param options - Optional tracking options including autotel context
*
* @example
* ```typescript
* // Track custom checkout steps
* await subscriber.trackFunnelProgression('checkout', 'cart_viewed', 1);
* await subscriber.trackFunnelProgression('checkout', 'shipping_selected', 2);
* await subscriber.trackFunnelProgression('checkout', 'payment_entered', 3);
* await subscriber.trackFunnelProgression('checkout', 'order_confirmed', 4);
* ```
*/
trackFunnelProgression?(funnelName: string, stepName: string, stepNumber?: number, attributes?: EventAttributes, options?: EventTrackingOptions): Promise<void>;
/**
* Optional: Flush pending events and clean up resources
*
* Implement this if your subscriber buffers events, maintains connections,
* or needs cleanup before shutdown. Called during graceful shutdown.
*
* @example
* ```typescript
* class MySubscriber implements EventSubscriber {
* async shutdown(): Promise<void> {
* await this.flushBuffer();
* await this.closeConnections();
* }
* }
* ```
*/
shutdown?(): Promise<void>;
/**
* Optional: Subscriber name for debugging and error reporting
*
* @example "PostHogSubscriber", "SnowflakeSubscriber", "CustomWebhookSubscriber"
*/
readonly name?: string;
/**
* Optional: Subscriber version for debugging
*
* @example "1.0.0"
*/
readonly version?: string;
}
export type { AutotelEventContext, EventAttributes, EventAttributesInput, EventSubscriber, EventTrackingOptions, FunnelStatus, OutcomeStatus };