diff --git a/.changeset/violet-actors-pump.md b/.changeset/violet-actors-pump.md new file mode 100644 index 000000000..d0da27956 --- /dev/null +++ b/.changeset/violet-actors-pump.md @@ -0,0 +1,6 @@ +--- +'@segment/analytics-next': minor +--- + +- Add public settings API +- Do not expose loadLegacySettings / loadCDNSettings (private API) diff --git a/packages/browser/src/browser/__tests__/integration.test.ts b/packages/browser/src/browser/__tests__/integration.test.ts index 73d4fef9a..a1a42e5cd 100644 --- a/packages/browser/src/browser/__tests__/integration.test.ts +++ b/packages/browser/src/browser/__tests__/integration.test.ts @@ -1,5 +1,8 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { cdnSettingsKitchenSink } from '../../test-helpers/fixtures/cdn-settings' +import { + cdnSettingsKitchenSink, + cdnSettingsMinimal, +} from '../../test-helpers/fixtures/cdn-settings' import { createMockFetchImplementation } from '../../test-helpers/fixtures/create-fetch-method' import { Context } from '../../core/context' import { Plugin } from '../../core/plugin' @@ -7,8 +10,8 @@ import { JSDOM } from 'jsdom' import { Analytics, InitOptions } from '../../core/analytics' import { LegacyDestination } from '../../plugins/ajs-destination' import { PersistedPriorityQueue } from '../../lib/priority-queue/persisted' -// @ts-ignore loadLegacySettings mocked dependency is accused as unused -import { AnalyticsBrowser, loadLegacySettings } from '..' +// @ts-ignore loadCDNSettings mocked dependency is accused as unused +import { AnalyticsBrowser, loadCDNSettings } from '..' // @ts-ignore isOffline mocked dependency is accused as unused import { isOffline } from '../../core/connection' import * as SegmentPlugin from '../../plugins/segmentio' @@ -23,7 +26,8 @@ import { highEntropyTestData, lowEntropyTestData, } from '../../test-helpers/fixtures/client-hints' -import { getGlobalAnalytics, NullAnalytics } from '../..' +import { getGlobalAnalytics } from '../../lib/global-analytics-helper' +import { NullAnalytics } from '../../core/analytics' import { recordIntegrationMetric } from '../../core/stats/metric-helpers' let fetchCalls: ReturnType[] = [] @@ -1030,24 +1034,47 @@ describe('use', () => { }) }) -describe('timeout', () => { - it('has a default timeout value', async () => { +describe('public settings api', () => { + it('has expected settings', async () => { const [analytics] = await AnalyticsBrowser.load({ writeKey, + cdnSettings: cdnSettingsMinimal, + }) + + expect(analytics.settings).toEqual({ + writeKey, + cdnSettings: cdnSettingsMinimal, + timeout: 300, }) - //@ts-ignore - expect(analytics.settings.timeout).toEqual(300) + }) + + it('should have a writeKey', async () => { + const [analytics] = await AnalyticsBrowser.load({ + writeKey, + }) + + expect(analytics.settings.writeKey).toBe(writeKey) + }) + + it('should have cdn settings', async () => { + const [analytics] = await AnalyticsBrowser.load({ + writeKey, + cdnSettings: cdnSettingsMinimal, + }) + + expect(analytics.settings.cdnSettings).toEqual(cdnSettingsMinimal) }) it('can set a timeout value', async () => { const [analytics] = await AnalyticsBrowser.load({ writeKey, }) + expect(analytics.settings.timeout).toEqual(300) analytics.timeout(50) - //@ts-ignore expect(analytics.settings.timeout).toEqual(50) }) }) + describe('register', () => { it('will not invoke any plugins that have initialization errors', async () => { const analytics = AnalyticsBrowser.load({ @@ -1190,7 +1217,7 @@ describe('retries', () => { beforeEach(async () => { // @ts-ignore ignore reassining function - loadLegacySettings = jest.fn().mockReturnValue( + loadCDNSettings = jest.fn().mockReturnValue( Promise.resolve({ integrations: { 'Segment.io': { retryQueue: false } }, }) diff --git a/packages/browser/src/browser/__tests__/query-string.integration.test.ts b/packages/browser/src/browser/__tests__/query-string.integration.test.ts index dbcdc5103..0007d4cc5 100644 --- a/packages/browser/src/browser/__tests__/query-string.integration.test.ts +++ b/packages/browser/src/browser/__tests__/query-string.integration.test.ts @@ -1,6 +1,6 @@ import { JSDOM } from 'jsdom' import { Analytics } from '../../core/analytics' -// @ts-ignore loadLegacySettings mocked dependency is accused as unused +// @ts-ignore loadCDNSettings mocked dependency is accused as unused import { AnalyticsBrowser } from '..' import { setGlobalCDNUrl } from '../../lib/parse-cdn' import { TEST_WRITEKEY } from '../../test-helpers/test-writekeys' diff --git a/packages/browser/src/browser/__tests__/standalone-analytics.test.ts b/packages/browser/src/browser/__tests__/standalone-analytics.test.ts index e569b9f93..1bc385f27 100644 --- a/packages/browser/src/browser/__tests__/standalone-analytics.test.ts +++ b/packages/browser/src/browser/__tests__/standalone-analytics.test.ts @@ -1,6 +1,6 @@ import jsdom, { JSDOM } from 'jsdom' import { InitOptions, getGlobalAnalytics } from '../../' -import { AnalyticsBrowser, loadLegacySettings } from '../../browser' +import { AnalyticsBrowser, loadCDNSettings } from '../../browser' import { snippet } from '../../tester/__fixtures__/segment-snippet' import { install } from '../standalone-analytics' import unfetch from 'unfetch' @@ -130,7 +130,7 @@ describe('standalone bundle', () => { // @ts-ignore ignore Response required fields .mockImplementation((): Promise => fetchSettings) - await loadLegacySettings(segmentDotCom) + await loadCDNSettings(segmentDotCom) expect(unfetch).toHaveBeenCalledWith( 'https://cdn.foo.com/v1/projects/foo/settings' @@ -145,7 +145,7 @@ describe('standalone bundle', () => { const mockCdn = 'http://my-overridden-cdn.com' getGlobalAnalytics()!._cdn = mockCdn - await loadLegacySettings(segmentDotCom) + await loadCDNSettings(segmentDotCom) expect(unfetch).toHaveBeenCalledWith(expect.stringContaining(mockCdn)) }) diff --git a/packages/browser/src/browser/index.ts b/packages/browser/src/browser/index.ts index 43af1a379..f04cc9b01 100644 --- a/packages/browser/src/browser/index.ts +++ b/packages/browser/src/browser/index.ts @@ -2,12 +2,7 @@ import { getProcessEnv } from '../lib/get-process-env' import { getCDN, setGlobalCDNUrl } from '../lib/parse-cdn' import { fetch } from '../lib/fetch' -import { - Analytics, - AnalyticsSettings, - NullAnalytics, - InitOptions, -} from '../core/analytics' +import { Analytics, NullAnalytics, InitOptions } from '../core/analytics' import { Context } from '../core/context' import { Plan } from '../core/events' import { Plugin } from '../core/plugin' @@ -111,7 +106,8 @@ export interface CDNSettings { } } -export interface AnalyticsBrowserSettings extends AnalyticsSettings { +export interface AnalyticsBrowserSettings { + writeKey: string /** * The settings for the Segment Source. * If provided, `AnalyticsBrowser` will not fetch remote settings @@ -122,9 +118,17 @@ export interface AnalyticsBrowserSettings extends AnalyticsSettings { * If provided, will override the default Segment CDN (https://cdn.segment.com) for this application. */ cdnURL?: string + /** + * Plugins or npm-installed action destinations + */ + plugins?: (Plugin | PluginFactory)[] + /** + * npm-installed classic destinations + */ + classicIntegrations?: ClassicIntegrationSource[] } -export function loadLegacySettings( +export function loadCDNSettings( writeKey: string, cdnURL?: string ): Promise { @@ -330,31 +334,31 @@ async function loadAnalytics( preInitBuffer.push(new PreInitMethodCall('page', [])) } - let legacySettings = + let cdnSettings = settings.cdnSettings ?? - (await loadLegacySettings(settings.writeKey, settings.cdnURL)) + (await loadCDNSettings(settings.writeKey, settings.cdnURL)) if (options.updateCDNSettings) { - legacySettings = options.updateCDNSettings(legacySettings) + cdnSettings = options.updateCDNSettings(cdnSettings) } // if options.disable is a function, we allow user to disable analytics based on CDN Settings if (typeof options.disable === 'function') { - const disabled = await options.disable(legacySettings) + const disabled = await options.disable(cdnSettings) if (disabled) { return [new NullAnalytics(), Context.system()] } } const retryQueue: boolean = - legacySettings.integrations['Segment.io']?.retryQueue ?? true + cdnSettings.integrations['Segment.io']?.retryQueue ?? true options = { retryQueue, ...options, } - const analytics = new Analytics(settings, options) + const analytics = new Analytics({ ...settings, cdnSettings }, options) attachInspector(analytics) @@ -367,8 +371,8 @@ async function loadAnalytics( | undefined Stats.initRemoteMetrics({ - ...legacySettings.metrics, - host: segmentLoadOptions?.apiHost ?? legacySettings.metrics?.host, + ...cdnSettings.metrics, + host: segmentLoadOptions?.apiHost ?? cdnSettings.metrics?.host, protocol: segmentLoadOptions?.protocol, }) @@ -377,7 +381,7 @@ async function loadAnalytics( const ctx = await registerPlugins( settings.writeKey, - legacySettings, + cdnSettings, analytics, options, plugins, diff --git a/packages/browser/src/core/analytics/index.ts b/packages/browser/src/core/analytics/index.ts index 4cfa5f50c..07ad14687 100644 --- a/packages/browser/src/core/analytics/index.ts +++ b/packages/browser/src/core/analytics/index.ts @@ -28,10 +28,7 @@ import { EventQueue } from '../queue/event-queue' import { Group, ID, User, UserOptions } from '../user' import autoBind from '../../lib/bind-all' import { PersistedPriorityQueue } from '../../lib/priority-queue/persisted' -import type { - LegacyIntegration, - ClassicIntegrationSource, -} from '../../plugins/ajs-destination/types' +import type { LegacyIntegration } from '../../plugins/ajs-destination/types' import type { DestinationMiddlewareFunction, MiddlewareFunction, @@ -52,7 +49,6 @@ import { initializeStorages, isArrayOfStoreType, } from '../storage' -import { PluginFactory } from '../../plugins/remote-loader' import { setGlobalAnalytics } from '../../lib/global-analytics-helper' import { popPageContext } from '../buffer' @@ -75,11 +71,33 @@ function createDefaultQueue( return new EventQueue(priorityQueue) } +/** + * The public settings that are set on the analytics instance + */ +export class AnalyticsInstanceSettings { + readonly writeKey: string + /** + * This is an unstable API, it may change in the future without warning. + */ + readonly cdnSettings: CDNSettings + + /** + * Auto-track specific timeout setting for legacy purposes. + */ + timeout = 300 + + constructor(settings: AnalyticsSettings) { + this.writeKey = settings.writeKey + this.cdnSettings = settings.cdnSettings ?? { integrations: {} } + } +} + +/** + * The settings that are used to configure the analytics instance + */ export interface AnalyticsSettings { writeKey: string - timeout?: number - plugins?: (Plugin | PluginFactory)[] - classicIntegrations?: ClassicIntegrationSource[] + cdnSettings?: CDNSettings } export interface InitOptions { @@ -155,7 +173,7 @@ export class Analytics extends Emitter implements AnalyticsCore, AnalyticsClassic { - protected settings: AnalyticsSettings + settings: AnalyticsInstanceSettings private _user: User private _group: Group private eventFactory: EventFactory @@ -177,8 +195,7 @@ export class Analytics super() const cookieOptions = options?.cookie const disablePersistance = options?.disableClientPersistence ?? false - this.settings = settings - this.settings.timeout = this.settings.timeout ?? 300 + this.settings = new AnalyticsInstanceSettings(settings) this.queue = queue ?? createDefaultQueue( diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index fdd552aa8..ec821ae04 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -1,5 +1,10 @@ -export * from './core/analytics' -export * from './browser' +export { Analytics, AnalyticsSettings, InitOptions } from './core/analytics' +export { + AnalyticsBrowser, + AnalyticsBrowserSettings, + CDNSettings, + RemoteIntegrationSettings, +} from './browser' export * from './node' export * from './core/context'