OneApp Docs
PackagesIntegrations

@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-core

BaseAdapter, 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-core

2. Create a custom provider

lib/my-provider.ts
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

lib/analytics.ts
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 };
lib/analytics-with-consent.ts
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

PropertyValue
Locationpackages/3p-core
PurposeProvider abstraction layer
Used By@repo/3p-posthog, @repo/3p-segment, @repo/3p-vercel

Export Paths

PathDescription
@repo/3p-coreMain exports
@repo/3p-core/typesTypeScript interfaces
@repo/3p-core/utilsUtility 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-end

Creating 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);

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) {
  // ...
}

On this page