UNPKG

@segment/analytics-node

Version:

https://www.npmjs.com/package/@segment/analytics-node

211 lines 8.54 kB
import { bindAll, pTimeout } from '@segment/analytics-core'; import { validateSettings } from './settings'; import { version } from '../generated/version'; import { createConfiguredNodePlugin } from '../plugins/segmentio'; import { NodeEventFactory } from './event-factory'; import { dispatchAndEmit } from './dispatch-emit'; import { NodeEmitter } from './emitter'; import { Context } from './context'; import { NodeEventQueue } from './event-queue'; import { FetchHTTPClient } from '../lib/http-client'; export class Analytics extends NodeEmitter { _eventFactory; _isClosed = false; _pendingEvents = 0; _closeAndFlushDefaultTimeout; _publisher; _isFlushing = false; _queue; ready; constructor(settings) { super(); validateSettings(settings); this._eventFactory = new NodeEventFactory(); this._queue = new NodeEventQueue(); const flushInterval = settings.flushInterval ?? 10000; this._closeAndFlushDefaultTimeout = flushInterval * 1.25; // add arbitrary multiplier in case an event is in a plugin. const { plugin, publisher } = createConfiguredNodePlugin({ writeKey: settings.writeKey, host: settings.host, path: settings.path, maxRetries: settings.maxRetries ?? 3, flushAt: settings.flushAt ?? settings.maxEventsInBatch ?? 15, httpRequestTimeout: settings.httpRequestTimeout, disable: settings.disable, flushInterval, httpClient: typeof settings.httpClient === 'function' ? new FetchHTTPClient(settings.httpClient) : settings.httpClient ?? new FetchHTTPClient(), oauthSettings: settings.oauthSettings, }, this); this._publisher = publisher; this.ready = this.register(plugin).then(() => undefined); this.emit('initialize', settings); bindAll(this); } get VERSION() { return version; } /** * Call this method to stop collecting new events and flush all existing events. * This method also waits for any event method-specific callbacks to be triggered, * and any of their subsequent promises to be resolved/rejected. */ closeAndFlush({ timeout = this._closeAndFlushDefaultTimeout, } = {}) { return this.flush({ timeout, close: true }); } /** * Call this method to flush all existing events.. * This method also waits for any event method-specific callbacks to be triggered, * and any of their subsequent promises to be resolved/rejected. */ async flush({ timeout, close = false, } = {}) { if (this._isFlushing) { // if we're already flushing, then we don't need to do anything console.warn('Overlapping flush calls detected. Please wait for the previous flush to finish before calling .flush again'); return; } else { this._isFlushing = true; } if (close) { this._isClosed = true; } this._publisher.flush(this._pendingEvents); const promise = new Promise((resolve) => { if (!this._pendingEvents) { resolve(); } else { this.once('drained', () => { resolve(); }); } }).finally(() => { this._isFlushing = false; }); return timeout ? pTimeout(promise, timeout).catch(() => undefined) : promise; } _dispatch(segmentEvent, callback) { if (this._isClosed) { this.emit('call_after_close', segmentEvent); return undefined; } this._pendingEvents++; dispatchAndEmit(segmentEvent, this._queue, this, callback) .catch((ctx) => ctx) .finally(() => { this._pendingEvents--; if (!this._pendingEvents) { this.emit('drained'); } }); } /** * Combines two unassociated user identities. * @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#alias */ alias({ userId, previousId, context, timestamp, integrations, messageId, }, callback) { const segmentEvent = this._eventFactory.alias(userId, previousId, { context, integrations, timestamp, messageId, }); this._dispatch(segmentEvent, callback); } /** * Associates an identified user with a collective. * @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#group */ group({ timestamp, groupId, userId, anonymousId, traits = {}, context, integrations, messageId, }, callback) { const segmentEvent = this._eventFactory.group(groupId, traits, { context, anonymousId, userId, timestamp, integrations, messageId, }); this._dispatch(segmentEvent, callback); } /** * Includes a unique userId and (maybe anonymousId) and any optional traits you know about them. * @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#identify */ identify({ userId, anonymousId, traits = {}, context, timestamp, integrations, messageId, }, callback) { const segmentEvent = this._eventFactory.identify(userId, traits, { context, anonymousId, userId, timestamp, integrations, messageId, }); this._dispatch(segmentEvent, callback); } /** * The page method lets you record page views on your website, along with optional extra information about the page being viewed. * @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#page */ page({ userId, anonymousId, category, name, properties, context, timestamp, integrations, messageId, }, callback) { const segmentEvent = this._eventFactory.page(category ?? null, name ?? null, properties, { context, anonymousId, userId, timestamp, integrations, messageId }); this._dispatch(segmentEvent, callback); } /** * Records screen views on your app, along with optional extra information * about the screen viewed by the user. * * TODO: This is not documented on the segment docs ATM (for node). */ screen({ userId, anonymousId, category, name, properties, context, timestamp, integrations, messageId, }, callback) { const segmentEvent = this._eventFactory.screen(category ?? null, name ?? null, properties, { context, anonymousId, userId, timestamp, integrations, messageId }); this._dispatch(segmentEvent, callback); } /** * Records actions your users perform. * @link https://segment.com/docs/connections/sources/catalog/libraries/server/node/#track */ track({ userId, anonymousId, event, properties, context, timestamp, integrations, messageId, }, callback) { const segmentEvent = this._eventFactory.track(event, properties, { context, userId, anonymousId, timestamp, integrations, messageId, }); this._dispatch(segmentEvent, callback); } /** * Registers one or more plugins to augment Analytics functionality. * @param plugins */ register(...plugins) { return this._queue.criticalTasks.run(async () => { const ctx = Context.system(); const registrations = plugins.map((xt) => this._queue.register(ctx, xt, this)); await Promise.all(registrations); this.emit('register', plugins.map((el) => el.name)); }); } /** * Deregisters one or more plugins based on their names. * @param pluginNames - The names of one or more plugins to deregister. */ async deregister(...pluginNames) { const ctx = Context.system(); const deregistrations = pluginNames.map((pl) => { const plugin = this._queue.plugins.find((p) => p.name === pl); if (plugin) { return this._queue.deregister(ctx, plugin, this); } else { ctx.log('warn', `plugin ${pl} not found`); } }); await Promise.all(deregistrations); this.emit('deregister', pluginNames); } } //# sourceMappingURL=analytics-node.js.map