UNPKG

@segment/analytics-next

Version:

Analytics Next (aka Analytics 2.0) is the latest version of Segment’s JavaScript SDK - enabling you to send your data to any tool without having to learn, test, or use a new API every time.

154 lines (130 loc) 4.49 kB
import { Facade } from '@segment/facade' import { Analytics } from '../../core/analytics' import { CDNSettings } from '../../browser' import { isOffline } from '../../core/connection' import { Context } from '../../core/context' import { Plugin } from '../../core/plugin' import { PriorityQueue } from '../../lib/priority-queue' import { PersistedPriorityQueue } from '../../lib/priority-queue/persisted' import { toFacade } from '../../lib/to-facade' import batch, { BatchingDispatchConfig } from './batched-dispatcher' import standard, { StandardDispatcherConfig } from './fetch-dispatcher' import { normalize } from './normalize' import { scheduleFlush } from './schedule-flush' import { SEGMENT_API_HOST } from '../../core/constants' type DeliveryStrategy = | { strategy?: 'standard' config?: StandardDispatcherConfig } | { strategy?: 'batching' config?: BatchingDispatchConfig } export type SegmentioSettings = { apiKey: string apiHost?: string protocol?: 'http' | 'https' addBundledMetadata?: boolean unbundledIntegrations?: string[] bundledConfigIds?: string[] unbundledConfigIds?: string[] maybeBundledConfigIds?: Record<string, string[]> deliveryStrategy?: DeliveryStrategy } type JSON = ReturnType<Facade['json']> function onAlias(analytics: Analytics, json: JSON): JSON { const user = analytics.user() json.previousId = json.previousId ?? json.from ?? user.id() ?? user.anonymousId() json.userId = json.userId ?? json.to delete json.from delete json.to return json } export function segmentio( analytics: Analytics, settings?: SegmentioSettings, integrations?: CDNSettings['integrations'] ): Plugin { // Attach `pagehide` before buffer is created so that inflight events are added // to the buffer before the buffer persists events in its own `pagehide` handler. window.addEventListener('pagehide', () => { buffer.push(...Array.from(inflightEvents)) inflightEvents.clear() }) const writeKey = settings?.apiKey ?? '' const buffer = analytics.options.disableClientPersistence ? new PriorityQueue<Context>(analytics.queue.queue.maxAttempts, []) : new PersistedPriorityQueue( analytics.queue.queue.maxAttempts, `${writeKey}:dest-Segment.io` ) const inflightEvents = new Set<Context>() const flushing = false const apiHost = settings?.apiHost ?? SEGMENT_API_HOST const protocol = settings?.protocol ?? 'https' const remote = `${protocol}://${apiHost}` const deliveryStrategy = settings?.deliveryStrategy const client = deliveryStrategy?.strategy === 'batching' ? batch(apiHost, deliveryStrategy.config) : standard(deliveryStrategy?.config as StandardDispatcherConfig) async function send(ctx: Context): Promise<Context> { if (isOffline()) { buffer.push(ctx) // eslint-disable-next-line @typescript-eslint/no-use-before-define scheduleFlush(flushing, buffer, segmentio, scheduleFlush) return ctx } inflightEvents.add(ctx) const path = ctx.event.type.charAt(0) let json = toFacade(ctx.event).json() if (ctx.event.type === 'track') { delete json.traits } if (ctx.event.type === 'alias') { json = onAlias(analytics, json) } return client .dispatch( `${remote}/${path}`, normalize(analytics, json, settings, integrations, ctx) ) .then(() => ctx) .catch((error) => { ctx.log('error', 'Error sending event', error) if (error.name === 'RateLimitError') { const timeout = error.retryTimeout buffer.pushWithBackoff(ctx, timeout) } else { buffer.pushWithBackoff(ctx) } // eslint-disable-next-line @typescript-eslint/no-use-before-define scheduleFlush(flushing, buffer, segmentio, scheduleFlush) return ctx }) .finally(() => { inflightEvents.delete(ctx) }) } const segmentio: Plugin = { name: 'Segment.io', type: 'destination', version: '0.1.0', isLoaded: (): boolean => true, load: (): Promise<void> => Promise.resolve(), track: send, identify: send, page: send, alias: send, group: send, screen: send, } // Buffer may already have items if they were previously stored in localStorage. // Start flushing them immediately. if (buffer.todo) { scheduleFlush(flushing, buffer, segmentio, scheduleFlush) } return segmentio }