@repo/3p-core
Core utilities for building analytics provider integrations. BaseAdapter pattern, retry logic, batching, consent management. Foundation for PostHog, Segment, and Vercel adapters.
Quick Start
Add core utilities in 2 minutes:
pnpm add @repo/3p-coreBaseAdapter, retry, batching, consent included. Skip to Quick Start →
Why @repo/3p-core?
Every analytics provider duplicates retry logic. Batching reimplemented for each service. Consent management written multiple times. Event queue code scattered. No common interface for providers. Type definitions inconsistent across packages.
@repo/3p-core solves this with a unified BaseAdapter pattern, composable utilities, and shared types for all analytics providers.
Production-ready with retry logic, circuit breakers, event batching, consent management, and debug utilities.
Use cases
- Provider abstraction — Consistent interface for PostHog, Segment, Vercel
- Retry logic — Exponential backoff, circuit breakers, failover handling
- Event batching — Queue events, flush on interval or size threshold
- Consent management — GDPR/CCPA compliance with user preferences
- Custom providers — Build new analytics integrations with BaseAdapter
How it works
@repo/3p-core provides base classes and utilities:
import { BaseAdapter } from "@repo/3p-core/adapter";
import { withRetry } from "@repo/3p-core/composable/with-retry";
// Create custom provider
class MyAdapter extends BaseAdapter {
async track(event) {
return await this.sdk.track(event);
}
}
// Add retry logic
const adapter = new MyAdapter({ provider: "my-provider" });
const resilient = withRetry(adapter, { maxRetries: 3 });
await resilient.track({ name: "Event", properties: {} });Uses BaseAdapter for consistent API, composable utilities for retry/batching/privacy, and ConsentManager for GDPR compliance.
Key features
BaseAdapter — Consistent interface for all analytics providers
Retry logic — Exponential backoff with circuit breakers
Event batching — Queue events, flush on interval or size
Consent management — GDPR/CCPA with user preference storage
Composable utilities — withRetry, withBatching, withPrivacy
Type-safe — Full TypeScript interfaces for all utilities
Quick Start
1. Install the package
pnpm add @repo/3p-core2. Create a custom provider
import { BaseAdapter } from "@repo/3p-core/adapter";
import type { BaseAdapterConfig, TrackEvent } from "@repo/3p-core/types";
export class MyAdapter extends BaseAdapter {
constructor(config: BaseAdapterConfig) {
super(config);
}
async track(event: TrackEvent): Promise<boolean> {
// Implement your tracking logic
await MySDK.track(event.name, event.properties);
return true;
}
async identify(userId: string, traits?: Record<string, unknown>): Promise<boolean> {
await MySDK.identify(userId, traits);
return true;
}
}3. Add retry and batching
import { MyAdapter } from "./my-provider";
import { withRetry } from "@repo/3p-core/composable/with-retry";
import { withBatching } from "@repo/3p-core/composable/with-batching";
const adapter = new MyAdapter({
provider: "my-provider",
enabled: true
});
// Chain composable utilities
const analytics = withRetry(withBatching(adapter, { batchSize: 100, flushInterval: 5000 }), {
maxRetries: 3
});
export { analytics };4. Use with consent management
import { ConsentManager } from "@repo/3p-core/consent";
import { analytics } from "./analytics";
const consent = new ConsentManager({
defaultConsent: {
analytics: false,
marketing: false
}
});
export async function trackWithConsent(event: TrackEvent) {
if (consent.hasConsent("analytics")) {
await analytics.track(event);
}
}That's it! You now have a custom analytics provider with retry logic, batching, and consent management.
Use LazyMultiProvider for multiple providers
Orchestrate multiple providers with lazy loading:
import { LazyMultiProvider } from "@repo/3p-core/orchestration/lazy-multi-provider";
const analytics = new LazyMultiProvider({
providers: {
myProvider: {
enabled: true,
priority: 1,
loader: async () => new MyAdapter({ provider: "my-provider" })
}
}
});Technical Details
For Developers: Technical implementation details
Overview
| Property | Value |
|---|---|
| Location | packages/3p-core |
| Purpose | Provider abstraction layer |
| Used By | @repo/3p-posthog, @repo/3p-segment, @repo/3p-vercel |
Export Paths
| Path | Description |
|---|---|
@repo/3p-core | Main exports |
@repo/3p-core/types | TypeScript interfaces |
@repo/3p-core/utils | Utility functions |
Provider Interface
Implement This Interface
All analytics providers must implement this interface for consistent behavior across the application.
import type { AnalyticsProvider } from "@repo/3p-core/types";
// highlight-start
interface AnalyticsProvider {
name: string;
init(config: ProviderConfig): Promise<void>;
track(event: string, properties?: Record<string, unknown>): Promise<void>;
identify(userId: string, traits?: Record<string, unknown>): Promise<void>;
page(name?: string, properties?: Record<string, unknown>): Promise<void>;
group(groupId: string, traits?: Record<string, unknown>): Promise<void>;
reset(): Promise<void>;
flush(): Promise<void>;
}
// highlight-endCreating a Provider
import { createProvider } from "@repo/3p-core";
// highlight-start
export const myProvider = createProvider({
name: "my-provider",
async init(config) {
// Initialize your provider SDK
await MySDK.init(config.apiKey);
},
// highlight-end
async track(event, properties) {
await MySDK.track(event, properties);
},
async identify(userId, traits) {
await MySDK.identify(userId, traits);
},
async page(name, properties) {
await MySDK.page(name, properties);
},
async group(groupId, traits) {
await MySDK.group(groupId, traits);
},
async reset() {
await MySDK.reset();
},
async flush() {
await MySDK.flush();
}
});Provider Registry
import { registerProvider, getProvider } from "@repo/3p-core";
// Register a provider
// highlight-next-line
registerProvider(myProvider);
// Get a registered provider
const provider = getProvider("my-provider");
// List all providers
const providers = getAllProviders();Event Queue
Batch Events
Use the event queue to batch events and reduce network requests. Events are automatically flushed at the configured interval.
import { createEventQueue } from "@repo/3p-core";
// highlight-start
const queue = createEventQueue({
maxSize: 100,
flushInterval: 5000, // 5 seconds
onFlush: async (events) => {
await sendBatch(events);
}
});
// highlight-end
// Add events to queue
queue.push({ event: "click", properties: {} });
// Force flush
await queue.flush();Retry Logic
import { withRetry } from "@repo/3p-core/utils";
// highlight-start
const sendWithRetry = withRetry(sendEvent, {
maxRetries: 3,
backoff: "exponential",
initialDelay: 1000
});
// highlight-end
await sendWithRetry(event);Batching
import { createBatcher } from "@repo/3p-core/utils";
const batcher = createBatcher({
// highlight-start
maxBatchSize: 50,
maxWaitTime: 1000,
// highlight-end
onBatch: async (items) => {
await api.sendBatch(items);
}
});
// Items are automatically batched
batcher.add(event1);
batcher.add(event2);Consent Handling
GDPR Compliance
Always check user consent before tracking. Store consent preferences and respect user choices.
import { ConsentManager } from "@repo/3p-core";
const consent = new ConsentManager({
// highlight-start
defaultConsent: {
analytics: false,
marketing: false
}
// highlight-end
});
// Check consent
if (consent.hasConsent("analytics")) {
await track(event);
}
// Update consent
consent.setConsent({
analytics: true,
marketing: false
});Debug Utilities
import { createDebugger } from "@repo/3p-core/utils";
// highlight-next-line
const debug = createDebugger("my-provider");
debug.log("Initializing provider");
debug.warn("Missing optional config");
debug.error("Failed to send event", error);Type Utilities
import type { EventProperties, UserTraits, GroupTraits, ProviderConfig } from "@repo/3p-core/types";
// Use in your provider implementation
function track(event: string, properties: EventProperties) {
// ...
}Related Packages
- @repo/analytics - Main analytics package
- @repo/3p-posthog - PostHog provider
- @repo/3p-segment - Segment provider
- @repo/3p-vercel - Vercel Analytics