diff --git a/.browserslistrc b/.browserslistrc index 27001cf97..0664125b8 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,5 +1,5 @@ -chrome >= 32, -ie >= 9, -edge >= 13, -firefox >= 27, -safari >= 8, +chrome >= 72, +ie >= 11, +edge >= 79, +firefox >= 78, +safari >= 9.1, diff --git a/.bundlemonrc.json b/.bundlemonrc.json index 00805baa2..4dfd4d327 100644 --- a/.bundlemonrc.json +++ b/.bundlemonrc.json @@ -7,27 +7,27 @@ }, { "path": "./trackers/browser-tracker/dist/index.umd.min.js", - "maxSize": "16kb", + "maxSize": "20kb", "maxPercentIncrease": 10 }, { "path": "./trackers/javascript-tracker/dist/sp.js", - "maxSize": "26kb", + "maxSize": "30kb", "maxPercentIncrease": 10 }, { "path": "./trackers/javascript-tracker/dist/sp.lite.js", - "maxSize": "16kb", + "maxSize": "20kb", "maxPercentIncrease": 10 }, { "path": "./libraries/browser-tracker-core/dist/index.module.js", - "maxSize": "28kb", + "maxSize": "25kb", "maxPercentIncrease": 10 }, { "path": "./libraries/tracker-core/dist/index.module.js", - "maxSize": "15kb", + "maxSize": "20kb", "maxPercentIncrease": 10 } ], diff --git a/api-docs/docs/browser-tracker/browser-tracker.api.md b/api-docs/docs/browser-tracker/browser-tracker.api.md index 8818fcbe5..4eedddea0 100644 --- a/api-docs/docs/browser-tracker/browser-tracker.api.md +++ b/api-docs/docs/browser-tracker/browser-tracker.api.md @@ -29,7 +29,7 @@ export interface ActivityTrackingConfigurationCallback { } // @public -export function addGlobalContexts(contexts: Array, trackers?: Array): void; +export function addGlobalContexts(contexts: Array | Record, trackers?: Array): void; // @public export function addPlugin(configuration: BrowserPluginConfiguration, trackers?: Array): void; @@ -40,15 +40,11 @@ export type AnonymousTrackingOptions = boolean | { withServerAnonymisation?: boolean; }; -// Warning: (ae-forgotten-export) The symbol "CorePlugin" needs to be exported by the entry point index.module.d.ts -// // @public export interface BrowserPlugin extends CorePlugin { activateBrowserPlugin?: (tracker: BrowserTracker) => void; } -// Warning: (ae-forgotten-export) The symbol "CorePluginConfiguration" needs to be exported by the entry point index.module.d.ts -// // @public export interface BrowserPluginConfiguration extends CorePluginConfiguration { /* The plugin to add */ @@ -60,7 +56,6 @@ export interface BrowserPluginConfiguration extends CorePluginConfiguration { export interface BrowserTracker { addPlugin: (configuration: BrowserPluginConfiguration) => void; clearUserData: (configuration?: ClearUserDataConfiguration) => void; - // Warning: (ae-forgotten-export) The symbol "TrackerCore" needs to be exported by the entry point index.module.d.ts core: TrackerCore; crossDomainLinker: (crossDomainLinkerCriterion: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean) => void; disableActivityTracking: () => void; @@ -96,7 +91,6 @@ export interface BrowserTracker { setUserIdFromLocation: (querystringField: string) => void; setUserIdFromReferrer: (querystringField: string) => void; setVisitorCookieTimeout: (timeout: number) => void; - // Warning: (ae-forgotten-export) The symbol "SharedState" needs to be exported by the entry point index.module.d.ts sharedState: SharedState; trackPageView: (event?: PageViewEvent & CommonEventProperties) => void; updatePageActivity: () => void; @@ -145,7 +139,6 @@ export interface ClientSession extends Record { // @public export interface CommonEventProperties> { context?: Array> | null; - // Warning: (ae-forgotten-export) The symbol "Timestamp" needs to be exported by the entry point index.module.d.ts timestamp?: Timestamp | null; } @@ -154,7 +147,6 @@ export type ConditionalContextProvider = FilterProvider | RuleSetProvider; // @public export interface ContextEvent { - // Warning: (ae-forgotten-export) The symbol "Payload" needs to be exported by the entry point index.module.d.ts event: Payload; eventSchema: string; eventType: string; @@ -172,9 +164,34 @@ export type ContextPrimitive = SelfDescribingJson | ContextGenerator; // @public (undocumented) export type CookieSameSite = "None" | "Lax" | "Strict"; +// @public +export interface CorePlugin { + activateCorePlugin?: (core: TrackerCore) => void; + afterTrack?: (payload: Payload) => void; + beforeTrack?: (payloadBuilder: PayloadBuilder) => void; + contexts?: () => SelfDescribingJson[]; + filter?: (payload: Payload) => boolean; + logger?: (logger: Logger) => void; +} + +// @public +export interface CorePluginConfiguration { + /* The plugin to add */ + // (undocumented) + plugin: CorePlugin; +} + // @public export function crossDomainLinker(crossDomainLinkerCriterion: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean, trackers?: Array): void; +// @public +export interface DeviceTimestamp { + // (undocumented) + readonly type: "dtm"; + // (undocumented) + readonly value: number; +} + // @public export function disableActivityTracking(trackers?: Array): void; @@ -197,6 +214,28 @@ export function discardBrace(enable: boolean, trackers?: Array): void; // @public export function discardHashTag(enable: boolean, trackers?: Array): void; +// @public (undocumented) +export interface EmitterConfigurationBase { + bufferSize?: number; + connectionTimeout?: number; + credentials?: "omit" | "same-origin" | "include"; + customFetch?: (input: Request, options?: RequestInit) => Promise; + customHeaders?: Record; + dontRetryStatusCodes?: number[]; + eventMethod?: EventMethod; + eventStore?: EventStore; + idService?: string; + keepalive?: boolean; + maxGetBytes?: number; + maxPostBytes?: number; + onRequestFailure?: (data: RequestFailure, response?: Response) => void; + onRequestSuccess?: (data: EventBatch, response: Response) => void; + postPath?: string; + retryFailedRequests?: boolean; + retryStatusCodes?: number[]; + useStm?: boolean; +} + // @public export function enableActivityTracking(configuration: ActivityTrackingConfiguration, trackers?: Array): void; @@ -217,18 +256,51 @@ export interface EnableAnonymousTrackingConfiguration { } // @public -export type EventBatch = GetBatch | PostBatch; +export type EventBatch = Payload[]; + +// Warning: (ae-forgotten-export) The symbol "EventJsonWithKeys" needs to be exported by the entry point index.module.d.ts +// +// @public +export type EventJson = Array; // @public (undocumented) -export type EventMethod = "post" | "get" | "beacon"; +export type EventMethod = "post" | "get"; // @public export interface EventPayloadAndContext { context: Array; - // Warning: (ae-forgotten-export) The symbol "PayloadBuilder" needs to be exported by the entry point index.module.d.ts event: PayloadBuilder; } +// @public +export interface EventStore { + add: (payload: EventStorePayload) => Promise; + count: () => Promise; + getAll: () => Promise; + getAllPayloads: () => Promise; + iterator: () => EventStoreIterator; + removeHead: (count: number) => Promise; +} + +// @public (undocumented) +export interface EventStoreConfiguration { + maxSize?: number; +} + +// @public (undocumented) +export interface EventStoreIterator { + next: () => Promise<{ + value: EventStorePayload | undefined; + done: boolean; + }>; +} + +// @public (undocumented) +export interface EventStorePayload { + payload: Payload; + svrAnon?: boolean; +} + // @public (undocumented) export type ExtendedCrossDomainLinkerAttributes = { userId?: boolean; @@ -258,16 +330,35 @@ export interface FlushBufferConfiguration { } // @public -export type GetBatch = string[]; +export type JsonProcessor = (payloadBuilder: PayloadBuilder, jsonForProcessing: EventJson, contextEntitiesForProcessing: SelfDescribingJson[]) => void; -// @public -export function newSession(trackers?: Array): void; +// @public (undocumented) +export interface LocalStorageEventStoreConfigurationBase extends EventStoreConfiguration { + maxLocalStorageQueueSize?: number; + useLocalStorage?: boolean; +} + +// @public (undocumented) +export interface Logger { + // (undocumented) + debug: (message: string, ...extraParams: unknown[]) => void; + // (undocumented) + error: (message: string, error?: unknown, ...extraParams: unknown[]) => void; + // (undocumented) + info: (message: string, ...extraParams: unknown[]) => void; + // Warning: (ae-forgotten-export) The symbol "LOG_LEVEL" needs to be exported by the entry point index.module.d.ts + // + // (undocumented) + setLogLevel: (level: LOG_LEVEL) => void; + // (undocumented) + warn: (message: string, error?: unknown, ...extraParams: unknown[]) => void; +} // @public -export function newTracker(trackerId: string, endpoint: string): BrowserTracker; +export function newSession(trackers?: Array): void; // @public -export function newTracker(trackerId: string, endpoint: string, configuration: TrackerConfiguration): BrowserTracker; +export function newTracker(trackerId: string, endpoint: string, configuration?: TrackerConfiguration): BrowserTracker | null | undefined; // @public export interface PageViewEvent { @@ -290,11 +381,23 @@ firstEventTs: number | undefined, eventIndex: number ]; -// @public (undocumented) -export type Platform = "web" | "mob" | "pc" | "srv" | "app" | "tv" | "cnsl" | "iot"; +// @public +export type Payload = Record; // @public -export type PostBatch = Record[]; +export interface PayloadBuilder { + add: (key: string, value: unknown) => void; + addContextEntity: (entity: SelfDescribingJson) => void; + addDict: (dict: Payload) => void; + addJson: (keyIfEncoded: string, keyIfNotEncoded: string, json: Record) => void; + build: () => Payload; + getJson: () => EventJson; + getPayload: () => Payload; + withJsonProcessor: (jsonProcessor: JsonProcessor) => void; +} + +// @public (undocumented) +export type Platform = "web" | "mob" | "pc" | "srv" | "app" | "tv" | "cnsl" | "iot"; // @public export function preservePageViewId(trackers?: Array): void; @@ -303,7 +406,7 @@ export function preservePageViewId(trackers?: Array): void; export type PreservePageViewIdForUrl = boolean | "full" | "pathname" | "pathnameAndSearch"; // @public -export function removeGlobalContexts(contexts: Array, trackers?: Array): void; +export function removeGlobalContexts(contexts: Array, trackers?: Array): void; // @public export type RequestFailure = { @@ -328,14 +431,14 @@ Array | ContextPrimitive ]; // @public -export interface SelfDescribingEvent { - event: SelfDescribingJson; +export interface SelfDescribingEvent> { + event: SelfDescribingJson; } // @public -export type SelfDescribingJson = Record> = { +export type SelfDescribingJson> = { schema: string; - data: T; + data: T extends any[] ? never : T extends {} ? T : never; }; // @public @@ -374,6 +477,24 @@ export function setUserIdFromReferrer(querystringField: string, trackers?: Array // @public export function setVisitorCookieTimeout(timeout: number, trackers?: Array): void; +// @public +export class SharedState { + // (undocumented) + bufferFlushers: Array<(sync: boolean) => void>; + /* DOM Ready */ + // (undocumented) + hasLoaded: boolean; + /* DOM Ready */ + // (undocumented) + pageViewId?: string; + /* DOM Ready */ + // (undocumented) + pageViewUrl?: string; + /* DOM Ready */ + // (undocumented) + registeredOnLoadHandlers: Array<() => void>; +} + // @public (undocumented) export type StateStorageStrategy = "cookieAndLocalStorage" | "cookie" | "localStorage" | "none"; @@ -391,6 +512,9 @@ export interface StructuredEvent { value?: number; } +// @public +export type Timestamp = TrueTimestamp | DeviceTimestamp | number; + // @public export type TrackerConfiguration = { encodeBase64?: boolean; @@ -399,47 +523,66 @@ export type TrackerConfiguration = { cookieSameSite?: CookieSameSite; cookieSecure?: boolean; cookieLifetime?: number; - withCredentials?: boolean; sessionCookieTimeout?: number; appId?: string; platform?: Platform; respectDoNotTrack?: boolean; - eventMethod?: EventMethod; - postPath?: string; - useStm?: boolean; - bufferSize?: number; crossDomainLinker?: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean; useExtendedCrossDomainLinker?: ExtendedCrossDomainLinkerOptions; - maxPostBytes?: number; - maxGetBytes?: number; discoverRootDomain?: boolean; stateStorageStrategy?: StateStorageStrategy; - maxLocalStorageQueueSize?: number; resetActivityTrackingOnPageView?: boolean; - connectionTimeout?: number; anonymousTracking?: AnonymousTrackingOptions; contexts?: BuiltInContexts; plugins?: Array; - customHeaders?: Record; - retryStatusCodes?: number[]; - dontRetryStatusCodes?: number[]; onSessionUpdateCallback?: (updatedSession: ClientSession) => void; - idService?: string; - retryFailedRequests?: boolean; - onRequestSuccess?: (data: EventBatch) => void; - onRequestFailure?: (data: RequestFailure) => void; preservePageViewIdForUrl?: PreservePageViewIdForUrl; -}; + synchronousCookieWrite?: boolean; +} & EmitterConfigurationBase & LocalStorageEventStoreConfigurationBase; + +// @public +export interface TrackerCore { + addGlobalContexts(contexts: Array | Record): void; + addPayloadDict(dict: Payload): void; + addPayloadPair: (key: string, value: unknown) => void; + addPlugin(configuration: CorePluginConfiguration): void; + clearGlobalContexts(): void; + getBase64Encoding(): boolean; + removeGlobalContexts(contexts: Array): void; + resetPayloadPairs(dict: Payload): void; + setAppId(appId: string): void; + setBase64Encoding(encode: boolean): void; + setColorDepth(depth: string): void; + setIpAddress(ip: string): void; + setLang(lang: string): void; + setPlatform(value: string): void; + setScreenResolution(width: string, height: string): void; + setTimezone(timezone: string): void; + setTrackerNamespace(name: string): void; + setTrackerVersion(version: string): void; + setUseragent(useragent: string): void; + setUserId(userId: string): void; + setViewport(width: string, height: string): void; + track: (pb: PayloadBuilder, context?: Array | null, timestamp?: Timestamp | null) => Payload | undefined; +} // @public export function trackPageView(event?: PageViewEvent & CommonEventProperties, trackers?: Array): void; // @public -export function trackSelfDescribingEvent(event: SelfDescribingEvent & CommonEventProperties, trackers?: Array): void; +export function trackSelfDescribingEvent>(event: SelfDescribingEvent & CommonEventProperties, trackers?: Array): void; // @public export function trackStructEvent(event: StructuredEvent & CommonEventProperties, trackers?: Array): void; +// @public +export interface TrueTimestamp { + // (undocumented) + readonly type: "ttm"; + // (undocumented) + readonly value: number; +} + // @public export function updatePageActivity(trackers?: Array): void; diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.addglobalcontexts.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.addglobalcontexts.md index 5b77e0513..d77b7c3ae 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.addglobalcontexts.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.addglobalcontexts.md @@ -9,14 +9,14 @@ All provided contexts will be sent with every event Signature: ```typescript -declare function addGlobalContexts(contexts: Array, trackers?: Array): void; +declare function addGlobalContexts(contexts: Array | Record, trackers?: Array): void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| contexts | Array<ConditionalContextProvider \| ContextPrimitive> | An array of contexts or conditional contexts | +| contexts | Array<ConditionalContextProvider \| ContextPrimitive> \| Record<string, ConditionalContextProvider \| ContextPrimitive> | An array of contexts or conditional contexts | | trackers | Array<string> | The tracker identifiers which the global contexts will be added to | Returns: diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.activatecoreplugin.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.activatecoreplugin.md new file mode 100644 index 000000000..2cc3b446e --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.activatecoreplugin.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [CorePlugin](./browser-tracker.coreplugin.md) > [activateCorePlugin](./browser-tracker.coreplugin.activatecoreplugin.md) + +## CorePlugin.activateCorePlugin property + +Called when the plugin is initialised during the trackerCore construction + +Signature: + +```typescript +activateCorePlugin?: (core: TrackerCore) => void; +``` + +## Remarks + +Use to capture the specific core instance for each instance of a core plugin + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.aftertrack.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.aftertrack.md new file mode 100644 index 000000000..bfd62b673 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.aftertrack.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [CorePlugin](./browser-tracker.coreplugin.md) > [afterTrack](./browser-tracker.coreplugin.aftertrack.md) + +## CorePlugin.afterTrack property + +Called just after the trackerCore callback fires + +Signature: + +```typescript +afterTrack?: (payload: Payload) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.beforetrack.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.beforetrack.md new file mode 100644 index 000000000..6e30d3045 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.beforetrack.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [CorePlugin](./browser-tracker.coreplugin.md) > [beforeTrack](./browser-tracker.coreplugin.beforetrack.md) + +## CorePlugin.beforeTrack property + +Called before the `filter` method is called and before the trackerCore callback fires (if the filter passes) + +Signature: + +```typescript +beforeTrack?: (payloadBuilder: PayloadBuilder) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.contexts.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.contexts.md new file mode 100644 index 000000000..d89bf1f7f --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.contexts.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [CorePlugin](./browser-tracker.coreplugin.md) > [contexts](./browser-tracker.coreplugin.contexts.md) + +## CorePlugin.contexts property + +Called when constructing the context for each event Useful for adding additional context to events + +Signature: + +```typescript +contexts?: () => SelfDescribingJson[]; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.filter.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.filter.md new file mode 100644 index 000000000..08c5dd447 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.filter.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [CorePlugin](./browser-tracker.coreplugin.md) > [filter](./browser-tracker.coreplugin.filter.md) + +## CorePlugin.filter property + +Called before the payload is sent to the callback to decide whether to send the payload or skip it + +Signature: + +```typescript +filter?: (payload: Payload) => boolean; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.logger.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.logger.md new file mode 100644 index 000000000..59ae6ae6e --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.logger.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [CorePlugin](./browser-tracker.coreplugin.md) > [logger](./browser-tracker.coreplugin.logger.md) + +## CorePlugin.logger property + +Passed a logger instance which can be used to send log information to the active logger + +Signature: + +```typescript +logger?: (logger: Logger) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.md new file mode 100644 index 000000000..55875f4c0 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.coreplugin.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [CorePlugin](./browser-tracker.coreplugin.md) + +## CorePlugin interface + +Interface which defines Core Plugins + +Signature: + +```typescript +interface CorePlugin +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [activateCorePlugin?](./browser-tracker.coreplugin.activatecoreplugin.md) | (core: TrackerCore) => void | (Optional) Called when the plugin is initialised during the trackerCore construction | +| [afterTrack?](./browser-tracker.coreplugin.aftertrack.md) | (payload: Payload) => void | (Optional) Called just after the trackerCore callback fires | +| [beforeTrack?](./browser-tracker.coreplugin.beforetrack.md) | (payloadBuilder: PayloadBuilder) => void | (Optional) Called before the filter method is called and before the trackerCore callback fires (if the filter passes) | +| [contexts?](./browser-tracker.coreplugin.contexts.md) | () => SelfDescribingJson\[\] | (Optional) Called when constructing the context for each event Useful for adding additional context to events | +| [filter?](./browser-tracker.coreplugin.filter.md) | (payload: Payload) => boolean | (Optional) Called before the payload is sent to the callback to decide whether to send the payload or skip it | +| [logger?](./browser-tracker.coreplugin.logger.md) | (logger: Logger) => void | (Optional) Passed a logger instance which can be used to send log information to the active logger | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.corepluginconfiguration.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.corepluginconfiguration.md new file mode 100644 index 000000000..223654314 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.corepluginconfiguration.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [CorePluginConfiguration](./browser-tracker.corepluginconfiguration.md) + +## CorePluginConfiguration interface + +The configuration of the plugin to add + +Signature: + +```typescript +interface CorePluginConfiguration +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [plugin](./browser-tracker.corepluginconfiguration.plugin.md) | CorePlugin | | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.corepluginconfiguration.plugin.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.corepluginconfiguration.plugin.md new file mode 100644 index 000000000..7131d2310 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.corepluginconfiguration.plugin.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [CorePluginConfiguration](./browser-tracker.corepluginconfiguration.md) > [plugin](./browser-tracker.corepluginconfiguration.plugin.md) + +## CorePluginConfiguration.plugin property + +Signature: + +```typescript +plugin: CorePlugin; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.devicetimestamp.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.devicetimestamp.md new file mode 100644 index 000000000..bd0fad3b4 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.devicetimestamp.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [DeviceTimestamp](./browser-tracker.devicetimestamp.md) + +## DeviceTimestamp interface + +A representation of a Device Timestamp (dtm) + +Signature: + +```typescript +interface DeviceTimestamp +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [type](./browser-tracker.devicetimestamp.type.md) | "dtm" | | +| [value](./browser-tracker.devicetimestamp.value.md) | number | | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.devicetimestamp.type.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.devicetimestamp.type.md new file mode 100644 index 000000000..59f6089c2 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.devicetimestamp.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [DeviceTimestamp](./browser-tracker.devicetimestamp.md) > [type](./browser-tracker.devicetimestamp.type.md) + +## DeviceTimestamp.type property + +Signature: + +```typescript +readonly type: "dtm"; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.devicetimestamp.value.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.devicetimestamp.value.md new file mode 100644 index 000000000..f0829f0dc --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.devicetimestamp.value.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [DeviceTimestamp](./browser-tracker.devicetimestamp.md) > [value](./browser-tracker.devicetimestamp.value.md) + +## DeviceTimestamp.value property + +Signature: + +```typescript +readonly value: number; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.buffersize.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.buffersize.md new file mode 100644 index 000000000..c094e7a7e --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.buffersize.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [bufferSize](./browser-tracker.emitterconfigurationbase.buffersize.md) + +## EmitterConfigurationBase.bufferSize property + +The amount of events that should be buffered before sending Recommended to leave as 1 to reduce change of losing events + +Signature: + +```typescript +bufferSize?: number; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.connectiontimeout.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.connectiontimeout.md new file mode 100644 index 000000000..d13a613d5 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.connectiontimeout.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [connectionTimeout](./browser-tracker.emitterconfigurationbase.connectiontimeout.md) + +## EmitterConfigurationBase.connectionTimeout property + +How long to wait before aborting requests to the collector + +Signature: + +```typescript +connectionTimeout?: number; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.credentials.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.credentials.md new file mode 100644 index 000000000..683b3a9cc --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.credentials.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [credentials](./browser-tracker.emitterconfigurationbase.credentials.md) + +## EmitterConfigurationBase.credentials property + +Controls whether or not the browser sends credentials (defaults to 'include') + +Signature: + +```typescript +credentials?: "omit" | "same-origin" | "include"; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.customfetch.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.customfetch.md new file mode 100644 index 000000000..4c4b6748c --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.customfetch.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [customFetch](./browser-tracker.emitterconfigurationbase.customfetch.md) + +## EmitterConfigurationBase.customFetch property + +Enables overriding the default fetch function with a custom implementation. + +Signature: + +```typescript +customFetch?: (input: Request, options?: RequestInit) => Promise; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.customheaders.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.customheaders.md new file mode 100644 index 000000000..0c10efa4b --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.customheaders.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [customHeaders](./browser-tracker.emitterconfigurationbase.customheaders.md) + +## EmitterConfigurationBase.customHeaders property + +An object of key value pairs which represent headers to attach when sending a POST request, only works for POST + +Signature: + +```typescript +customHeaders?: Record; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.dontretrystatuscodes.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.dontretrystatuscodes.md new file mode 100644 index 000000000..c98d09680 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.dontretrystatuscodes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [dontRetryStatusCodes](./browser-tracker.emitterconfigurationbase.dontretrystatuscodes.md) + +## EmitterConfigurationBase.dontRetryStatusCodes property + +List of HTTP response status codes for which events sent to Collector should not be retried in future request. Only non-success status codes are considered (greater or equal to 300). The don't retry codes are only considered for GET and POST requests. By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422 (these don't retry codes will remain even if you set your own `dontRetryStatusCodes` but can be changed using the `retryStatusCodes`). + +Signature: + +```typescript +dontRetryStatusCodes?: number[]; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.eventmethod.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.eventmethod.md new file mode 100644 index 000000000..eea7ad823 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.eventmethod.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [eventMethod](./browser-tracker.emitterconfigurationbase.eventmethod.md) + +## EmitterConfigurationBase.eventMethod property + +The preferred technique to use to send events + +Signature: + +```typescript +eventMethod?: EventMethod; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.eventstore.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.eventstore.md new file mode 100644 index 000000000..0ac1f659e --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.eventstore.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [eventStore](./browser-tracker.emitterconfigurationbase.eventstore.md) + +## EmitterConfigurationBase.eventStore property + +Enables providing a custom EventStore implementation to store events before sending them to the collector. + +Signature: + +```typescript +eventStore?: EventStore; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.idservice.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.idservice.md new file mode 100644 index 000000000..d0cb68a4f --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.idservice.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [idService](./browser-tracker.emitterconfigurationbase.idservice.md) + +## EmitterConfigurationBase.idService property + +Id service full URL. This URL will be added to the queue and will be called using a GET method. This option is there to allow the service URL to be called in order to set any required identifiers e.g. extra cookies. + +The request respects the `anonymousTracking` option, including the SP-Anonymous header if needed, and any additional custom headers from the customHeaders option. + +Signature: + +```typescript +idService?: string; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.keepalive.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.keepalive.md new file mode 100644 index 000000000..faf4e5faf --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.keepalive.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [keepalive](./browser-tracker.emitterconfigurationbase.keepalive.md) + +## EmitterConfigurationBase.keepalive property + +Indicates that the request should be allowed to outlive the webpage that initiated it. Enables collector requests to complete even if the page is closed or navigated away from. + +Signature: + +```typescript +keepalive?: boolean; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.maxgetbytes.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.maxgetbytes.md new file mode 100644 index 000000000..6d24229e2 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.maxgetbytes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [maxGetBytes](./browser-tracker.emitterconfigurationbase.maxgetbytes.md) + +## EmitterConfigurationBase.maxGetBytes property + +The max size a GET request (its complete URL) can be. Requests over this size will be tried as a POST request. + +Signature: + +```typescript +maxGetBytes?: number; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.maxpostbytes.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.maxpostbytes.md new file mode 100644 index 000000000..e655ed15c --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.maxpostbytes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [maxPostBytes](./browser-tracker.emitterconfigurationbase.maxpostbytes.md) + +## EmitterConfigurationBase.maxPostBytes property + +The max size a POST request can be before the tracker will force send it Also dictates the max size of a POST request before a batch of events is split into multiple requests + +Signature: + +```typescript +maxPostBytes?: number; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.md new file mode 100644 index 000000000..274189172 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) + +## EmitterConfigurationBase interface + +Signature: + +```typescript +interface EmitterConfigurationBase +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [bufferSize?](./browser-tracker.emitterconfigurationbase.buffersize.md) | number | (Optional) The amount of events that should be buffered before sending Recommended to leave as 1 to reduce change of losing events | +| [connectionTimeout?](./browser-tracker.emitterconfigurationbase.connectiontimeout.md) | number | (Optional) How long to wait before aborting requests to the collector | +| [credentials?](./browser-tracker.emitterconfigurationbase.credentials.md) | "omit" \| "same-origin" \| "include" | (Optional) Controls whether or not the browser sends credentials (defaults to 'include') | +| [customFetch?](./browser-tracker.emitterconfigurationbase.customfetch.md) | (input: Request, options?: RequestInit) => Promise<Response> | (Optional) Enables overriding the default fetch function with a custom implementation. | +| [customHeaders?](./browser-tracker.emitterconfigurationbase.customheaders.md) | Record<string, string> | (Optional) An object of key value pairs which represent headers to attach when sending a POST request, only works for POST | +| [dontRetryStatusCodes?](./browser-tracker.emitterconfigurationbase.dontretrystatuscodes.md) | number\[\] | (Optional) List of HTTP response status codes for which events sent to Collector should not be retried in future request. Only non-success status codes are considered (greater or equal to 300). The don't retry codes are only considered for GET and POST requests. By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422 (these don't retry codes will remain even if you set your own dontRetryStatusCodes but can be changed using the retryStatusCodes). | +| [eventMethod?](./browser-tracker.emitterconfigurationbase.eventmethod.md) | EventMethod | (Optional) The preferred technique to use to send events | +| [eventStore?](./browser-tracker.emitterconfigurationbase.eventstore.md) | EventStore | (Optional) Enables providing a custom EventStore implementation to store events before sending them to the collector. | +| [idService?](./browser-tracker.emitterconfigurationbase.idservice.md) | string | (Optional) Id service full URL. This URL will be added to the queue and will be called using a GET method. This option is there to allow the service URL to be called in order to set any required identifiers e.g. extra cookies.The request respects the anonymousTracking option, including the SP-Anonymous header if needed, and any additional custom headers from the customHeaders option. | +| [keepalive?](./browser-tracker.emitterconfigurationbase.keepalive.md) | boolean | (Optional) Indicates that the request should be allowed to outlive the webpage that initiated it. Enables collector requests to complete even if the page is closed or navigated away from. | +| [maxGetBytes?](./browser-tracker.emitterconfigurationbase.maxgetbytes.md) | number | (Optional) The max size a GET request (its complete URL) can be. Requests over this size will be tried as a POST request. | +| [maxPostBytes?](./browser-tracker.emitterconfigurationbase.maxpostbytes.md) | number | (Optional) The max size a POST request can be before the tracker will force send it Also dictates the max size of a POST request before a batch of events is split into multiple requests | +| [onRequestFailure?](./browser-tracker.emitterconfigurationbase.onrequestfailure.md) | (data: RequestFailure, response?: Response) => void | (Optional) A callback function to be executed whenever a request fails to be sent to the collector. This is the inverse of the onRequestSuccess callback, so any non 2xx status code will trigger this callback. | +| [onRequestSuccess?](./browser-tracker.emitterconfigurationbase.onrequestsuccess.md) | (data: EventBatch, response: Response) => void | (Optional) A callback function to be executed whenever a request is successfully sent to the collector. In practice this means any request which returns a 2xx status code will trigger this callback. | +| [postPath?](./browser-tracker.emitterconfigurationbase.postpath.md) | string | (Optional) The post path which events will be sent to. Ensure your collector is configured to accept events on this post path | +| [retryFailedRequests?](./browser-tracker.emitterconfigurationbase.retryfailedrequests.md) | boolean | (Optional) Whether to retry failed requests to the collector.Failed requests are requests that failed due to \[timeouts\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout\_event), \[network errors\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/error\_event), and \[abort events\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort\_event).Takes precedent over retryStatusCodes and dontRetryStatusCodes. | +| [retryStatusCodes?](./browser-tracker.emitterconfigurationbase.retrystatuscodes.md) | number\[\] | (Optional) List of HTTP response status codes for which events sent to Collector should be retried in future requests. Only non-success status codes are considered (greater or equal to 300). The retry codes are only considered for GET and POST requests. They take priority over the dontRetryStatusCodes option. By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422. | +| [useStm?](./browser-tracker.emitterconfigurationbase.usestm.md) | boolean | (Optional) Should the Sent Timestamp be attached to events. Only applies for GET events. | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.onrequestfailure.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.onrequestfailure.md new file mode 100644 index 000000000..38b53f7d9 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.onrequestfailure.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [onRequestFailure](./browser-tracker.emitterconfigurationbase.onrequestfailure.md) + +## EmitterConfigurationBase.onRequestFailure property + +A callback function to be executed whenever a request fails to be sent to the collector. This is the inverse of the onRequestSuccess callback, so any non 2xx status code will trigger this callback. + +Signature: + +```typescript +onRequestFailure?: (data: RequestFailure, response?: Response) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.onrequestsuccess.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.onrequestsuccess.md new file mode 100644 index 000000000..640824819 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.onrequestsuccess.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [onRequestSuccess](./browser-tracker.emitterconfigurationbase.onrequestsuccess.md) + +## EmitterConfigurationBase.onRequestSuccess property + +A callback function to be executed whenever a request is successfully sent to the collector. In practice this means any request which returns a 2xx status code will trigger this callback. + +Signature: + +```typescript +onRequestSuccess?: (data: EventBatch, response: Response) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.postpath.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.postpath.md new file mode 100644 index 000000000..aae87c281 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.postpath.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [postPath](./browser-tracker.emitterconfigurationbase.postpath.md) + +## EmitterConfigurationBase.postPath property + +The post path which events will be sent to. Ensure your collector is configured to accept events on this post path + +Signature: + +```typescript +postPath?: string; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.retryfailedrequests.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.retryfailedrequests.md new file mode 100644 index 000000000..196a6e7bc --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.retryfailedrequests.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [retryFailedRequests](./browser-tracker.emitterconfigurationbase.retryfailedrequests.md) + +## EmitterConfigurationBase.retryFailedRequests property + +Whether to retry failed requests to the collector. + +Failed requests are requests that failed due to \[timeouts\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout\_event), \[network errors\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/error\_event), and \[abort events\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort\_event). + +Takes precedent over `retryStatusCodes` and `dontRetryStatusCodes`. + +Signature: + +```typescript +retryFailedRequests?: boolean; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.retrystatuscodes.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.retrystatuscodes.md new file mode 100644 index 000000000..bd65300bc --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.retrystatuscodes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [retryStatusCodes](./browser-tracker.emitterconfigurationbase.retrystatuscodes.md) + +## EmitterConfigurationBase.retryStatusCodes property + +List of HTTP response status codes for which events sent to Collector should be retried in future requests. Only non-success status codes are considered (greater or equal to 300). The retry codes are only considered for GET and POST requests. They take priority over the `dontRetryStatusCodes` option. By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422. + +Signature: + +```typescript +retryStatusCodes?: number[]; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.usestm.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.usestm.md new file mode 100644 index 000000000..a678f8ec3 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.emitterconfigurationbase.usestm.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) > [useStm](./browser-tracker.emitterconfigurationbase.usestm.md) + +## EmitterConfigurationBase.useStm property + +Should the Sent Timestamp be attached to events. Only applies for GET events. + +Signature: + +```typescript +useStm?: boolean; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventbatch.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventbatch.md index 3c68f3a47..e5b3b7460 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventbatch.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventbatch.md @@ -4,10 +4,10 @@ ## EventBatch type -A collection of events which are sent to the collector. This can either be a collection of query strings or JSON objects. +A collection of event payloads which are sent to the collector. Signature: ```typescript -type EventBatch = GetBatch | PostBatch; +type EventBatch = Payload[]; ``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventjson.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventjson.md new file mode 100644 index 000000000..9984fb028 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventjson.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventJson](./browser-tracker.eventjson.md) + +## EventJson type + +An array of tuples which represents the unprocessed JSON to be added to the Payload + +Signature: + +```typescript +type EventJson = Array; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventmethod.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventmethod.md index 94dfa18d1..283adbbeb 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventmethod.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventmethod.md @@ -7,5 +7,5 @@ Signature: ```typescript -type EventMethod = "post" | "get" | "beacon"; +type EventMethod = "post" | "get"; ``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.add.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.add.md new file mode 100644 index 000000000..1e770e91a --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.add.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStore](./browser-tracker.eventstore.md) > [add](./browser-tracker.eventstore.add.md) + +## EventStore.add property + +Add an event to the store + +Signature: + +```typescript +add: (payload: EventStorePayload) => Promise; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.count.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.count.md new file mode 100644 index 000000000..b1c073d49 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.count.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStore](./browser-tracker.eventstore.md) > [count](./browser-tracker.eventstore.count.md) + +## EventStore.count property + +Count all events in the store + +Signature: + +```typescript +count: () => Promise; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.getall.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.getall.md new file mode 100644 index 000000000..295e07ced --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.getall.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStore](./browser-tracker.eventstore.md) > [getAll](./browser-tracker.eventstore.getall.md) + +## EventStore.getAll property + +Retrieve all payloads including their meta configuration in the store + +Signature: + +```typescript +getAll: () => Promise; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.getallpayloads.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.getallpayloads.md new file mode 100644 index 000000000..42d0c186e --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.getallpayloads.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStore](./browser-tracker.eventstore.md) > [getAllPayloads](./browser-tracker.eventstore.getallpayloads.md) + +## EventStore.getAllPayloads property + +Retrieve all pure payloads in the store + +Signature: + +```typescript +getAllPayloads: () => Promise; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.iterator.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.iterator.md new file mode 100644 index 000000000..70bb55e0d --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.iterator.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStore](./browser-tracker.eventstore.md) > [iterator](./browser-tracker.eventstore.iterator.md) + +## EventStore.iterator property + +Get an iterator over all events in the store + +Signature: + +```typescript +iterator: () => EventStoreIterator; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.md new file mode 100644 index 000000000..cbf5c602e --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStore](./browser-tracker.eventstore.md) + +## EventStore interface + +EventStore allows storing and retrieving events before they are sent to the collector + +Signature: + +```typescript +interface EventStore +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [add](./browser-tracker.eventstore.add.md) | (payload: EventStorePayload) => Promise<number> | Add an event to the store | +| [count](./browser-tracker.eventstore.count.md) | () => Promise<number> | Count all events in the store | +| [getAll](./browser-tracker.eventstore.getall.md) | () => Promise<readonly EventStorePayload\[\]> | Retrieve all payloads including their meta configuration in the store | +| [getAllPayloads](./browser-tracker.eventstore.getallpayloads.md) | () => Promise<readonly Payload\[\]> | Retrieve all pure payloads in the store | +| [iterator](./browser-tracker.eventstore.iterator.md) | () => EventStoreIterator | Get an iterator over all events in the store | +| [removeHead](./browser-tracker.eventstore.removehead.md) | (count: number) => Promise<void> | Remove the first count events from the store | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.removehead.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.removehead.md new file mode 100644 index 000000000..082aa93a2 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstore.removehead.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStore](./browser-tracker.eventstore.md) > [removeHead](./browser-tracker.eventstore.removehead.md) + +## EventStore.removeHead property + +Remove the first `count` events from the store + +Signature: + +```typescript +removeHead: (count: number) => Promise; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreconfiguration.maxsize.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreconfiguration.maxsize.md new file mode 100644 index 000000000..212e3d54f --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreconfiguration.maxsize.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStoreConfiguration](./browser-tracker.eventstoreconfiguration.md) > [maxSize](./browser-tracker.eventstoreconfiguration.maxsize.md) + +## EventStoreConfiguration.maxSize property + +The maximum amount of events that will be buffered in the event store + +This is useful to ensure the Tracker doesn't fill the 5MB or 10MB available to each website should the collector be unavailable due to lost connectivity. Will drop old events once the limit is hit + +Signature: + +```typescript +maxSize?: number; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreconfiguration.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreconfiguration.md new file mode 100644 index 000000000..3993ed5e1 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreconfiguration.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStoreConfiguration](./browser-tracker.eventstoreconfiguration.md) + +## EventStoreConfiguration interface + +Signature: + +```typescript +interface EventStoreConfiguration +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [maxSize?](./browser-tracker.eventstoreconfiguration.maxsize.md) | number | (Optional) The maximum amount of events that will be buffered in the event storeThis is useful to ensure the Tracker doesn't fill the 5MB or 10MB available to each website should the collector be unavailable due to lost connectivity. Will drop old events once the limit is hit | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreiterator.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreiterator.md new file mode 100644 index 000000000..f234a8cda --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreiterator.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStoreIterator](./browser-tracker.eventstoreiterator.md) + +## EventStoreIterator interface + +Signature: + +```typescript +interface EventStoreIterator +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [next](./browser-tracker.eventstoreiterator.next.md) | () => Promise<{ value: EventStorePayload \| undefined; done: boolean; }> | Retrieve the next event in the store | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreiterator.next.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreiterator.next.md new file mode 100644 index 000000000..31d93ab25 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstoreiterator.next.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStoreIterator](./browser-tracker.eventstoreiterator.md) > [next](./browser-tracker.eventstoreiterator.next.md) + +## EventStoreIterator.next property + +Retrieve the next event in the store + +Signature: + +```typescript +next: () => Promise<{ + value: EventStorePayload | undefined; + done: boolean; + }>; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstorepayload.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstorepayload.md new file mode 100644 index 000000000..09cad3307 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstorepayload.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStorePayload](./browser-tracker.eventstorepayload.md) + +## EventStorePayload interface + +Signature: + +```typescript +interface EventStorePayload +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [payload](./browser-tracker.eventstorepayload.payload.md) | Payload | The event payload to be stored | +| [svrAnon?](./browser-tracker.eventstorepayload.svranon.md) | boolean | (Optional) If the request should undergo server anonymization. | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstorepayload.payload.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstorepayload.payload.md new file mode 100644 index 000000000..837ff1e5d --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstorepayload.payload.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStorePayload](./browser-tracker.eventstorepayload.md) > [payload](./browser-tracker.eventstorepayload.payload.md) + +## EventStorePayload.payload property + +The event payload to be stored + +Signature: + +```typescript +payload: Payload; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstorepayload.svranon.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstorepayload.svranon.md new file mode 100644 index 000000000..d1a6c44ce --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.eventstorepayload.svranon.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [EventStorePayload](./browser-tracker.eventstorepayload.md) > [svrAnon](./browser-tracker.eventstorepayload.svranon.md) + +## EventStorePayload.svrAnon property + +If the request should undergo server anonymization. + +Signature: + +```typescript +svrAnon?: boolean; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.getbatch.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.getbatch.md deleted file mode 100644 index 6af01ced0..000000000 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.getbatch.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [GetBatch](./browser-tracker.getbatch.md) - -## GetBatch type - -A collection of GET events which are sent to the collector. This will be a collection of query strings. - -Signature: - -```typescript -type GetBatch = string[]; -``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.jsonprocessor.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.jsonprocessor.md new file mode 100644 index 000000000..26e8bf8e0 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.jsonprocessor.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [JsonProcessor](./browser-tracker.jsonprocessor.md) + +## JsonProcessor type + +A function which will processor the Json onto the injected PayloadBuilder + +Signature: + +```typescript +type JsonProcessor = (payloadBuilder: PayloadBuilder, jsonForProcessing: EventJson, contextEntitiesForProcessing: SelfDescribingJson[]) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.localstorageeventstoreconfigurationbase.maxlocalstoragequeuesize.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.localstorageeventstoreconfigurationbase.maxlocalstoragequeuesize.md new file mode 100644 index 000000000..0ef04bc99 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.localstorageeventstoreconfigurationbase.maxlocalstoragequeuesize.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [LocalStorageEventStoreConfigurationBase](./browser-tracker.localstorageeventstoreconfigurationbase.md) > [maxLocalStorageQueueSize](./browser-tracker.localstorageeventstoreconfigurationbase.maxlocalstoragequeuesize.md) + +## LocalStorageEventStoreConfigurationBase.maxLocalStorageQueueSize property + +The maximum amount of events that will be buffered in local storage + +This is useful to ensure the Tracker doesn't fill the 5MB or 10MB available to each website should the collector be unavailable due to lost connectivity. Will drop events once the limit is hit + +Signature: + +```typescript +maxLocalStorageQueueSize?: number; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.localstorageeventstoreconfigurationbase.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.localstorageeventstoreconfigurationbase.md new file mode 100644 index 000000000..6d78a2a50 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.localstorageeventstoreconfigurationbase.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [LocalStorageEventStoreConfigurationBase](./browser-tracker.localstorageeventstoreconfigurationbase.md) + +## LocalStorageEventStoreConfigurationBase interface + +Signature: + +```typescript +interface LocalStorageEventStoreConfigurationBase extends EventStoreConfiguration +``` +Extends: EventStoreConfiguration + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [maxLocalStorageQueueSize?](./browser-tracker.localstorageeventstoreconfigurationbase.maxlocalstoragequeuesize.md) | number | (Optional) The maximum amount of events that will be buffered in local storageThis is useful to ensure the Tracker doesn't fill the 5MB or 10MB available to each website should the collector be unavailable due to lost connectivity. Will drop events once the limit is hit | +| [useLocalStorage?](./browser-tracker.localstorageeventstoreconfigurationbase.uselocalstorage.md) | boolean | (Optional) Whether to use localStorage at all Default is true | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.localstorageeventstoreconfigurationbase.uselocalstorage.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.localstorageeventstoreconfigurationbase.uselocalstorage.md new file mode 100644 index 000000000..da464b7cc --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.localstorageeventstoreconfigurationbase.uselocalstorage.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [LocalStorageEventStoreConfigurationBase](./browser-tracker.localstorageeventstoreconfigurationbase.md) > [useLocalStorage](./browser-tracker.localstorageeventstoreconfigurationbase.uselocalstorage.md) + +## LocalStorageEventStoreConfigurationBase.useLocalStorage property + +Whether to use localStorage at all Default is true + +Signature: + +```typescript +useLocalStorage?: boolean; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.debug.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.debug.md new file mode 100644 index 000000000..1167bd49d --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.debug.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [Logger](./browser-tracker.logger.md) > [debug](./browser-tracker.logger.debug.md) + +## Logger.debug property + +Signature: + +```typescript +debug: (message: string, ...extraParams: unknown[]) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.error.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.error.md new file mode 100644 index 000000000..6e745f314 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.error.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [Logger](./browser-tracker.logger.md) > [error](./browser-tracker.logger.error.md) + +## Logger.error property + +Signature: + +```typescript +error: (message: string, error?: unknown, ...extraParams: unknown[]) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.info.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.info.md new file mode 100644 index 000000000..0476b618e --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.info.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [Logger](./browser-tracker.logger.md) > [info](./browser-tracker.logger.info.md) + +## Logger.info property + +Signature: + +```typescript +info: (message: string, ...extraParams: unknown[]) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.md new file mode 100644 index 000000000..3161b8eeb --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [Logger](./browser-tracker.logger.md) + +## Logger interface + +Signature: + +```typescript +interface Logger +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [debug](./browser-tracker.logger.debug.md) | (message: string, ...extraParams: unknown\[\]) => void | | +| [error](./browser-tracker.logger.error.md) | (message: string, error?: unknown, ...extraParams: unknown\[\]) => void | | +| [info](./browser-tracker.logger.info.md) | (message: string, ...extraParams: unknown\[\]) => void | | +| [setLogLevel](./browser-tracker.logger.setloglevel.md) | (level: LOG\_LEVEL) => void | | +| [warn](./browser-tracker.logger.warn.md) | (message: string, error?: unknown, ...extraParams: unknown\[\]) => void | | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.setloglevel.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.setloglevel.md new file mode 100644 index 000000000..d16d6b118 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.setloglevel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [Logger](./browser-tracker.logger.md) > [setLogLevel](./browser-tracker.logger.setloglevel.md) + +## Logger.setLogLevel property + +Signature: + +```typescript +setLogLevel: (level: LOG_LEVEL) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.warn.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.warn.md new file mode 100644 index 000000000..de43906c0 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.logger.warn.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [Logger](./browser-tracker.logger.md) > [warn](./browser-tracker.logger.warn.md) + +## Logger.warn property + +Signature: + +```typescript +warn: (message: string, error?: unknown, ...extraParams: unknown[]) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.md index 58669c275..1e475e6de 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.md @@ -4,6 +4,12 @@ ## browser-tracker package +## Classes + +| Class | Description | +| --- | --- | +| [SharedState](./browser-tracker.sharedstate.md) | A set of variables which are shared among all initialised trackers | + ## Functions | Function | Description | @@ -23,8 +29,7 @@ | [enableAnonymousTracking(configuration, trackers)](./browser-tracker.enableanonymoustracking.md) | Enables anonymous tracking (ie. tracker initialized without anonymousTracking) | | [flushBuffer(configuration, trackers)](./browser-tracker.flushbuffer.md) | Send all events in the outQueue Only need to use this when sending events with a bufferSize of at least 2 | | [newSession(trackers)](./browser-tracker.newsession.md) | Expires current session and starts a new session. | -| [newTracker(trackerId, endpoint)](./browser-tracker.newtracker.md) | Initialise a new tracker | -| [newTracker(trackerId, endpoint, configuration)](./browser-tracker.newtracker_1.md) | Initialise a new tracker | +| [newTracker(trackerId, endpoint, configuration)](./browser-tracker.newtracker.md) | Initialise a new tracker | | [preservePageViewId(trackers)](./browser-tracker.preservepageviewid.md) | Stop regenerating pageViewId (available from web_page context) | | [removeGlobalContexts(contexts, trackers)](./browser-tracker.removeglobalcontexts.md) | All provided contexts will no longer be sent with every event | | [setBufferSize(newBufferSize, trackers)](./browser-tracker.setbuffersize.md) | Set the buffer size Can be useful if you want to stop batching requests to ensure events start sending closer to event creation | @@ -57,14 +62,27 @@ | [ClientSession](./browser-tracker.clientsession.md) | Schema for client client session context entity | | [CommonEventProperties](./browser-tracker.commoneventproperties.md) | Additional data points to set when tracking an event | | [ContextEvent](./browser-tracker.contextevent.md) | Argument for [ContextGenerator](./browser-tracker.contextgenerator.md) and [ContextFilter](./browser-tracker.contextfilter.md) callback | +| [CorePlugin](./browser-tracker.coreplugin.md) | Interface which defines Core Plugins | +| [CorePluginConfiguration](./browser-tracker.corepluginconfiguration.md) | The configuration of the plugin to add | +| [DeviceTimestamp](./browser-tracker.devicetimestamp.md) | A representation of a Device Timestamp (dtm) | | [DisableAnonymousTrackingConfiguration](./browser-tracker.disableanonymoustrackingconfiguration.md) | The configuration that can be changed when disabling anonymous tracking | +| [EmitterConfigurationBase](./browser-tracker.emitterconfigurationbase.md) | | | [EnableAnonymousTrackingConfiguration](./browser-tracker.enableanonymoustrackingconfiguration.md) | The configuration that can be changed when enabling anonymous tracking | | [EventPayloadAndContext](./browser-tracker.eventpayloadandcontext.md) | Interface for returning a built event (PayloadBuilder) and context (Array of SelfDescribingJson). | +| [EventStore](./browser-tracker.eventstore.md) | EventStore allows storing and retrieving events before they are sent to the collector | +| [EventStoreConfiguration](./browser-tracker.eventstoreconfiguration.md) | | +| [EventStoreIterator](./browser-tracker.eventstoreiterator.md) | | +| [EventStorePayload](./browser-tracker.eventstorepayload.md) | | | [FlushBufferConfiguration](./browser-tracker.flushbufferconfiguration.md) | The configuration that can be changed when flushing the buffer | +| [LocalStorageEventStoreConfigurationBase](./browser-tracker.localstorageeventstoreconfigurationbase.md) | | +| [Logger](./browser-tracker.logger.md) | | | [PageViewEvent](./browser-tracker.pageviewevent.md) | A Page View event Used for tracking a page view | +| [PayloadBuilder](./browser-tracker.payloadbuilder.md) | Interface for mutable object encapsulating tracker payload | | [RuleSet](./browser-tracker.ruleset.md) | A ruleset has accept or reject properties that contain rules for matching Iglu schema URIs | | [SelfDescribingEvent](./browser-tracker.selfdescribingevent.md) | A Self Describing Event A custom event type, allowing for an event to be tracked using your own custom schema and a data object which conforms to the supplied schema | | [StructuredEvent](./browser-tracker.structuredevent.md) | A Structured Event A classic style of event tracking, allows for easier movement between analytics systems. A loosely typed event, creating a Self Describing event is preferred, but useful for interoperability. | +| [TrackerCore](./browser-tracker.trackercore.md) | Export interface containing all Core functions | +| [TrueTimestamp](./browser-tracker.truetimestamp.md) | A representation of a True Timestamp (ttm) | ## Variables @@ -85,19 +103,21 @@ | [ContextGenerator](./browser-tracker.contextgenerator.md) | A context generator is a user-supplied callback that is evaluated for each event to allow an additional context to be dynamically attached to the event | | [ContextPrimitive](./browser-tracker.contextprimitive.md) | A context primitive is either a self-describing JSON or a context generator | | [CookieSameSite](./browser-tracker.cookiesamesite.md) | | -| [EventBatch](./browser-tracker.eventbatch.md) | A collection of events which are sent to the collector. This can either be a collection of query strings or JSON objects. | +| [EventBatch](./browser-tracker.eventbatch.md) | A collection of event payloads which are sent to the collector. | +| [EventJson](./browser-tracker.eventjson.md) | An array of tuples which represents the unprocessed JSON to be added to the Payload | | [EventMethod](./browser-tracker.eventmethod.md) | | | [ExtendedCrossDomainLinkerAttributes](./browser-tracker.extendedcrossdomainlinkerattributes.md) | | | [ExtendedCrossDomainLinkerOptions](./browser-tracker.extendedcrossdomainlinkeroptions.md) | | | [FilterProvider](./browser-tracker.filterprovider.md) | A filter provider is a tuple that has two parts: a context filter and the context primitive(s) If the context filter evaluates to true, the tracker will attach the context primitive(s) | -| [GetBatch](./browser-tracker.getbatch.md) | A collection of GET events which are sent to the collector. This will be a collection of query strings. | +| [JsonProcessor](./browser-tracker.jsonprocessor.md) | A function which will processor the Json onto the injected PayloadBuilder | | [ParsedIdCookie](./browser-tracker.parsedidcookie.md) | The format of state elements stored in the id cookie. | +| [Payload](./browser-tracker.payload.md) | Type for a Payload dictionary | | [Platform](./browser-tracker.platform.md) | | -| [PostBatch](./browser-tracker.postbatch.md) | A collection of POST events which are sent to the collector. This will be a collection of JSON objects. | | [PreservePageViewIdForUrl](./browser-tracker.preservepageviewidforurl.md) | | | [RequestFailure](./browser-tracker.requestfailure.md) | The data that will be available to the onRequestFailure callback | | [RuleSetProvider](./browser-tracker.rulesetprovider.md) | A ruleset provider is aa tuple that has two parts: a ruleset and the context primitive(s) If the ruleset allows the current event schema URI, the tracker will attach the context primitive(s) | | [SelfDescribingJson](./browser-tracker.selfdescribingjson.md) | Export interface for any Self-Describing JSON such as context or Self Describing events | | [StateStorageStrategy](./browser-tracker.statestoragestrategy.md) | | +| [Timestamp](./browser-tracker.timestamp.md) | Algebraic datatype representing possible timestamp type choice | | [TrackerConfiguration](./browser-tracker.trackerconfiguration.md) | The configuration object for initialising the tracker | diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.newtracker.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.newtracker.md index 446ff6375..23d477b89 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.newtracker.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.newtracker.md @@ -9,7 +9,7 @@ Initialise a new tracker Signature: ```typescript -declare function newTracker(trackerId: string, endpoint: string): BrowserTracker; +declare function newTracker(trackerId: string, endpoint: string, configuration?: TrackerConfiguration): BrowserTracker | null | undefined; ``` ## Parameters @@ -18,8 +18,9 @@ declare function newTracker(trackerId: string, endpoint: string): BrowserTracker | --- | --- | --- | | trackerId | string | The tracker id - also known as tracker namespace | | endpoint | string | Collector endpoint in the form collector.mysite.com | +| configuration | TrackerConfiguration | The initialisation options of the tracker | Returns: -BrowserTracker +BrowserTracker \| null \| undefined diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.newtracker_1.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.newtracker_1.md deleted file mode 100644 index ac03fc02a..000000000 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.newtracker_1.md +++ /dev/null @@ -1,26 +0,0 @@ - - -[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [newTracker](./browser-tracker.newtracker_1.md) - -## newTracker() function - -Initialise a new tracker - -Signature: - -```typescript -declare function newTracker(trackerId: string, endpoint: string, configuration: TrackerConfiguration): BrowserTracker; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| trackerId | string | The tracker id - also known as tracker namespace | -| endpoint | string | Collector endpoint in the form collector.mysite.com | -| configuration | TrackerConfiguration | The initialisation options of the tracker | - -Returns: - -BrowserTracker - diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payload.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payload.md new file mode 100644 index 000000000..366651db0 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payload.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [Payload](./browser-tracker.payload.md) + +## Payload type + +Type for a Payload dictionary + +Signature: + +```typescript +type Payload = Record; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.add.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.add.md new file mode 100644 index 000000000..2406f791e --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.add.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PayloadBuilder](./browser-tracker.payloadbuilder.md) > [add](./browser-tracker.payloadbuilder.add.md) + +## PayloadBuilder.add property + +Adds an entry to the Payload + +Signature: + +```typescript +add: (key: string, value: unknown) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.addcontextentity.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.addcontextentity.md new file mode 100644 index 000000000..d4d8da13c --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.addcontextentity.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PayloadBuilder](./browser-tracker.payloadbuilder.md) > [addContextEntity](./browser-tracker.payloadbuilder.addcontextentity.md) + +## PayloadBuilder.addContextEntity property + +Caches a context entity to be added to payload on build + +Signature: + +```typescript +addContextEntity: (entity: SelfDescribingJson) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.adddict.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.adddict.md new file mode 100644 index 000000000..26ede193c --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.adddict.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PayloadBuilder](./browser-tracker.payloadbuilder.md) > [addDict](./browser-tracker.payloadbuilder.adddict.md) + +## PayloadBuilder.addDict property + +Merges a payload into the existing payload + +Signature: + +```typescript +addDict: (dict: Payload) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.addjson.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.addjson.md new file mode 100644 index 000000000..810e91371 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.addjson.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PayloadBuilder](./browser-tracker.payloadbuilder.md) > [addJson](./browser-tracker.payloadbuilder.addjson.md) + +## PayloadBuilder.addJson property + +Caches a JSON object to be added to payload on build + +Signature: + +```typescript +addJson: (keyIfEncoded: string, keyIfNotEncoded: string, json: Record) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.build.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.build.md new file mode 100644 index 000000000..223be1ce0 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.build.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PayloadBuilder](./browser-tracker.payloadbuilder.md) > [build](./browser-tracker.payloadbuilder.build.md) + +## PayloadBuilder.build property + +Builds and returns the Payload + +Signature: + +```typescript +build: () => Payload; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.getjson.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.getjson.md new file mode 100644 index 000000000..aa7f1962f --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.getjson.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PayloadBuilder](./browser-tracker.payloadbuilder.md) > [getJson](./browser-tracker.payloadbuilder.getjson.md) + +## PayloadBuilder.getJson property + +Gets all JSON objects added to payload + +Signature: + +```typescript +getJson: () => EventJson; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.getpayload.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.getpayload.md new file mode 100644 index 000000000..b1e99b19a --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.getpayload.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PayloadBuilder](./browser-tracker.payloadbuilder.md) > [getPayload](./browser-tracker.payloadbuilder.getpayload.md) + +## PayloadBuilder.getPayload property + +Gets the current payload, before cached JSON is processed + +Signature: + +```typescript +getPayload: () => Payload; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.md new file mode 100644 index 000000000..da88b4627 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PayloadBuilder](./browser-tracker.payloadbuilder.md) + +## PayloadBuilder interface + +Interface for mutable object encapsulating tracker payload + +Signature: + +```typescript +interface PayloadBuilder +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [add](./browser-tracker.payloadbuilder.add.md) | (key: string, value: unknown) => void | Adds an entry to the Payload | +| [addContextEntity](./browser-tracker.payloadbuilder.addcontextentity.md) | (entity: SelfDescribingJson) => void | Caches a context entity to be added to payload on build | +| [addDict](./browser-tracker.payloadbuilder.adddict.md) | (dict: Payload) => void | Merges a payload into the existing payload | +| [addJson](./browser-tracker.payloadbuilder.addjson.md) | (keyIfEncoded: string, keyIfNotEncoded: string, json: Record<string, unknown>) => void | Caches a JSON object to be added to payload on build | +| [build](./browser-tracker.payloadbuilder.build.md) | () => Payload | Builds and returns the Payload | +| [getJson](./browser-tracker.payloadbuilder.getjson.md) | () => EventJson | Gets all JSON objects added to payload | +| [getPayload](./browser-tracker.payloadbuilder.getpayload.md) | () => Payload | Gets the current payload, before cached JSON is processed | +| [withJsonProcessor](./browser-tracker.payloadbuilder.withjsonprocessor.md) | (jsonProcessor: JsonProcessor) => void | Adds a function which will be executed when building the payload to process the JSON which has been added to this payload | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.withjsonprocessor.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.withjsonprocessor.md new file mode 100644 index 000000000..08d3f2cf3 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.payloadbuilder.withjsonprocessor.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PayloadBuilder](./browser-tracker.payloadbuilder.md) > [withJsonProcessor](./browser-tracker.payloadbuilder.withjsonprocessor.md) + +## PayloadBuilder.withJsonProcessor property + +Adds a function which will be executed when building the payload to process the JSON which has been added to this payload + +Signature: + +```typescript +withJsonProcessor: (jsonProcessor: JsonProcessor) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.postbatch.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.postbatch.md deleted file mode 100644 index 223e63e5d..000000000 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.postbatch.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [PostBatch](./browser-tracker.postbatch.md) - -## PostBatch type - -A collection of POST events which are sent to the collector. This will be a collection of JSON objects. - -Signature: - -```typescript -type PostBatch = Record[]; -``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.removeglobalcontexts.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.removeglobalcontexts.md index 35eece5fb..508e042fd 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.removeglobalcontexts.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.removeglobalcontexts.md @@ -9,14 +9,14 @@ All provided contexts will no longer be sent with every event Signature: ```typescript -declare function removeGlobalContexts(contexts: Array, trackers?: Array): void; +declare function removeGlobalContexts(contexts: Array, trackers?: Array): void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| contexts | Array<ConditionalContextProvider \| ContextPrimitive> | An array of contexts or conditional contexts | +| contexts | Array<ConditionalContextProvider \| ContextPrimitive \| string> | An array of contexts or conditional contexts | | trackers | Array<string> | The tracker identifiers which the global contexts will be remove from | Returns: diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingevent.event.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingevent.event.md index 872d5408d..6c39ebd8b 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingevent.event.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingevent.event.md @@ -9,5 +9,5 @@ The Self Describing JSON which describes the event Signature: ```typescript -event: SelfDescribingJson; +event: SelfDescribingJson; ``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingevent.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingevent.md index 4a84a8467..32a77cd49 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingevent.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingevent.md @@ -9,12 +9,12 @@ A Self Describing Event A custom event type, allowing for an event to be tracked Signature: ```typescript -interface SelfDescribingEvent +interface SelfDescribingEvent> ``` ## Properties | Property | Type | Description | | --- | --- | --- | -| [event](./browser-tracker.selfdescribingevent.event.md) | SelfDescribingJson | The Self Describing JSON which describes the event | +| [event](./browser-tracker.selfdescribingevent.event.md) | SelfDescribingJson<T> | The Self Describing JSON which describes the event | diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingjson.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingjson.md index 8d2fceddb..6033c9791 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingjson.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.selfdescribingjson.md @@ -9,8 +9,8 @@ Export interface for any Self-Describing JSON such as context or Self Describing Signature: ```typescript -type SelfDescribingJson = Record> = { +type SelfDescribingJson> = { schema: string; - data: T; + data: T extends any[] ? never : T extends {} ? T : never; }; ``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.bufferflushers.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.bufferflushers.md new file mode 100644 index 000000000..fd15315d5 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.bufferflushers.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [SharedState](./browser-tracker.sharedstate.md) > [bufferFlushers](./browser-tracker.sharedstate.bufferflushers.md) + +## SharedState.bufferFlushers property + +Signature: + +```typescript +bufferFlushers: Array<(sync: boolean) => void>; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.hasloaded.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.hasloaded.md new file mode 100644 index 000000000..0a72f97a9 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.hasloaded.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [SharedState](./browser-tracker.sharedstate.md) > [hasLoaded](./browser-tracker.sharedstate.hasloaded.md) + +## SharedState.hasLoaded property + +Signature: + +```typescript +hasLoaded: boolean; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.md new file mode 100644 index 000000000..853f0e674 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [SharedState](./browser-tracker.sharedstate.md) + +## SharedState class + +A set of variables which are shared among all initialised trackers + +Signature: + +```typescript +declare class SharedState +``` + +## Properties + +| Property | Modifiers | Type | Description | +| --- | --- | --- | --- | +| [bufferFlushers](./browser-tracker.sharedstate.bufferflushers.md) | | Array<(sync: boolean) => void> | | +| [hasLoaded](./browser-tracker.sharedstate.hasloaded.md) | | boolean | | +| [pageViewId?](./browser-tracker.sharedstate.pageviewid.md) | | string | (Optional) | +| [pageViewUrl?](./browser-tracker.sharedstate.pageviewurl.md) | | string | (Optional) | +| [registeredOnLoadHandlers](./browser-tracker.sharedstate.registeredonloadhandlers.md) | | Array<() => void> | | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.pageviewid.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.pageviewid.md new file mode 100644 index 000000000..918e19447 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.pageviewid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [SharedState](./browser-tracker.sharedstate.md) > [pageViewId](./browser-tracker.sharedstate.pageviewid.md) + +## SharedState.pageViewId property + +Signature: + +```typescript +pageViewId?: string; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.pageviewurl.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.pageviewurl.md new file mode 100644 index 000000000..836702227 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.pageviewurl.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [SharedState](./browser-tracker.sharedstate.md) > [pageViewUrl](./browser-tracker.sharedstate.pageviewurl.md) + +## SharedState.pageViewUrl property + +Signature: + +```typescript +pageViewUrl?: string; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.registeredonloadhandlers.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.registeredonloadhandlers.md new file mode 100644 index 000000000..dca205808 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.sharedstate.registeredonloadhandlers.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [SharedState](./browser-tracker.sharedstate.md) > [registeredOnLoadHandlers](./browser-tracker.sharedstate.registeredonloadhandlers.md) + +## SharedState.registeredOnLoadHandlers property + +Signature: + +```typescript +registeredOnLoadHandlers: Array<() => void>; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.timestamp.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.timestamp.md new file mode 100644 index 000000000..0822e3f3b --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.timestamp.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [Timestamp](./browser-tracker.timestamp.md) + +## Timestamp type + +Algebraic datatype representing possible timestamp type choice + +Signature: + +```typescript +type Timestamp = TrueTimestamp | DeviceTimestamp | number; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackerconfiguration.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackerconfiguration.md index dbd899188..04b2fafda 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackerconfiguration.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackerconfiguration.md @@ -16,37 +16,22 @@ type TrackerConfiguration = { cookieSameSite?: CookieSameSite; cookieSecure?: boolean; cookieLifetime?: number; - withCredentials?: boolean; sessionCookieTimeout?: number; appId?: string; platform?: Platform; respectDoNotTrack?: boolean; - eventMethod?: EventMethod; - postPath?: string; - useStm?: boolean; - bufferSize?: number; crossDomainLinker?: (elt: HTMLAnchorElement | HTMLAreaElement) => boolean; useExtendedCrossDomainLinker?: ExtendedCrossDomainLinkerOptions; - maxPostBytes?: number; - maxGetBytes?: number; discoverRootDomain?: boolean; stateStorageStrategy?: StateStorageStrategy; - maxLocalStorageQueueSize?: number; resetActivityTrackingOnPageView?: boolean; - connectionTimeout?: number; anonymousTracking?: AnonymousTrackingOptions; contexts?: BuiltInContexts; plugins?: Array; - customHeaders?: Record; - retryStatusCodes?: number[]; - dontRetryStatusCodes?: number[]; onSessionUpdateCallback?: (updatedSession: ClientSession) => void; - idService?: string; - retryFailedRequests?: boolean; - onRequestSuccess?: (data: EventBatch) => void; - onRequestFailure?: (data: RequestFailure) => void; preservePageViewIdForUrl?: PreservePageViewIdForUrl; -}; + synchronousCookieWrite?: boolean; +} & EmitterConfigurationBase & LocalStorageEventStoreConfigurationBase; ``` ## Example @@ -59,6 +44,5 @@ newTracker('sp1', 'collector.my-website.com', { plugins: [ PerformanceTimingPlugin(), AdTrackingPlugin() ], stateStorageStrategy: 'cookieAndLocalStorage' }); - ``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addglobalcontexts.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addglobalcontexts.md new file mode 100644 index 000000000..e3cae4eff --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addglobalcontexts.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [addGlobalContexts](./browser-tracker.trackercore.addglobalcontexts.md) + +## TrackerCore.addGlobalContexts() method + +Adds contexts globally, contexts added here will be attached to all applicable events + +Signature: + +```typescript +addGlobalContexts(contexts: Array | Record): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| contexts | Array<ConditionalContextProvider \| ContextPrimitive> \| Record<string, ConditionalContextProvider \| ContextPrimitive> | An array containing either contexts or a conditional contexts | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addpayloaddict.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addpayloaddict.md new file mode 100644 index 000000000..8534971a3 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addpayloaddict.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [addPayloadDict](./browser-tracker.trackercore.addpayloaddict.md) + +## TrackerCore.addPayloadDict() method + +Merges a dictionary into payloadPairs + +Signature: + +```typescript +addPayloadDict(dict: Payload): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| dict | Payload | Adds a new payload dictionary to the existing one | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addpayloadpair.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addpayloadpair.md new file mode 100644 index 000000000..bfc992f48 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addpayloadpair.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [addPayloadPair](./browser-tracker.trackercore.addpayloadpair.md) + +## TrackerCore.addPayloadPair property + +Set a persistent key-value pair to be added to every payload + +Signature: + +```typescript +addPayloadPair: (key: string, value: unknown) => void; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addplugin.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addplugin.md new file mode 100644 index 000000000..f58e4c2c6 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.addplugin.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [addPlugin](./browser-tracker.trackercore.addplugin.md) + +## TrackerCore.addPlugin() method + +Add a plugin into the plugin collection after Core has already been initialised + +Signature: + +```typescript +addPlugin(configuration: CorePluginConfiguration): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| configuration | CorePluginConfiguration | The plugin to add | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.clearglobalcontexts.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.clearglobalcontexts.md new file mode 100644 index 000000000..d0d6e8ef0 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.clearglobalcontexts.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [clearGlobalContexts](./browser-tracker.trackercore.clearglobalcontexts.md) + +## TrackerCore.clearGlobalContexts() method + +Removes all global contexts + +Signature: + +```typescript +clearGlobalContexts(): void; +``` +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.getbase64encoding.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.getbase64encoding.md new file mode 100644 index 000000000..721440dbb --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.getbase64encoding.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [getBase64Encoding](./browser-tracker.trackercore.getbase64encoding.md) + +## TrackerCore.getBase64Encoding() method + +Get current base64 encoding state + +Signature: + +```typescript +getBase64Encoding(): boolean; +``` +Returns: + +boolean + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.md new file mode 100644 index 000000000..e2c9f5afd --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.md @@ -0,0 +1,46 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) + +## TrackerCore interface + +Export interface containing all Core functions + +Signature: + +```typescript +interface TrackerCore +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [addPayloadPair](./browser-tracker.trackercore.addpayloadpair.md) | (key: string, value: unknown) => void | Set a persistent key-value pair to be added to every payload | +| [track](./browser-tracker.trackercore.track.md) | (pb: PayloadBuilder, context?: Array<SelfDescribingJson> \| null, timestamp?: Timestamp \| null) => Payload \| undefined | Call with a payload from a buildX function Adds context and payloadPairs name-value pairs to the payload Applies the callback to the built payload | + +## Methods + +| Method | Description | +| --- | --- | +| [addGlobalContexts(contexts)](./browser-tracker.trackercore.addglobalcontexts.md) | Adds contexts globally, contexts added here will be attached to all applicable events | +| [addPayloadDict(dict)](./browser-tracker.trackercore.addpayloaddict.md) | Merges a dictionary into payloadPairs | +| [addPlugin(configuration)](./browser-tracker.trackercore.addplugin.md) | Add a plugin into the plugin collection after Core has already been initialised | +| [clearGlobalContexts()](./browser-tracker.trackercore.clearglobalcontexts.md) | Removes all global contexts | +| [getBase64Encoding()](./browser-tracker.trackercore.getbase64encoding.md) | Get current base64 encoding state | +| [removeGlobalContexts(contexts)](./browser-tracker.trackercore.removeglobalcontexts.md) | Removes previously added global context, performs a deep comparison of the contexts or conditional contexts | +| [resetPayloadPairs(dict)](./browser-tracker.trackercore.resetpayloadpairs.md) | Replace payloadPairs with a new dictionary | +| [setAppId(appId)](./browser-tracker.trackercore.setappid.md) | Set the application ID | +| [setBase64Encoding(encode)](./browser-tracker.trackercore.setbase64encoding.md) | Turn base 64 encoding on or off | +| [setColorDepth(depth)](./browser-tracker.trackercore.setcolordepth.md) | Set the color depth | +| [setIpAddress(ip)](./browser-tracker.trackercore.setipaddress.md) | Set the IP address | +| [setLang(lang)](./browser-tracker.trackercore.setlang.md) | Set the language | +| [setPlatform(value)](./browser-tracker.trackercore.setplatform.md) | Set the platform | +| [setScreenResolution(width, height)](./browser-tracker.trackercore.setscreenresolution.md) | Set the screen resolution | +| [setTimezone(timezone)](./browser-tracker.trackercore.settimezone.md) | Set the timezone | +| [setTrackerNamespace(name)](./browser-tracker.trackercore.settrackernamespace.md) | Set the tracker namespace | +| [setTrackerVersion(version)](./browser-tracker.trackercore.settrackerversion.md) | Set the tracker version | +| [setUseragent(useragent)](./browser-tracker.trackercore.setuseragent.md) | Set the Useragent | +| [setUserId(userId)](./browser-tracker.trackercore.setuserid.md) | Set the user ID | +| [setViewport(width, height)](./browser-tracker.trackercore.setviewport.md) | Set the viewport dimensions | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.removeglobalcontexts.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.removeglobalcontexts.md new file mode 100644 index 000000000..32d42ab60 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.removeglobalcontexts.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [removeGlobalContexts](./browser-tracker.trackercore.removeglobalcontexts.md) + +## TrackerCore.removeGlobalContexts() method + +Removes previously added global context, performs a deep comparison of the contexts or conditional contexts + +Signature: + +```typescript +removeGlobalContexts(contexts: Array): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| contexts | Array<ConditionalContextProvider \| ContextPrimitive \| string> | An array containing either contexts or a conditional contexts | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.resetpayloadpairs.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.resetpayloadpairs.md new file mode 100644 index 000000000..b4216e6b1 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.resetpayloadpairs.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [resetPayloadPairs](./browser-tracker.trackercore.resetpayloadpairs.md) + +## TrackerCore.resetPayloadPairs() method + +Replace payloadPairs with a new dictionary + +Signature: + +```typescript +resetPayloadPairs(dict: Payload): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| dict | Payload | Resets all current payload pairs with a new dictionary of pairs | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setappid.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setappid.md new file mode 100644 index 000000000..7e81fa3cc --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setappid.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setAppId](./browser-tracker.trackercore.setappid.md) + +## TrackerCore.setAppId() method + +Set the application ID + +Signature: + +```typescript +setAppId(appId: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| appId | string | An application ID which identifies the current application | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setbase64encoding.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setbase64encoding.md new file mode 100644 index 000000000..510c19bfe --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setbase64encoding.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setBase64Encoding](./browser-tracker.trackercore.setbase64encoding.md) + +## TrackerCore.setBase64Encoding() method + +Turn base 64 encoding on or off + +Signature: + +```typescript +setBase64Encoding(encode: boolean): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| encode | boolean | Whether to encode payload | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setcolordepth.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setcolordepth.md new file mode 100644 index 000000000..200fd458a --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setcolordepth.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setColorDepth](./browser-tracker.trackercore.setcolordepth.md) + +## TrackerCore.setColorDepth() method + +Set the color depth + +Signature: + +```typescript +setColorDepth(depth: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| depth | string | A color depth value as string | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setipaddress.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setipaddress.md new file mode 100644 index 000000000..67dcacf6a --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setipaddress.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setIpAddress](./browser-tracker.trackercore.setipaddress.md) + +## TrackerCore.setIpAddress() method + +Set the IP address + +Signature: + +```typescript +setIpAddress(ip: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ip | string | An IP Address string | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setlang.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setlang.md new file mode 100644 index 000000000..ee1c10f41 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setlang.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setLang](./browser-tracker.trackercore.setlang.md) + +## TrackerCore.setLang() method + +Set the language + +Signature: + +```typescript +setLang(lang: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| lang | string | A language string e.g. 'en-UK' | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setplatform.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setplatform.md new file mode 100644 index 000000000..375deb965 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setplatform.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setPlatform](./browser-tracker.trackercore.setplatform.md) + +## TrackerCore.setPlatform() method + +Set the platform + +Signature: + +```typescript +setPlatform(value: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| value | string | A valid Snowplow platform value | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setscreenresolution.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setscreenresolution.md new file mode 100644 index 000000000..4ab8cb85a --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setscreenresolution.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setScreenResolution](./browser-tracker.trackercore.setscreenresolution.md) + +## TrackerCore.setScreenResolution() method + +Set the screen resolution + +Signature: + +```typescript +setScreenResolution(width: string, height: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| width | string | screen resolution width | +| height | string | screen resolution height | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.settimezone.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.settimezone.md new file mode 100644 index 000000000..d67876ea7 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.settimezone.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setTimezone](./browser-tracker.trackercore.settimezone.md) + +## TrackerCore.setTimezone() method + +Set the timezone + +Signature: + +```typescript +setTimezone(timezone: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| timezone | string | A timezone string | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.settrackernamespace.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.settrackernamespace.md new file mode 100644 index 000000000..b216072cf --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.settrackernamespace.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setTrackerNamespace](./browser-tracker.trackercore.settrackernamespace.md) + +## TrackerCore.setTrackerNamespace() method + +Set the tracker namespace + +Signature: + +```typescript +setTrackerNamespace(name: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | The trackers namespace | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.settrackerversion.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.settrackerversion.md new file mode 100644 index 000000000..05f761ddd --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.settrackerversion.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setTrackerVersion](./browser-tracker.trackercore.settrackerversion.md) + +## TrackerCore.setTrackerVersion() method + +Set the tracker version + +Signature: + +```typescript +setTrackerVersion(version: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| version | string | The version of the current tracker | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setuseragent.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setuseragent.md new file mode 100644 index 000000000..267d54a30 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setuseragent.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setUseragent](./browser-tracker.trackercore.setuseragent.md) + +## TrackerCore.setUseragent() method + +Set the Useragent + +Signature: + +```typescript +setUseragent(useragent: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| useragent | string | A useragent string | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setuserid.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setuserid.md new file mode 100644 index 000000000..8bb275a1e --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setuserid.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setUserId](./browser-tracker.trackercore.setuserid.md) + +## TrackerCore.setUserId() method + +Set the user ID + +Signature: + +```typescript +setUserId(userId: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| userId | string | The custom user id | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setviewport.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setviewport.md new file mode 100644 index 000000000..1940c919f --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.setviewport.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [setViewport](./browser-tracker.trackercore.setviewport.md) + +## TrackerCore.setViewport() method + +Set the viewport dimensions + +Signature: + +```typescript +setViewport(width: string, height: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| width | string | viewport width | +| height | string | viewport height | + +Returns: + +void + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.track.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.track.md new file mode 100644 index 000000000..9ca4274d1 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackercore.track.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrackerCore](./browser-tracker.trackercore.md) > [track](./browser-tracker.trackercore.track.md) + +## TrackerCore.track property + +Call with a payload from a buildX function Adds context and payloadPairs name-value pairs to the payload Applies the callback to the built payload + +Signature: + +```typescript +track: (pb: PayloadBuilder, context?: Array | null, timestamp?: Timestamp | null) => Payload | undefined; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackselfdescribingevent.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackselfdescribingevent.md index 9cbd522bc..2a28d3206 100644 --- a/api-docs/docs/browser-tracker/markdown/browser-tracker.trackselfdescribingevent.md +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.trackselfdescribingevent.md @@ -9,14 +9,14 @@ Track a self-describing event happening on this page. A custom event type, allow Signature: ```typescript -declare function trackSelfDescribingEvent(event: SelfDescribingEvent & CommonEventProperties, trackers?: Array): void; +declare function trackSelfDescribingEvent>(event: SelfDescribingEvent & CommonEventProperties, trackers?: Array): void; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| event | SelfDescribingEvent & CommonEventProperties | The event information | +| event | SelfDescribingEvent<T> & CommonEventProperties | The event information | | trackers | Array<string> | The tracker identifiers which the event will be sent to | Returns: diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.truetimestamp.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.truetimestamp.md new file mode 100644 index 000000000..6bd5cf26b --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.truetimestamp.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrueTimestamp](./browser-tracker.truetimestamp.md) + +## TrueTimestamp interface + +A representation of a True Timestamp (ttm) + +Signature: + +```typescript +interface TrueTimestamp +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [type](./browser-tracker.truetimestamp.type.md) | "ttm" | | +| [value](./browser-tracker.truetimestamp.value.md) | number | | + diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.truetimestamp.type.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.truetimestamp.type.md new file mode 100644 index 000000000..18e0036e8 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.truetimestamp.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrueTimestamp](./browser-tracker.truetimestamp.md) > [type](./browser-tracker.truetimestamp.type.md) + +## TrueTimestamp.type property + +Signature: + +```typescript +readonly type: "ttm"; +``` diff --git a/api-docs/docs/browser-tracker/markdown/browser-tracker.truetimestamp.value.md b/api-docs/docs/browser-tracker/markdown/browser-tracker.truetimestamp.value.md new file mode 100644 index 000000000..80a5dca81 --- /dev/null +++ b/api-docs/docs/browser-tracker/markdown/browser-tracker.truetimestamp.value.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/browser-tracker](./browser-tracker.md) > [TrueTimestamp](./browser-tracker.truetimestamp.md) > [value](./browser-tracker.truetimestamp.value.md) + +## TrueTimestamp.value property + +Signature: + +```typescript +readonly value: number; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.buildselfdescribingevent.md b/api-docs/docs/node-tracker/markdown/node-tracker.buildselfdescribingevent.md index 111b618ae..4f293a24a 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.buildselfdescribingevent.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.buildselfdescribingevent.md @@ -9,14 +9,14 @@ Build a self-describing event A custom event type, allowing for an event to be t Signature: ```typescript -declare function buildSelfDescribingEvent(event: SelfDescribingEvent): PayloadBuilder; +declare function buildSelfDescribingEvent>(event: SelfDescribingEvent): PayloadBuilder; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| event | SelfDescribingEvent | Contains the properties and schema location for the event | +| event | SelfDescribingEvent<T> | Contains the properties and schema location for the event | Returns: diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.conditionalcontextprovider.md b/api-docs/docs/node-tracker/markdown/node-tracker.conditionalcontextprovider.md new file mode 100644 index 000000000..177638e68 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.conditionalcontextprovider.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [ConditionalContextProvider](./node-tracker.conditionalcontextprovider.md) + +## ConditionalContextProvider type + +Conditional context providers are two element arrays used to decide when to attach contexts, where: - the first element is some conditional criterion - the second element is any number of context primitives + +Signature: + +```typescript +type ConditionalContextProvider = FilterProvider | RuleSetProvider; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.contextprimitive.md b/api-docs/docs/node-tracker/markdown/node-tracker.contextprimitive.md new file mode 100644 index 000000000..c19d3fef4 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.contextprimitive.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [ContextPrimitive](./node-tracker.contextprimitive.md) + +## ContextPrimitive type + +A context primitive is either a self-describing JSON or a context generator + +Signature: + +```typescript +type ContextPrimitive = SelfDescribingJson | ContextGenerator; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.beforetrack.md b/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.beforetrack.md index da2f00118..fb031028f 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.beforetrack.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.beforetrack.md @@ -4,7 +4,7 @@ ## CorePlugin.beforeTrack property -Called just before the trackerCore callback fires +Called before the `filter` method is called and before the trackerCore callback fires (if the filter passes) Signature: diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.filter.md b/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.filter.md new file mode 100644 index 000000000..c5d39c6d2 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.filter.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [CorePlugin](./node-tracker.coreplugin.md) > [filter](./node-tracker.coreplugin.filter.md) + +## CorePlugin.filter property + +Called before the payload is sent to the callback to decide whether to send the payload or skip it + +Signature: + +```typescript +filter?: (payload: Payload) => boolean; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.md b/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.md index 2106026ce..37513de7d 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.coreplugin.md @@ -18,7 +18,8 @@ interface CorePlugin | --- | --- | --- | | [activateCorePlugin?](./node-tracker.coreplugin.activatecoreplugin.md) | (core: TrackerCore) => void | (Optional) Called when the plugin is initialised during the trackerCore construction | | [afterTrack?](./node-tracker.coreplugin.aftertrack.md) | (payload: Payload) => void | (Optional) Called just after the trackerCore callback fires | -| [beforeTrack?](./node-tracker.coreplugin.beforetrack.md) | (payloadBuilder: PayloadBuilder) => void | (Optional) Called just before the trackerCore callback fires | +| [beforeTrack?](./node-tracker.coreplugin.beforetrack.md) | (payloadBuilder: PayloadBuilder) => void | (Optional) Called before the filter method is called and before the trackerCore callback fires (if the filter passes) | | [contexts?](./node-tracker.coreplugin.contexts.md) | () => SelfDescribingJson\[\] | (Optional) Called when constructing the context for each event Useful for adding additional context to events | +| [filter?](./node-tracker.coreplugin.filter.md) | (payload: Payload) => boolean | (Optional) Called before the payload is sent to the callback to decide whether to send the payload or skip it | | [logger?](./node-tracker.coreplugin.logger.md) | (logger: Logger) => void | (Optional) Passed a logger instance which can be used to send log information to the active logger | diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.corepluginconfiguration.md b/api-docs/docs/node-tracker/markdown/node-tracker.corepluginconfiguration.md new file mode 100644 index 000000000..83aeb4c3a --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.corepluginconfiguration.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [CorePluginConfiguration](./node-tracker.corepluginconfiguration.md) + +## CorePluginConfiguration interface + +The configuration of the plugin to add + +Signature: + +```typescript +interface CorePluginConfiguration +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [plugin](./node-tracker.corepluginconfiguration.plugin.md) | CorePlugin | | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.corepluginconfiguration.plugin.md b/api-docs/docs/node-tracker/markdown/node-tracker.corepluginconfiguration.plugin.md new file mode 100644 index 000000000..9f063fd70 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.corepluginconfiguration.plugin.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [CorePluginConfiguration](./node-tracker.corepluginconfiguration.md) > [plugin](./node-tracker.corepluginconfiguration.plugin.md) + +## CorePluginConfiguration.plugin property + +Signature: + +```typescript +plugin: CorePlugin; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.customemitter.md b/api-docs/docs/node-tracker/markdown/node-tracker.customemitter.md new file mode 100644 index 000000000..116caedaa --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.customemitter.md @@ -0,0 +1,14 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [CustomEmitter](./node-tracker.customemitter.md) + +## CustomEmitter type + +Signature: + +```typescript +type CustomEmitter = { + /* Function returning custom Emitter or Emitter[] to be used. If set, other options are irrelevant */ + customEmitter: () => Emitter | Array; +}; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.devicetimestamp.md b/api-docs/docs/node-tracker/markdown/node-tracker.devicetimestamp.md new file mode 100644 index 000000000..46008fc3a --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.devicetimestamp.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [DeviceTimestamp](./node-tracker.devicetimestamp.md) + +## DeviceTimestamp interface + +A representation of a Device Timestamp (dtm) + +Signature: + +```typescript +interface DeviceTimestamp +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [type](./node-tracker.devicetimestamp.type.md) | "dtm" | | +| [value](./node-tracker.devicetimestamp.value.md) | number | | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.devicetimestamp.type.md b/api-docs/docs/node-tracker/markdown/node-tracker.devicetimestamp.type.md new file mode 100644 index 000000000..26936963e --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.devicetimestamp.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [DeviceTimestamp](./node-tracker.devicetimestamp.md) > [type](./node-tracker.devicetimestamp.type.md) + +## DeviceTimestamp.type property + +Signature: + +```typescript +readonly type: "dtm"; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.devicetimestamp.value.md b/api-docs/docs/node-tracker/markdown/node-tracker.devicetimestamp.value.md new file mode 100644 index 000000000..0f4d9a489 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.devicetimestamp.value.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [DeviceTimestamp](./node-tracker.devicetimestamp.md) > [value](./node-tracker.devicetimestamp.value.md) + +## DeviceTimestamp.value property + +Signature: + +```typescript +readonly value: number; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.flush.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.flush.md index e548ed1d5..a64176e70 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.flush.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.flush.md @@ -4,8 +4,10 @@ ## Emitter.flush property +Forces the emitter to send all events in the event store to the collector. + Signature: ```typescript -flush: () => void; +flush: () => Promise; ``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.input.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.input.md index f37d04b76..562cf2ccf 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.input.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.input.md @@ -4,8 +4,10 @@ ## Emitter.input property +Adds a payload to the event store or sends it to the collector. + Signature: ```typescript -input: (payload: Payload) => void; +input: (payload: Payload) => Promise; ``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.md index 78196241f..b09554040 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.md @@ -4,6 +4,8 @@ ## Emitter interface +Emitter is responsible for sending events to the collector. It manages the event queue and sends events in batches depending on configuration. + Signature: ```typescript @@ -14,7 +16,9 @@ interface Emitter | Property | Type | Description | | --- | --- | --- | -| [flush](./node-tracker.emitter.flush.md) | () => void | | -| [input](./node-tracker.emitter.input.md) | (payload: Payload) => void | | -| [setAnonymization?](./node-tracker.emitter.setanonymization.md) | (shouldAnonymize: boolean) => void | (Optional) Set if the requests from the emitter should be anonymized. Read more about anonymization used at https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/snowplow-tracker-protocol/going-deeper/http-headers/. | +| [flush](./node-tracker.emitter.flush.md) | () => Promise<void> | Forces the emitter to send all events in the event store to the collector. | +| [input](./node-tracker.emitter.input.md) | (payload: Payload) => Promise<void> | Adds a payload to the event store or sends it to the collector. | +| [setAnonymousTracking](./node-tracker.emitter.setanonymoustracking.md) | (anonymous: boolean) => void | Sets the server anonymization flag. | +| [setBufferSize](./node-tracker.emitter.setbuffersize.md) | (bufferSize: number) => void | Updates the buffer size of the emitter. | +| [setCollectorUrl](./node-tracker.emitter.setcollectorurl.md) | (url: string) => void | Updates the collector URL to which events will be sent. | diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setanonymization.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setanonymization.md deleted file mode 100644 index df5d44ceb..000000000 --- a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setanonymization.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Emitter](./node-tracker.emitter.md) > [setAnonymization](./node-tracker.emitter.setanonymization.md) - -## Emitter.setAnonymization property - -Set if the requests from the emitter should be anonymized. Read more about anonymization used at https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/snowplow-tracker-protocol/going-deeper/http-headers/. - -Signature: - -```typescript -setAnonymization?: (shouldAnonymize: boolean) => void; -``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setanonymoustracking.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setanonymoustracking.md new file mode 100644 index 000000000..2ef010f95 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setanonymoustracking.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Emitter](./node-tracker.emitter.md) > [setAnonymousTracking](./node-tracker.emitter.setanonymoustracking.md) + +## Emitter.setAnonymousTracking property + +Sets the server anonymization flag. + +Signature: + +```typescript +setAnonymousTracking: (anonymous: boolean) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setbuffersize.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setbuffersize.md new file mode 100644 index 000000000..ac5d916f2 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setbuffersize.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Emitter](./node-tracker.emitter.md) > [setBufferSize](./node-tracker.emitter.setbuffersize.md) + +## Emitter.setBufferSize property + +Updates the buffer size of the emitter. + +Signature: + +```typescript +setBufferSize: (bufferSize: number) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setcollectorurl.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setcollectorurl.md new file mode 100644 index 000000000..7af161683 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitter.setcollectorurl.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Emitter](./node-tracker.emitter.md) > [setCollectorUrl](./node-tracker.emitter.setcollectorurl.md) + +## Emitter.setCollectorUrl property + +Updates the collector URL to which events will be sent. + +Signature: + +```typescript +setCollectorUrl: (url: string) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.endpoint.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.endpoint.md new file mode 100644 index 000000000..c5569e568 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.endpoint.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfiguration](./node-tracker.emitterconfiguration.md) > [endpoint](./node-tracker.emitterconfiguration.endpoint.md) + +## EmitterConfiguration.endpoint property + +Signature: + +```typescript +endpoint: string; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.md new file mode 100644 index 000000000..6d060d88a --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfiguration](./node-tracker.emitterconfiguration.md) + +## EmitterConfiguration interface + +Signature: + +```typescript +interface EmitterConfiguration extends EmitterConfigurationBase +``` +Extends: EmitterConfigurationBase + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [endpoint](./node-tracker.emitterconfiguration.endpoint.md) | string | | +| [port?](./node-tracker.emitterconfiguration.port.md) | number | (Optional) | +| [protocol?](./node-tracker.emitterconfiguration.protocol.md) | "http" \| "https" | (Optional) | +| [serverAnonymization?](./node-tracker.emitterconfiguration.serveranonymization.md) | boolean | (Optional) | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.port.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.port.md new file mode 100644 index 000000000..009c1faf0 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.port.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfiguration](./node-tracker.emitterconfiguration.md) > [port](./node-tracker.emitterconfiguration.port.md) + +## EmitterConfiguration.port property + +Signature: + +```typescript +port?: number; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.protocol.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.protocol.md new file mode 100644 index 000000000..d7a692cf8 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.protocol.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfiguration](./node-tracker.emitterconfiguration.md) > [protocol](./node-tracker.emitterconfiguration.protocol.md) + +## EmitterConfiguration.protocol property + +Signature: + +```typescript +protocol?: "http" | "https"; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.serveranonymization.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.serveranonymization.md new file mode 100644 index 000000000..75d104443 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfiguration.serveranonymization.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfiguration](./node-tracker.emitterconfiguration.md) > [serverAnonymization](./node-tracker.emitterconfiguration.serveranonymization.md) + +## EmitterConfiguration.serverAnonymization property + +Signature: + +```typescript +serverAnonymization?: boolean; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.buffersize.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.buffersize.md new file mode 100644 index 000000000..9f7a2825a --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.buffersize.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [bufferSize](./node-tracker.emitterconfigurationbase.buffersize.md) + +## EmitterConfigurationBase.bufferSize property + +The amount of events that should be buffered before sending Recommended to leave as 1 to reduce change of losing events + +Signature: + +```typescript +bufferSize?: number; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.connectiontimeout.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.connectiontimeout.md new file mode 100644 index 000000000..419b774cb --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.connectiontimeout.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [connectionTimeout](./node-tracker.emitterconfigurationbase.connectiontimeout.md) + +## EmitterConfigurationBase.connectionTimeout property + +How long to wait before aborting requests to the collector + +Signature: + +```typescript +connectionTimeout?: number; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.credentials.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.credentials.md new file mode 100644 index 000000000..db5332ae2 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.credentials.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [credentials](./node-tracker.emitterconfigurationbase.credentials.md) + +## EmitterConfigurationBase.credentials property + +Controls whether or not the browser sends credentials (defaults to 'include') + +Signature: + +```typescript +credentials?: "omit" | "same-origin" | "include"; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.customfetch.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.customfetch.md new file mode 100644 index 000000000..64052f0b1 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.customfetch.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [customFetch](./node-tracker.emitterconfigurationbase.customfetch.md) + +## EmitterConfigurationBase.customFetch property + +Enables overriding the default fetch function with a custom implementation. + +Signature: + +```typescript +customFetch?: (input: Request, options?: RequestInit) => Promise; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.customheaders.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.customheaders.md new file mode 100644 index 000000000..ffb0bdf4f --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.customheaders.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [customHeaders](./node-tracker.emitterconfigurationbase.customheaders.md) + +## EmitterConfigurationBase.customHeaders property + +An object of key value pairs which represent headers to attach when sending a POST request, only works for POST + +Signature: + +```typescript +customHeaders?: Record; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.dontretrystatuscodes.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.dontretrystatuscodes.md new file mode 100644 index 000000000..5405fbcc2 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.dontretrystatuscodes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [dontRetryStatusCodes](./node-tracker.emitterconfigurationbase.dontretrystatuscodes.md) + +## EmitterConfigurationBase.dontRetryStatusCodes property + +List of HTTP response status codes for which events sent to Collector should not be retried in future request. Only non-success status codes are considered (greater or equal to 300). The don't retry codes are only considered for GET and POST requests. By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422 (these don't retry codes will remain even if you set your own `dontRetryStatusCodes` but can be changed using the `retryStatusCodes`). + +Signature: + +```typescript +dontRetryStatusCodes?: number[]; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.eventmethod.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.eventmethod.md new file mode 100644 index 000000000..d3164e3ea --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.eventmethod.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [eventMethod](./node-tracker.emitterconfigurationbase.eventmethod.md) + +## EmitterConfigurationBase.eventMethod property + +The preferred technique to use to send events + +Signature: + +```typescript +eventMethod?: EventMethod; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.eventstore.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.eventstore.md new file mode 100644 index 000000000..7941c33d4 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.eventstore.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [eventStore](./node-tracker.emitterconfigurationbase.eventstore.md) + +## EmitterConfigurationBase.eventStore property + +Enables providing a custom EventStore implementation to store events before sending them to the collector. + +Signature: + +```typescript +eventStore?: EventStore; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.idservice.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.idservice.md new file mode 100644 index 000000000..9b1bfb006 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.idservice.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [idService](./node-tracker.emitterconfigurationbase.idservice.md) + +## EmitterConfigurationBase.idService property + +Id service full URL. This URL will be added to the queue and will be called using a GET method. This option is there to allow the service URL to be called in order to set any required identifiers e.g. extra cookies. + +The request respects the `anonymousTracking` option, including the SP-Anonymous header if needed, and any additional custom headers from the customHeaders option. + +Signature: + +```typescript +idService?: string; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.keepalive.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.keepalive.md new file mode 100644 index 000000000..17f31e773 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.keepalive.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [keepalive](./node-tracker.emitterconfigurationbase.keepalive.md) + +## EmitterConfigurationBase.keepalive property + +Indicates that the request should be allowed to outlive the webpage that initiated it. Enables collector requests to complete even if the page is closed or navigated away from. + +Signature: + +```typescript +keepalive?: boolean; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.maxgetbytes.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.maxgetbytes.md new file mode 100644 index 000000000..270445198 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.maxgetbytes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [maxGetBytes](./node-tracker.emitterconfigurationbase.maxgetbytes.md) + +## EmitterConfigurationBase.maxGetBytes property + +The max size a GET request (its complete URL) can be. Requests over this size will be tried as a POST request. + +Signature: + +```typescript +maxGetBytes?: number; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.maxpostbytes.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.maxpostbytes.md new file mode 100644 index 000000000..92ceb5c79 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.maxpostbytes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [maxPostBytes](./node-tracker.emitterconfigurationbase.maxpostbytes.md) + +## EmitterConfigurationBase.maxPostBytes property + +The max size a POST request can be before the tracker will force send it Also dictates the max size of a POST request before a batch of events is split into multiple requests + +Signature: + +```typescript +maxPostBytes?: number; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.md new file mode 100644 index 000000000..60fd49b0f --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.md @@ -0,0 +1,35 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) + +## EmitterConfigurationBase interface + +Signature: + +```typescript +interface EmitterConfigurationBase +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [bufferSize?](./node-tracker.emitterconfigurationbase.buffersize.md) | number | (Optional) The amount of events that should be buffered before sending Recommended to leave as 1 to reduce change of losing events | +| [connectionTimeout?](./node-tracker.emitterconfigurationbase.connectiontimeout.md) | number | (Optional) How long to wait before aborting requests to the collector | +| [credentials?](./node-tracker.emitterconfigurationbase.credentials.md) | "omit" \| "same-origin" \| "include" | (Optional) Controls whether or not the browser sends credentials (defaults to 'include') | +| [customFetch?](./node-tracker.emitterconfigurationbase.customfetch.md) | (input: Request, options?: RequestInit) => Promise<Response> | (Optional) Enables overriding the default fetch function with a custom implementation. | +| [customHeaders?](./node-tracker.emitterconfigurationbase.customheaders.md) | Record<string, string> | (Optional) An object of key value pairs which represent headers to attach when sending a POST request, only works for POST | +| [dontRetryStatusCodes?](./node-tracker.emitterconfigurationbase.dontretrystatuscodes.md) | number\[\] | (Optional) List of HTTP response status codes for which events sent to Collector should not be retried in future request. Only non-success status codes are considered (greater or equal to 300). The don't retry codes are only considered for GET and POST requests. By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422 (these don't retry codes will remain even if you set your own dontRetryStatusCodes but can be changed using the retryStatusCodes). | +| [eventMethod?](./node-tracker.emitterconfigurationbase.eventmethod.md) | EventMethod | (Optional) The preferred technique to use to send events | +| [eventStore?](./node-tracker.emitterconfigurationbase.eventstore.md) | EventStore | (Optional) Enables providing a custom EventStore implementation to store events before sending them to the collector. | +| [idService?](./node-tracker.emitterconfigurationbase.idservice.md) | string | (Optional) Id service full URL. This URL will be added to the queue and will be called using a GET method. This option is there to allow the service URL to be called in order to set any required identifiers e.g. extra cookies.The request respects the anonymousTracking option, including the SP-Anonymous header if needed, and any additional custom headers from the customHeaders option. | +| [keepalive?](./node-tracker.emitterconfigurationbase.keepalive.md) | boolean | (Optional) Indicates that the request should be allowed to outlive the webpage that initiated it. Enables collector requests to complete even if the page is closed or navigated away from. | +| [maxGetBytes?](./node-tracker.emitterconfigurationbase.maxgetbytes.md) | number | (Optional) The max size a GET request (its complete URL) can be. Requests over this size will be tried as a POST request. | +| [maxPostBytes?](./node-tracker.emitterconfigurationbase.maxpostbytes.md) | number | (Optional) The max size a POST request can be before the tracker will force send it Also dictates the max size of a POST request before a batch of events is split into multiple requests | +| [onRequestFailure?](./node-tracker.emitterconfigurationbase.onrequestfailure.md) | (data: RequestFailure, response?: Response) => void | (Optional) A callback function to be executed whenever a request fails to be sent to the collector. This is the inverse of the onRequestSuccess callback, so any non 2xx status code will trigger this callback. | +| [onRequestSuccess?](./node-tracker.emitterconfigurationbase.onrequestsuccess.md) | (data: EventBatch, response: Response) => void | (Optional) A callback function to be executed whenever a request is successfully sent to the collector. In practice this means any request which returns a 2xx status code will trigger this callback. | +| [postPath?](./node-tracker.emitterconfigurationbase.postpath.md) | string | (Optional) The post path which events will be sent to. Ensure your collector is configured to accept events on this post path | +| [retryFailedRequests?](./node-tracker.emitterconfigurationbase.retryfailedrequests.md) | boolean | (Optional) Whether to retry failed requests to the collector.Failed requests are requests that failed due to \[timeouts\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout\_event), \[network errors\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/error\_event), and \[abort events\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort\_event).Takes precedent over retryStatusCodes and dontRetryStatusCodes. | +| [retryStatusCodes?](./node-tracker.emitterconfigurationbase.retrystatuscodes.md) | number\[\] | (Optional) List of HTTP response status codes for which events sent to Collector should be retried in future requests. Only non-success status codes are considered (greater or equal to 300). The retry codes are only considered for GET and POST requests. They take priority over the dontRetryStatusCodes option. By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422. | +| [useStm?](./node-tracker.emitterconfigurationbase.usestm.md) | boolean | (Optional) Should the Sent Timestamp be attached to events. Only applies for GET events. | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.onrequestfailure.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.onrequestfailure.md new file mode 100644 index 000000000..d95e112f8 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.onrequestfailure.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [onRequestFailure](./node-tracker.emitterconfigurationbase.onrequestfailure.md) + +## EmitterConfigurationBase.onRequestFailure property + +A callback function to be executed whenever a request fails to be sent to the collector. This is the inverse of the onRequestSuccess callback, so any non 2xx status code will trigger this callback. + +Signature: + +```typescript +onRequestFailure?: (data: RequestFailure, response?: Response) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.onrequestsuccess.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.onrequestsuccess.md new file mode 100644 index 000000000..e478564e7 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.onrequestsuccess.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [onRequestSuccess](./node-tracker.emitterconfigurationbase.onrequestsuccess.md) + +## EmitterConfigurationBase.onRequestSuccess property + +A callback function to be executed whenever a request is successfully sent to the collector. In practice this means any request which returns a 2xx status code will trigger this callback. + +Signature: + +```typescript +onRequestSuccess?: (data: EventBatch, response: Response) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.postpath.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.postpath.md new file mode 100644 index 000000000..9ab2d0d81 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.postpath.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [postPath](./node-tracker.emitterconfigurationbase.postpath.md) + +## EmitterConfigurationBase.postPath property + +The post path which events will be sent to. Ensure your collector is configured to accept events on this post path + +Signature: + +```typescript +postPath?: string; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.retryfailedrequests.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.retryfailedrequests.md new file mode 100644 index 000000000..e686112cf --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.retryfailedrequests.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [retryFailedRequests](./node-tracker.emitterconfigurationbase.retryfailedrequests.md) + +## EmitterConfigurationBase.retryFailedRequests property + +Whether to retry failed requests to the collector. + +Failed requests are requests that failed due to \[timeouts\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout\_event), \[network errors\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/error\_event), and \[abort events\](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort\_event). + +Takes precedent over `retryStatusCodes` and `dontRetryStatusCodes`. + +Signature: + +```typescript +retryFailedRequests?: boolean; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.retrystatuscodes.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.retrystatuscodes.md new file mode 100644 index 000000000..44c4ae4eb --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.retrystatuscodes.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [retryStatusCodes](./node-tracker.emitterconfigurationbase.retrystatuscodes.md) + +## EmitterConfigurationBase.retryStatusCodes property + +List of HTTP response status codes for which events sent to Collector should be retried in future requests. Only non-success status codes are considered (greater or equal to 300). The retry codes are only considered for GET and POST requests. They take priority over the `dontRetryStatusCodes` option. By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422. + +Signature: + +```typescript +retryStatusCodes?: number[]; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.usestm.md b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.usestm.md new file mode 100644 index 000000000..a8dc3576a --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.emitterconfigurationbase.usestm.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) > [useStm](./node-tracker.emitterconfigurationbase.usestm.md) + +## EmitterConfigurationBase.useStm property + +Should the Sent Timestamp be attached to events. Only applies for GET events. + +Signature: + +```typescript +useStm?: boolean; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventbatch.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventbatch.md new file mode 100644 index 000000000..a2978ef71 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventbatch.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventBatch](./node-tracker.eventbatch.md) + +## EventBatch type + +A collection of event payloads which are sent to the collector. + +Signature: + +```typescript +type EventBatch = Payload[]; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventjson.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventjson.md new file mode 100644 index 000000000..a5cdc46a2 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventjson.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventJson](./node-tracker.eventjson.md) + +## EventJson type + +An array of tuples which represents the unprocessed JSON to be added to the Payload + +Signature: + +```typescript +type EventJson = Array; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventjsonwithkeys.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventjsonwithkeys.md new file mode 100644 index 000000000..6a1f2b0c9 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventjsonwithkeys.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventJsonWithKeys](./node-tracker.eventjsonwithkeys.md) + +## EventJsonWithKeys type + +A tuple which represents the unprocessed JSON to be added to the Payload + +Signature: + +```typescript +type EventJsonWithKeys = { + keyIfEncoded: string; + keyIfNotEncoded: string; + json: Record; +}; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventmethod.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventmethod.md new file mode 100644 index 000000000..a00d7545d --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventmethod.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventMethod](./node-tracker.eventmethod.md) + +## EventMethod type + +Signature: + +```typescript +type EventMethod = "post" | "get"; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.add.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.add.md new file mode 100644 index 000000000..55901dbac --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.add.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStore](./node-tracker.eventstore.md) > [add](./node-tracker.eventstore.add.md) + +## EventStore.add property + +Add an event to the store + +Signature: + +```typescript +add: (payload: EventStorePayload) => Promise; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.count.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.count.md new file mode 100644 index 000000000..473b9c306 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.count.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStore](./node-tracker.eventstore.md) > [count](./node-tracker.eventstore.count.md) + +## EventStore.count property + +Count all events in the store + +Signature: + +```typescript +count: () => Promise; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.getall.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.getall.md new file mode 100644 index 000000000..07414e884 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.getall.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStore](./node-tracker.eventstore.md) > [getAll](./node-tracker.eventstore.getall.md) + +## EventStore.getAll property + +Retrieve all payloads including their meta configuration in the store + +Signature: + +```typescript +getAll: () => Promise; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.getallpayloads.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.getallpayloads.md new file mode 100644 index 000000000..35cb245db --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.getallpayloads.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStore](./node-tracker.eventstore.md) > [getAllPayloads](./node-tracker.eventstore.getallpayloads.md) + +## EventStore.getAllPayloads property + +Retrieve all pure payloads in the store + +Signature: + +```typescript +getAllPayloads: () => Promise; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.iterator.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.iterator.md new file mode 100644 index 000000000..1310c5fdc --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.iterator.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStore](./node-tracker.eventstore.md) > [iterator](./node-tracker.eventstore.iterator.md) + +## EventStore.iterator property + +Get an iterator over all events in the store + +Signature: + +```typescript +iterator: () => EventStoreIterator; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.md new file mode 100644 index 000000000..10ccdead0 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStore](./node-tracker.eventstore.md) + +## EventStore interface + +EventStore allows storing and retrieving events before they are sent to the collector + +Signature: + +```typescript +interface EventStore +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [add](./node-tracker.eventstore.add.md) | (payload: EventStorePayload) => Promise<number> | Add an event to the store | +| [count](./node-tracker.eventstore.count.md) | () => Promise<number> | Count all events in the store | +| [getAll](./node-tracker.eventstore.getall.md) | () => Promise<readonly EventStorePayload\[\]> | Retrieve all payloads including their meta configuration in the store | +| [getAllPayloads](./node-tracker.eventstore.getallpayloads.md) | () => Promise<readonly Payload\[\]> | Retrieve all pure payloads in the store | +| [iterator](./node-tracker.eventstore.iterator.md) | () => EventStoreIterator | Get an iterator over all events in the store | +| [removeHead](./node-tracker.eventstore.removehead.md) | (count: number) => Promise<void> | Remove the first count events from the store | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.removehead.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.removehead.md new file mode 100644 index 000000000..e8413bcad --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstore.removehead.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStore](./node-tracker.eventstore.md) > [removeHead](./node-tracker.eventstore.removehead.md) + +## EventStore.removeHead property + +Remove the first `count` events from the store + +Signature: + +```typescript +removeHead: (count: number) => Promise; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreconfiguration.maxsize.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreconfiguration.maxsize.md new file mode 100644 index 000000000..7a86458d1 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreconfiguration.maxsize.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStoreConfiguration](./node-tracker.eventstoreconfiguration.md) > [maxSize](./node-tracker.eventstoreconfiguration.maxsize.md) + +## EventStoreConfiguration.maxSize property + +The maximum amount of events that will be buffered in the event store + +This is useful to ensure the Tracker doesn't fill the 5MB or 10MB available to each website should the collector be unavailable due to lost connectivity. Will drop old events once the limit is hit + +Signature: + +```typescript +maxSize?: number; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreconfiguration.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreconfiguration.md new file mode 100644 index 000000000..da2757c7a --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreconfiguration.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStoreConfiguration](./node-tracker.eventstoreconfiguration.md) + +## EventStoreConfiguration interface + +Signature: + +```typescript +interface EventStoreConfiguration +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [maxSize?](./node-tracker.eventstoreconfiguration.maxsize.md) | number | (Optional) The maximum amount of events that will be buffered in the event storeThis is useful to ensure the Tracker doesn't fill the 5MB or 10MB available to each website should the collector be unavailable due to lost connectivity. Will drop old events once the limit is hit | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreiterator.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreiterator.md new file mode 100644 index 000000000..dfc1ef00f --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreiterator.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStoreIterator](./node-tracker.eventstoreiterator.md) + +## EventStoreIterator interface + +Signature: + +```typescript +interface EventStoreIterator +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [next](./node-tracker.eventstoreiterator.next.md) | () => Promise<{ value: EventStorePayload \| undefined; done: boolean; }> | Retrieve the next event in the store | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreiterator.next.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreiterator.next.md new file mode 100644 index 000000000..08c2c9490 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstoreiterator.next.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStoreIterator](./node-tracker.eventstoreiterator.md) > [next](./node-tracker.eventstoreiterator.next.md) + +## EventStoreIterator.next property + +Retrieve the next event in the store + +Signature: + +```typescript +next: () => Promise<{ + value: EventStorePayload | undefined; + done: boolean; + }>; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstorepayload.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstorepayload.md new file mode 100644 index 000000000..adb36353d --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstorepayload.md @@ -0,0 +1,19 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStorePayload](./node-tracker.eventstorepayload.md) + +## EventStorePayload interface + +Signature: + +```typescript +interface EventStorePayload +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [payload](./node-tracker.eventstorepayload.payload.md) | Payload | The event payload to be stored | +| [svrAnon?](./node-tracker.eventstorepayload.svranon.md) | boolean | (Optional) If the request should undergo server anonymization. | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstorepayload.payload.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstorepayload.payload.md new file mode 100644 index 000000000..f1bf74946 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstorepayload.payload.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStorePayload](./node-tracker.eventstorepayload.md) > [payload](./node-tracker.eventstorepayload.payload.md) + +## EventStorePayload.payload property + +The event payload to be stored + +Signature: + +```typescript +payload: Payload; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.eventstorepayload.svranon.md b/api-docs/docs/node-tracker/markdown/node-tracker.eventstorepayload.svranon.md new file mode 100644 index 000000000..f562923cc --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.eventstorepayload.svranon.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [EventStorePayload](./node-tracker.eventstorepayload.md) > [svrAnon](./node-tracker.eventstorepayload.svranon.md) + +## EventStorePayload.svrAnon property + +If the request should undergo server anonymization. + +Signature: + +```typescript +svrAnon?: boolean; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.filterprovider.md b/api-docs/docs/node-tracker/markdown/node-tracker.filterprovider.md new file mode 100644 index 000000000..3700dcfb8 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.filterprovider.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [FilterProvider](./node-tracker.filterprovider.md) + +## FilterProvider type + +A filter provider is a tuple that has two parts: a context filter and the context primitive(s) If the context filter evaluates to true, the tracker will attach the context primitive(s) + +Signature: + +```typescript +type FilterProvider = [ + ContextFilter, + Array | ContextPrimitive +]; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.formelement.md b/api-docs/docs/node-tracker/markdown/node-tracker.formelement.md new file mode 100644 index 000000000..e7782bb64 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.formelement.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [FormElement](./node-tracker.formelement.md) + +## FormElement type + +A representation of an element within a form + +Signature: + +```typescript +type FormElement = { + name: string; + value: string | null; + nodeName: string; + type?: string | null; +}; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.gotemitter.md b/api-docs/docs/node-tracker/markdown/node-tracker.gotemitter.md deleted file mode 100644 index 106a6040d..000000000 --- a/api-docs/docs/node-tracker/markdown/node-tracker.gotemitter.md +++ /dev/null @@ -1,33 +0,0 @@ - - -[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [gotEmitter](./node-tracker.gotemitter.md) - -## gotEmitter() function - -Create an emitter object, which uses the `got` library, that will send events to a collector - -Signature: - -```typescript -declare function gotEmitter(endpoint: string, protocol?: HttpProtocol, port?: number, method?: HttpMethod, bufferSize?: number, retry?: number | Partial, cookieJar?: PromiseCookieJar | ToughCookieJar, callback?: (error?: RequestError, response?: Response) => void, agents?: Agents, serverAnonymization?: boolean): Emitter; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| endpoint | string | The collector to which events will be sent | -| protocol | HttpProtocol | http or https | -| port | number | The port for requests to use | -| method | HttpMethod | get or post | -| bufferSize | number | Number of events which can be queued before flush is called | -| retry | number \| Partial<RequiredRetryOptions> | Configure the retry policy for got - https://github.com/sindresorhus/got/blob/v11.5.2/readme.md\#retry | -| cookieJar | PromiseCookieJar \| ToughCookieJar | Add a cookieJar to got - https://github.com/sindresorhus/got/blob/v11.5.2/readme.md\#cookiejar | -| callback | (error?: RequestError, response?: Response<string>) => void | Callback called after a got request following retries - called with ErrorRequest (https://github.com/sindresorhus/got/blob/v11.5.2/readme.md\#errors) and Response (https://github.com/sindresorhus/got/blob/v11.5.2/readme.md\#response) | -| agents | Agents | Set new http.Agent and https.Agent objects on got requests - https://github.com/sindresorhus/got/blob/v11.5.2/readme.md\#agent | -| serverAnonymization | boolean | If the request should undergo server anonymization. | - -Returns: - -Emitter - diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.httpmethod.md b/api-docs/docs/node-tracker/markdown/node-tracker.httpmethod.md deleted file mode 100644 index bb8a91f6b..000000000 --- a/api-docs/docs/node-tracker/markdown/node-tracker.httpmethod.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [HttpMethod](./node-tracker.httpmethod.md) - -## HttpMethod enum - -Signature: - -```typescript -declare enum HttpMethod -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| GET | "get" | | -| POST | "post" | | - diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.jsonprocessor.md b/api-docs/docs/node-tracker/markdown/node-tracker.jsonprocessor.md new file mode 100644 index 000000000..ab2ec3160 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.jsonprocessor.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [JsonProcessor](./node-tracker.jsonprocessor.md) + +## JsonProcessor type + +A function which will processor the Json onto the injected PayloadBuilder + +Signature: + +```typescript +type JsonProcessor = (payloadBuilder: PayloadBuilder, jsonForProcessing: EventJson, contextEntitiesForProcessing: SelfDescribingJson[]) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.httpprotocol.md b/api-docs/docs/node-tracker/markdown/node-tracker.log_level.md similarity index 51% rename from api-docs/docs/node-tracker/markdown/node-tracker.httpprotocol.md rename to api-docs/docs/node-tracker/markdown/node-tracker.log_level.md index c03c42c99..95c2e3a94 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.httpprotocol.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.log_level.md @@ -1,19 +1,22 @@ - - -[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [HttpProtocol](./node-tracker.httpprotocol.md) - -## HttpProtocol enum - -Signature: - -```typescript -declare enum HttpProtocol -``` - -## Enumeration Members - -| Member | Value | Description | -| --- | --- | --- | -| HTTP | "http" | | -| HTTPS | "https" | | - + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [LOG\_LEVEL](./node-tracker.log_level.md) + +## LOG\_LEVEL enum + +Signature: + +```typescript +declare enum LOG_LEVEL +``` + +## Enumeration Members + +| Member | Value | Description | +| --- | --- | --- | +| debug | 3 | | +| error | 1 | | +| info | 4 | | +| none | 0 | | +| warn | 2 | | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.logger.debug.md b/api-docs/docs/node-tracker/markdown/node-tracker.logger.debug.md new file mode 100644 index 000000000..1d354b29b --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.logger.debug.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Logger](./node-tracker.logger.md) > [debug](./node-tracker.logger.debug.md) + +## Logger.debug property + +Signature: + +```typescript +debug: (message: string, ...extraParams: unknown[]) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.logger.error.md b/api-docs/docs/node-tracker/markdown/node-tracker.logger.error.md new file mode 100644 index 000000000..36e14d97d --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.logger.error.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Logger](./node-tracker.logger.md) > [error](./node-tracker.logger.error.md) + +## Logger.error property + +Signature: + +```typescript +error: (message: string, error?: unknown, ...extraParams: unknown[]) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.logger.info.md b/api-docs/docs/node-tracker/markdown/node-tracker.logger.info.md new file mode 100644 index 000000000..8e2b7cd2b --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.logger.info.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Logger](./node-tracker.logger.md) > [info](./node-tracker.logger.info.md) + +## Logger.info property + +Signature: + +```typescript +info: (message: string, ...extraParams: unknown[]) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.logger.md b/api-docs/docs/node-tracker/markdown/node-tracker.logger.md new file mode 100644 index 000000000..494b0d0aa --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.logger.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Logger](./node-tracker.logger.md) + +## Logger interface + +Signature: + +```typescript +interface Logger +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [debug](./node-tracker.logger.debug.md) | (message: string, ...extraParams: unknown\[\]) => void | | +| [error](./node-tracker.logger.error.md) | (message: string, error?: unknown, ...extraParams: unknown\[\]) => void | | +| [info](./node-tracker.logger.info.md) | (message: string, ...extraParams: unknown\[\]) => void | | +| [setLogLevel](./node-tracker.logger.setloglevel.md) | (level: LOG\_LEVEL) => void | | +| [warn](./node-tracker.logger.warn.md) | (message: string, error?: unknown, ...extraParams: unknown\[\]) => void | | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.logger.setloglevel.md b/api-docs/docs/node-tracker/markdown/node-tracker.logger.setloglevel.md new file mode 100644 index 000000000..9c6591674 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.logger.setloglevel.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Logger](./node-tracker.logger.md) > [setLogLevel](./node-tracker.logger.setloglevel.md) + +## Logger.setLogLevel property + +Signature: + +```typescript +setLogLevel: (level: LOG_LEVEL) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.logger.warn.md b/api-docs/docs/node-tracker/markdown/node-tracker.logger.warn.md new file mode 100644 index 000000000..781268e8e --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.logger.warn.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Logger](./node-tracker.logger.md) > [warn](./node-tracker.logger.warn.md) + +## Logger.warn property + +Signature: + +```typescript +warn: (message: string, error?: unknown, ...extraParams: unknown[]) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.md b/api-docs/docs/node-tracker/markdown/node-tracker.md index 774b85a63..917346019 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.md @@ -8,8 +8,7 @@ | Enumeration | Description | | --- | --- | -| [HttpMethod](./node-tracker.httpmethod.md) | | -| [HttpProtocol](./node-tracker.httpprotocol.md) | | +| [LOG\_LEVEL](./node-tracker.log_level.md) | | ## Functions @@ -34,8 +33,7 @@ | [buildSiteSearch(event)](./node-tracker.buildsitesearch.md) | Build a Site Search Event Used when a user performs a search action on a page | | [buildSocialInteraction(event)](./node-tracker.buildsocialinteraction.md) | Build a Social Interaction Event Social tracking will be used to track the way users interact with Facebook, Twitter and Google + widgets e.g. to capture “like this” or “tweet this” events. | | [buildStructEvent(event)](./node-tracker.buildstructevent.md) | Build a Structured Event A classic style of event tracking, allows for easier movement between analytics systems. A loosely typed event, creating a Self Describing event is preferred, but useful for interoperability. | -| [gotEmitter(endpoint, protocol, port, method, bufferSize, retry, cookieJar, callback, agents, serverAnonymization)](./node-tracker.gotemitter.md) | Create an emitter object, which uses the got library, that will send events to a collector | -| [tracker(emitters, namespace, appId, encodeBase64)](./node-tracker.tracker.md) | Snowplow Node.js Tracker | +| [newTracker(trackerConfiguration, emitterConfiguration)](./node-tracker.newtracker.md) | | ## Interfaces @@ -50,23 +48,36 @@ | [ContextEvent](./node-tracker.contextevent.md) | Argument for [ContextGenerator](./node-tracker.contextgenerator.md) and [ContextFilter](./node-tracker.contextfilter.md) callback | | [CoreConfiguration](./node-tracker.coreconfiguration.md) | The configuration object for the tracker core library | | [CorePlugin](./node-tracker.coreplugin.md) | Interface which defines Core Plugins | +| [CorePluginConfiguration](./node-tracker.corepluginconfiguration.md) | The configuration of the plugin to add | +| [DeviceTimestamp](./node-tracker.devicetimestamp.md) | A representation of a Device Timestamp (dtm) | | [EcommerceTransactionEvent](./node-tracker.ecommercetransactionevent.md) | An Ecommerce Transaction Event Allows for tracking common ecommerce events, this event is usually used when a customer completes a transaction. | | [EcommerceTransactionItemEvent](./node-tracker.ecommercetransactionitemevent.md) | An Ecommerce Transaction Item Related to the [EcommerceTransactionEvent](./node-tracker.ecommercetransactionevent.md) Each Ecommerce Transaction may contain one or more EcommerceTransactionItem events | -| [Emitter](./node-tracker.emitter.md) | | +| [Emitter](./node-tracker.emitter.md) | Emitter is responsible for sending events to the collector. It manages the event queue and sends events in batches depending on configuration. | +| [EmitterConfiguration](./node-tracker.emitterconfiguration.md) | | +| [EmitterConfigurationBase](./node-tracker.emitterconfigurationbase.md) | | | [EventPayloadAndContext](./node-tracker.eventpayloadandcontext.md) | Interface for returning a built event (PayloadBuilder) and context (Array of SelfDescribingJson). | +| [EventStore](./node-tracker.eventstore.md) | EventStore allows storing and retrieving events before they are sent to the collector | +| [EventStoreConfiguration](./node-tracker.eventstoreconfiguration.md) | | +| [EventStoreIterator](./node-tracker.eventstoreiterator.md) | | +| [EventStorePayload](./node-tracker.eventstorepayload.md) | | | [FormFocusOrChangeEvent](./node-tracker.formfocusorchangeevent.md) | Represents either a Form Focus or Form Change event When a user focuses on a form element or when a user makes a change to a form element. | | [FormSubmissionEvent](./node-tracker.formsubmissionevent.md) | A Form Submission Event Used to track when a user submits a form | | [LinkClickEvent](./node-tracker.linkclickevent.md) | A Link Click Event Used when a user clicks on a link on a webpage, typically an anchor tag <a> | +| [Logger](./node-tracker.logger.md) | | | [PagePingEvent](./node-tracker.pagepingevent.md) | A Page Ping Event Fires when activity tracking is enabled in the browser. Tracks same information as the last tracked Page View and includes scroll information from the current page view | | [PageViewEvent](./node-tracker.pageviewevent.md) | A Page View Event Represents a Page View, which is typically fired as soon as possible when a web page is loaded within the users browser. Often also fired on "virtual page views" within Single Page Applications (SPA). | | [PayloadBuilder](./node-tracker.payloadbuilder.md) | Interface for mutable object encapsulating tracker payload | | [RemoveFromCartEvent](./node-tracker.removefromcartevent.md) | An Remove To Cart Event For tracking users removing items from a cart on an ecommerce site. | +| [RuleSet](./node-tracker.ruleset.md) | A ruleset has accept or reject properties that contain rules for matching Iglu schema URIs | | [ScreenViewEvent](./node-tracker.screenviewevent.md) | A Screen View Event Similar to a Page View but less focused on typical web properties Often used for mobile applications as the user is presented with new views as they performance navigation events | | [SelfDescribingEvent](./node-tracker.selfdescribingevent.md) | A Self Describing Event A custom event type, allowing for an event to be tracked using your own custom schema and a data object which conforms to the supplied schema | | [SiteSearchEvent](./node-tracker.sitesearchevent.md) | A Site Search Event Used when a user performs a search action on a page | | [SocialInteractionEvent](./node-tracker.socialinteractionevent.md) | A Social Interaction Event Social tracking will be used to track the way users interact with Facebook, Twitter and Google + widgets e.g. to capture “like this” or “tweet this” events. | | [StructuredEvent](./node-tracker.structuredevent.md) | A Structured Event A classic style of event tracking, allows for easier movement between analytics systems. A loosely typed event, creating a Self Describing event is preferred, but useful for interoperability. | | [Tracker](./node-tracker.tracker.md) | | +| [TrackerConfiguration](./node-tracker.trackerconfiguration.md) | | +| [TrackerCore](./node-tracker.trackercore.md) | Export interface containing all Core functions | +| [TrueTimestamp](./node-tracker.truetimestamp.md) | A representation of a True Timestamp (ttm) | ## Variables @@ -78,9 +89,22 @@ | Type Alias | Description | | --- | --- | +| [ConditionalContextProvider](./node-tracker.conditionalcontextprovider.md) | Conditional context providers are two element arrays used to decide when to attach contexts, where: - the first element is some conditional criterion - the second element is any number of context primitives | | [ContextFilter](./node-tracker.contextfilter.md) | A context filter is a user-supplied callback that is evaluated for each event to determine if the context associated with the filter should be attached to the event | | [ContextGenerator](./node-tracker.contextgenerator.md) | A context generator is a user-supplied callback that is evaluated for each event to allow an additional context to be dynamically attached to the event | +| [ContextPrimitive](./node-tracker.contextprimitive.md) | A context primitive is either a self-describing JSON or a context generator | +| [CustomEmitter](./node-tracker.customemitter.md) | | +| [EventBatch](./node-tracker.eventbatch.md) | A collection of event payloads which are sent to the collector. | +| [EventJson](./node-tracker.eventjson.md) | An array of tuples which represents the unprocessed JSON to be added to the Payload | +| [EventJsonWithKeys](./node-tracker.eventjsonwithkeys.md) | A tuple which represents the unprocessed JSON to be added to the Payload | +| [EventMethod](./node-tracker.eventmethod.md) | | +| [FilterProvider](./node-tracker.filterprovider.md) | A filter provider is a tuple that has two parts: a context filter and the context primitive(s) If the context filter evaluates to true, the tracker will attach the context primitive(s) | +| [FormElement](./node-tracker.formelement.md) | A representation of an element within a form | +| [JsonProcessor](./node-tracker.jsonprocessor.md) | A function which will processor the Json onto the injected PayloadBuilder | +| [NodeEmitterConfiguration](./node-tracker.nodeemitterconfiguration.md) | | | [Payload](./node-tracker.payload.md) | Type for a Payload dictionary | +| [RequestFailure](./node-tracker.requestfailure.md) | The data that will be available to the onRequestFailure callback | +| [RuleSetProvider](./node-tracker.rulesetprovider.md) | A ruleset provider is aa tuple that has two parts: a ruleset and the context primitive(s) If the ruleset allows the current event schema URI, the tracker will attach the context primitive(s) | | [SelfDescribingJson](./node-tracker.selfdescribingjson.md) | Export interface for any Self-Describing JSON such as context or Self Describing events | | [Timestamp](./node-tracker.timestamp.md) | Algebraic datatype representing possible timestamp type choice | diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.newtracker.md b/api-docs/docs/node-tracker/markdown/node-tracker.newtracker.md new file mode 100644 index 000000000..249945dff --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.newtracker.md @@ -0,0 +1,23 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [newTracker](./node-tracker.newtracker.md) + +## newTracker() function + +Signature: + +```typescript +declare function newTracker(trackerConfiguration: TrackerConfiguration, emitterConfiguration: NodeEmitterConfiguration | NodeEmitterConfiguration[]): Tracker; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| trackerConfiguration | TrackerConfiguration | | +| emitterConfiguration | NodeEmitterConfiguration \| NodeEmitterConfiguration\[\] | | + +Returns: + +Tracker + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.nodeemitterconfiguration.md b/api-docs/docs/node-tracker/markdown/node-tracker.nodeemitterconfiguration.md new file mode 100644 index 000000000..e73ca14f6 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.nodeemitterconfiguration.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [NodeEmitterConfiguration](./node-tracker.nodeemitterconfiguration.md) + +## NodeEmitterConfiguration type + +Signature: + +```typescript +type NodeEmitterConfiguration = CustomEmitter | EmitterConfiguration; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.requestfailure.md b/api-docs/docs/node-tracker/markdown/node-tracker.requestfailure.md new file mode 100644 index 000000000..e47d213b6 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.requestfailure.md @@ -0,0 +1,18 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [RequestFailure](./node-tracker.requestfailure.md) + +## RequestFailure type + +The data that will be available to the `onRequestFailure` callback + +Signature: + +```typescript +type RequestFailure = { + events: EventBatch; + status?: number; + message?: string; + willRetry: boolean; +}; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.ruleset.accept.md b/api-docs/docs/node-tracker/markdown/node-tracker.ruleset.accept.md new file mode 100644 index 000000000..8c3838662 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.ruleset.accept.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [RuleSet](./node-tracker.ruleset.md) > [accept](./node-tracker.ruleset.accept.md) + +## RuleSet.accept property + +Signature: + +```typescript +accept?: Array | string; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.ruleset.md b/api-docs/docs/node-tracker/markdown/node-tracker.ruleset.md new file mode 100644 index 000000000..7a2586af1 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.ruleset.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [RuleSet](./node-tracker.ruleset.md) + +## RuleSet interface + +A ruleset has accept or reject properties that contain rules for matching Iglu schema URIs + +Signature: + +```typescript +interface RuleSet +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [accept?](./node-tracker.ruleset.accept.md) | Array<string> \| string | (Optional) | +| [reject?](./node-tracker.ruleset.reject.md) | Array<string> \| string | (Optional) | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.ruleset.reject.md b/api-docs/docs/node-tracker/markdown/node-tracker.ruleset.reject.md new file mode 100644 index 000000000..d82f080a0 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.ruleset.reject.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [RuleSet](./node-tracker.ruleset.md) > [reject](./node-tracker.ruleset.reject.md) + +## RuleSet.reject property + +Signature: + +```typescript +reject?: Array | string; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.rulesetprovider.md b/api-docs/docs/node-tracker/markdown/node-tracker.rulesetprovider.md new file mode 100644 index 000000000..728190057 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.rulesetprovider.md @@ -0,0 +1,16 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [RuleSetProvider](./node-tracker.rulesetprovider.md) + +## RuleSetProvider type + +A ruleset provider is aa tuple that has two parts: a ruleset and the context primitive(s) If the ruleset allows the current event schema URI, the tracker will attach the context primitive(s) + +Signature: + +```typescript +type RuleSetProvider = [ + RuleSet, + Array | ContextPrimitive +]; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingevent.event.md b/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingevent.event.md index cf7c8fc0c..d66b67808 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingevent.event.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingevent.event.md @@ -9,5 +9,5 @@ The Self Describing JSON which describes the event Signature: ```typescript -event: SelfDescribingJson; +event: SelfDescribingJson; ``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingevent.md b/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingevent.md index 841e9e3c0..47653d298 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingevent.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingevent.md @@ -9,12 +9,12 @@ A Self Describing Event A custom event type, allowing for an event to be tracked Signature: ```typescript -interface SelfDescribingEvent +interface SelfDescribingEvent> ``` ## Properties | Property | Type | Description | | --- | --- | --- | -| [event](./node-tracker.selfdescribingevent.event.md) | SelfDescribingJson | The Self Describing JSON which describes the event | +| [event](./node-tracker.selfdescribingevent.event.md) | SelfDescribingJson<T> | The Self Describing JSON which describes the event | diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingjson.md b/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingjson.md index 7807f98f5..14c5d7379 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingjson.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.selfdescribingjson.md @@ -9,8 +9,8 @@ Export interface for any Self-Describing JSON such as context or Self Describing Signature: ```typescript -type SelfDescribingJson = Record> = { +type SelfDescribingJson> = { schema: string; - data: T; + data: T extends any[] ? never : T extends {} ? T : never; }; ``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.tracker.flush.md b/api-docs/docs/node-tracker/markdown/node-tracker.tracker.flush.md new file mode 100644 index 000000000..33e7a2511 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.tracker.flush.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [Tracker](./node-tracker.tracker.md) > [flush](./node-tracker.tracker.flush.md) + +## Tracker.flush property + +Calls flush on all emitters in order to send all queued events to the collector + +Signature: + +```typescript +flush: () => Promise; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.tracker.md b/api-docs/docs/node-tracker/markdown/node-tracker.tracker.md index 8974598de..04f6d564b 100644 --- a/api-docs/docs/node-tracker/markdown/node-tracker.tracker.md +++ b/api-docs/docs/node-tracker/markdown/node-tracker.tracker.md @@ -15,6 +15,7 @@ interface Tracker extends TrackerCore | Property | Type | Description | | --- | --- | --- | +| [flush](./node-tracker.tracker.flush.md) | () => Promise<void> | Calls flush on all emitters in order to send all queued events to the collector | | [setDomainUserId](./node-tracker.tracker.setdomainuserid.md) | (userId: string) => void | Set the domain user ID | | [setNetworkUserId](./node-tracker.tracker.setnetworkuserid.md) | (userId: string) => void | Set the network user ID | | [setSessionId](./node-tracker.tracker.setsessionid.md) | (sessionId: string) => void | Set the session ID (domain_sessionid in the atomic events) | diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.appid.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.appid.md new file mode 100644 index 000000000..79af4dab6 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.appid.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerConfiguration](./node-tracker.trackerconfiguration.md) > [appId](./node-tracker.trackerconfiguration.appid.md) + +## TrackerConfiguration.appId property + +Signature: + +```typescript +appId: string; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.encodebase64.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.encodebase64.md new file mode 100644 index 000000000..cbdfeab70 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.encodebase64.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerConfiguration](./node-tracker.trackerconfiguration.md) > [encodeBase64](./node-tracker.trackerconfiguration.encodebase64.md) + +## TrackerConfiguration.encodeBase64 property + +Whether unstructured events and custom contexts should be base64 encoded. + +Signature: + +```typescript +encodeBase64?: boolean; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.md new file mode 100644 index 000000000..ee79deea5 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerConfiguration](./node-tracker.trackerconfiguration.md) + +## TrackerConfiguration interface + +Signature: + +```typescript +interface TrackerConfiguration +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [appId](./node-tracker.trackerconfiguration.appid.md) | string | | +| [encodeBase64?](./node-tracker.trackerconfiguration.encodebase64.md) | boolean | (Optional) Whether unstructured events and custom contexts should be base64 encoded. | +| [namespace](./node-tracker.trackerconfiguration.namespace.md) | string | | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.namespace.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.namespace.md new file mode 100644 index 000000000..754927d1f --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackerconfiguration.namespace.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerConfiguration](./node-tracker.trackerconfiguration.md) > [namespace](./node-tracker.trackerconfiguration.namespace.md) + +## TrackerConfiguration.namespace property + +Signature: + +```typescript +namespace: string; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addglobalcontexts.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addglobalcontexts.md new file mode 100644 index 000000000..33f03e909 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addglobalcontexts.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [addGlobalContexts](./node-tracker.trackercore.addglobalcontexts.md) + +## TrackerCore.addGlobalContexts() method + +Adds contexts globally, contexts added here will be attached to all applicable events + +Signature: + +```typescript +addGlobalContexts(contexts: Array | Record): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| contexts | Array<ConditionalContextProvider \| ContextPrimitive> \| Record<string, ConditionalContextProvider \| ContextPrimitive> | An array containing either contexts or a conditional contexts | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addpayloaddict.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addpayloaddict.md new file mode 100644 index 000000000..4a8c576b6 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addpayloaddict.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [addPayloadDict](./node-tracker.trackercore.addpayloaddict.md) + +## TrackerCore.addPayloadDict() method + +Merges a dictionary into payloadPairs + +Signature: + +```typescript +addPayloadDict(dict: Payload): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| dict | Payload | Adds a new payload dictionary to the existing one | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addpayloadpair.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addpayloadpair.md new file mode 100644 index 000000000..8c04fdd85 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addpayloadpair.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [addPayloadPair](./node-tracker.trackercore.addpayloadpair.md) + +## TrackerCore.addPayloadPair property + +Set a persistent key-value pair to be added to every payload + +Signature: + +```typescript +addPayloadPair: (key: string, value: unknown) => void; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addplugin.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addplugin.md new file mode 100644 index 000000000..8ccf26ba8 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.addplugin.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [addPlugin](./node-tracker.trackercore.addplugin.md) + +## TrackerCore.addPlugin() method + +Add a plugin into the plugin collection after Core has already been initialised + +Signature: + +```typescript +addPlugin(configuration: CorePluginConfiguration): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| configuration | CorePluginConfiguration | The plugin to add | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.clearglobalcontexts.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.clearglobalcontexts.md new file mode 100644 index 000000000..2d6ee4c11 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.clearglobalcontexts.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [clearGlobalContexts](./node-tracker.trackercore.clearglobalcontexts.md) + +## TrackerCore.clearGlobalContexts() method + +Removes all global contexts + +Signature: + +```typescript +clearGlobalContexts(): void; +``` +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.getbase64encoding.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.getbase64encoding.md new file mode 100644 index 000000000..45dfaa989 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.getbase64encoding.md @@ -0,0 +1,17 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [getBase64Encoding](./node-tracker.trackercore.getbase64encoding.md) + +## TrackerCore.getBase64Encoding() method + +Get current base64 encoding state + +Signature: + +```typescript +getBase64Encoding(): boolean; +``` +Returns: + +boolean + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.md new file mode 100644 index 000000000..2fce40449 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.md @@ -0,0 +1,46 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) + +## TrackerCore interface + +Export interface containing all Core functions + +Signature: + +```typescript +interface TrackerCore +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [addPayloadPair](./node-tracker.trackercore.addpayloadpair.md) | (key: string, value: unknown) => void | Set a persistent key-value pair to be added to every payload | +| [track](./node-tracker.trackercore.track.md) | (pb: PayloadBuilder, context?: Array<SelfDescribingJson> \| null, timestamp?: Timestamp \| null) => Payload \| undefined | Call with a payload from a buildX function Adds context and payloadPairs name-value pairs to the payload Applies the callback to the built payload | + +## Methods + +| Method | Description | +| --- | --- | +| [addGlobalContexts(contexts)](./node-tracker.trackercore.addglobalcontexts.md) | Adds contexts globally, contexts added here will be attached to all applicable events | +| [addPayloadDict(dict)](./node-tracker.trackercore.addpayloaddict.md) | Merges a dictionary into payloadPairs | +| [addPlugin(configuration)](./node-tracker.trackercore.addplugin.md) | Add a plugin into the plugin collection after Core has already been initialised | +| [clearGlobalContexts()](./node-tracker.trackercore.clearglobalcontexts.md) | Removes all global contexts | +| [getBase64Encoding()](./node-tracker.trackercore.getbase64encoding.md) | Get current base64 encoding state | +| [removeGlobalContexts(contexts)](./node-tracker.trackercore.removeglobalcontexts.md) | Removes previously added global context, performs a deep comparison of the contexts or conditional contexts | +| [resetPayloadPairs(dict)](./node-tracker.trackercore.resetpayloadpairs.md) | Replace payloadPairs with a new dictionary | +| [setAppId(appId)](./node-tracker.trackercore.setappid.md) | Set the application ID | +| [setBase64Encoding(encode)](./node-tracker.trackercore.setbase64encoding.md) | Turn base 64 encoding on or off | +| [setColorDepth(depth)](./node-tracker.trackercore.setcolordepth.md) | Set the color depth | +| [setIpAddress(ip)](./node-tracker.trackercore.setipaddress.md) | Set the IP address | +| [setLang(lang)](./node-tracker.trackercore.setlang.md) | Set the language | +| [setPlatform(value)](./node-tracker.trackercore.setplatform.md) | Set the platform | +| [setScreenResolution(width, height)](./node-tracker.trackercore.setscreenresolution.md) | Set the screen resolution | +| [setTimezone(timezone)](./node-tracker.trackercore.settimezone.md) | Set the timezone | +| [setTrackerNamespace(name)](./node-tracker.trackercore.settrackernamespace.md) | Set the tracker namespace | +| [setTrackerVersion(version)](./node-tracker.trackercore.settrackerversion.md) | Set the tracker version | +| [setUseragent(useragent)](./node-tracker.trackercore.setuseragent.md) | Set the Useragent | +| [setUserId(userId)](./node-tracker.trackercore.setuserid.md) | Set the user ID | +| [setViewport(width, height)](./node-tracker.trackercore.setviewport.md) | Set the viewport dimensions | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.removeglobalcontexts.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.removeglobalcontexts.md new file mode 100644 index 000000000..f4e305cc2 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.removeglobalcontexts.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [removeGlobalContexts](./node-tracker.trackercore.removeglobalcontexts.md) + +## TrackerCore.removeGlobalContexts() method + +Removes previously added global context, performs a deep comparison of the contexts or conditional contexts + +Signature: + +```typescript +removeGlobalContexts(contexts: Array): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| contexts | Array<ConditionalContextProvider \| ContextPrimitive \| string> | An array containing either contexts or a conditional contexts | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.resetpayloadpairs.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.resetpayloadpairs.md new file mode 100644 index 000000000..f4482ad38 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.resetpayloadpairs.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [resetPayloadPairs](./node-tracker.trackercore.resetpayloadpairs.md) + +## TrackerCore.resetPayloadPairs() method + +Replace payloadPairs with a new dictionary + +Signature: + +```typescript +resetPayloadPairs(dict: Payload): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| dict | Payload | Resets all current payload pairs with a new dictionary of pairs | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setappid.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setappid.md new file mode 100644 index 000000000..b73fd649b --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setappid.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setAppId](./node-tracker.trackercore.setappid.md) + +## TrackerCore.setAppId() method + +Set the application ID + +Signature: + +```typescript +setAppId(appId: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| appId | string | An application ID which identifies the current application | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setbase64encoding.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setbase64encoding.md new file mode 100644 index 000000000..3ea7a8851 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setbase64encoding.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setBase64Encoding](./node-tracker.trackercore.setbase64encoding.md) + +## TrackerCore.setBase64Encoding() method + +Turn base 64 encoding on or off + +Signature: + +```typescript +setBase64Encoding(encode: boolean): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| encode | boolean | Whether to encode payload | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setcolordepth.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setcolordepth.md new file mode 100644 index 000000000..64bb3f4b2 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setcolordepth.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setColorDepth](./node-tracker.trackercore.setcolordepth.md) + +## TrackerCore.setColorDepth() method + +Set the color depth + +Signature: + +```typescript +setColorDepth(depth: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| depth | string | A color depth value as string | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setipaddress.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setipaddress.md new file mode 100644 index 000000000..3f426a0a2 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setipaddress.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setIpAddress](./node-tracker.trackercore.setipaddress.md) + +## TrackerCore.setIpAddress() method + +Set the IP address + +Signature: + +```typescript +setIpAddress(ip: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| ip | string | An IP Address string | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setlang.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setlang.md new file mode 100644 index 000000000..c2873793a --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setlang.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setLang](./node-tracker.trackercore.setlang.md) + +## TrackerCore.setLang() method + +Set the language + +Signature: + +```typescript +setLang(lang: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| lang | string | A language string e.g. 'en-UK' | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setplatform.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setplatform.md new file mode 100644 index 000000000..34aafb3ea --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setplatform.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setPlatform](./node-tracker.trackercore.setplatform.md) + +## TrackerCore.setPlatform() method + +Set the platform + +Signature: + +```typescript +setPlatform(value: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| value | string | A valid Snowplow platform value | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setscreenresolution.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setscreenresolution.md new file mode 100644 index 000000000..3fb148e58 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setscreenresolution.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setScreenResolution](./node-tracker.trackercore.setscreenresolution.md) + +## TrackerCore.setScreenResolution() method + +Set the screen resolution + +Signature: + +```typescript +setScreenResolution(width: string, height: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| width | string | screen resolution width | +| height | string | screen resolution height | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.settimezone.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.settimezone.md new file mode 100644 index 000000000..1e0dc7363 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.settimezone.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setTimezone](./node-tracker.trackercore.settimezone.md) + +## TrackerCore.setTimezone() method + +Set the timezone + +Signature: + +```typescript +setTimezone(timezone: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| timezone | string | A timezone string | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.settrackernamespace.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.settrackernamespace.md new file mode 100644 index 000000000..2df0c2cb2 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.settrackernamespace.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setTrackerNamespace](./node-tracker.trackercore.settrackernamespace.md) + +## TrackerCore.setTrackerNamespace() method + +Set the tracker namespace + +Signature: + +```typescript +setTrackerNamespace(name: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| name | string | The trackers namespace | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.settrackerversion.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.settrackerversion.md new file mode 100644 index 000000000..8fd1ea49b --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.settrackerversion.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setTrackerVersion](./node-tracker.trackercore.settrackerversion.md) + +## TrackerCore.setTrackerVersion() method + +Set the tracker version + +Signature: + +```typescript +setTrackerVersion(version: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| version | string | The version of the current tracker | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setuseragent.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setuseragent.md new file mode 100644 index 000000000..d170ecb1d --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setuseragent.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setUseragent](./node-tracker.trackercore.setuseragent.md) + +## TrackerCore.setUseragent() method + +Set the Useragent + +Signature: + +```typescript +setUseragent(useragent: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| useragent | string | A useragent string | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setuserid.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setuserid.md new file mode 100644 index 000000000..5d531adc5 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setuserid.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setUserId](./node-tracker.trackercore.setuserid.md) + +## TrackerCore.setUserId() method + +Set the user ID + +Signature: + +```typescript +setUserId(userId: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| userId | string | The custom user id | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setviewport.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setviewport.md new file mode 100644 index 000000000..8837314ca --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.setviewport.md @@ -0,0 +1,25 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [setViewport](./node-tracker.trackercore.setviewport.md) + +## TrackerCore.setViewport() method + +Set the viewport dimensions + +Signature: + +```typescript +setViewport(width: string, height: string): void; +``` + +## Parameters + +| Parameter | Type | Description | +| --- | --- | --- | +| width | string | viewport width | +| height | string | viewport height | + +Returns: + +void + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.track.md b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.track.md new file mode 100644 index 000000000..61465baf0 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.trackercore.track.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrackerCore](./node-tracker.trackercore.md) > [track](./node-tracker.trackercore.track.md) + +## TrackerCore.track property + +Call with a payload from a buildX function Adds context and payloadPairs name-value pairs to the payload Applies the callback to the built payload + +Signature: + +```typescript +track: (pb: PayloadBuilder, context?: Array | null, timestamp?: Timestamp | null) => Payload | undefined; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.truetimestamp.md b/api-docs/docs/node-tracker/markdown/node-tracker.truetimestamp.md new file mode 100644 index 000000000..c5f3be780 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.truetimestamp.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrueTimestamp](./node-tracker.truetimestamp.md) + +## TrueTimestamp interface + +A representation of a True Timestamp (ttm) + +Signature: + +```typescript +interface TrueTimestamp +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [type](./node-tracker.truetimestamp.type.md) | "ttm" | | +| [value](./node-tracker.truetimestamp.value.md) | number | | + diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.truetimestamp.type.md b/api-docs/docs/node-tracker/markdown/node-tracker.truetimestamp.type.md new file mode 100644 index 000000000..9420c2f0e --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.truetimestamp.type.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrueTimestamp](./node-tracker.truetimestamp.md) > [type](./node-tracker.truetimestamp.type.md) + +## TrueTimestamp.type property + +Signature: + +```typescript +readonly type: "ttm"; +``` diff --git a/api-docs/docs/node-tracker/markdown/node-tracker.truetimestamp.value.md b/api-docs/docs/node-tracker/markdown/node-tracker.truetimestamp.value.md new file mode 100644 index 000000000..2107c7a71 --- /dev/null +++ b/api-docs/docs/node-tracker/markdown/node-tracker.truetimestamp.value.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [@snowplow/node-tracker](./node-tracker.md) > [TrueTimestamp](./node-tracker.truetimestamp.md) > [value](./node-tracker.truetimestamp.value.md) + +## TrueTimestamp.value property + +Signature: + +```typescript +readonly value: number; +``` diff --git a/api-docs/docs/node-tracker/node-tracker.api.md b/api-docs/docs/node-tracker/node-tracker.api.md index 8d75db93e..51e1c54f1 100644 --- a/api-docs/docs/node-tracker/node-tracker.api.md +++ b/api-docs/docs/node-tracker/node-tracker.api.md @@ -4,13 +4,6 @@ ```ts -import { Agents } from 'got'; -import { PromiseCookieJar } from 'got'; -import { RequestError } from 'got'; -import { RequiredRetryOptions } from 'got'; -import { Response } from 'got'; -import { ToughCookieJar } from 'got'; - // @public export interface AdClickEvent { advertiserId?: string; @@ -105,7 +98,7 @@ export function buildRemoveFromCart(event: RemoveFromCartEvent): PayloadBuilder; export function buildScreenView(event: ScreenViewEvent): PayloadBuilder; // @public -export function buildSelfDescribingEvent(event: SelfDescribingEvent): PayloadBuilder; +export function buildSelfDescribingEvent>(event: SelfDescribingEvent): PayloadBuilder; // @public export function buildSiteSearch(event: SiteSearchEvent): PayloadBuilder; @@ -116,6 +109,9 @@ export function buildSocialInteraction(event: SocialInteractionEvent): PayloadBu // @public export function buildStructEvent(event: StructuredEvent): PayloadBuilder; +// @public +export type ConditionalContextProvider = FilterProvider | RuleSetProvider; + // @public export interface ConsentGrantedEvent { description?: string; @@ -147,6 +143,9 @@ export type ContextFilter = (args?: ContextEvent) => boolean; // @public export type ContextGenerator = (args?: ContextEvent) => SelfDescribingJson | SelfDescribingJson[] | undefined; +// @public +export type ContextPrimitive = SelfDescribingJson | ContextGenerator; + // @public export interface CoreConfiguration { /* Should payloads be base64 encoded when built */ @@ -162,15 +161,35 @@ export interface CoreConfiguration { // @public export interface CorePlugin { - // Warning: (ae-forgotten-export) The symbol "TrackerCore" needs to be exported by the entry point index.module.d.ts activateCorePlugin?: (core: TrackerCore) => void; afterTrack?: (payload: Payload) => void; beforeTrack?: (payloadBuilder: PayloadBuilder) => void; contexts?: () => SelfDescribingJson[]; - // Warning: (ae-forgotten-export) The symbol "Logger" needs to be exported by the entry point index.module.d.ts + filter?: (payload: Payload) => boolean; logger?: (logger: Logger) => void; } +// @public +export interface CorePluginConfiguration { + /* The plugin to add */ + // (undocumented) + plugin: CorePlugin; +} + +// @public (undocumented) +export type CustomEmitter = { + /* Function returning custom Emitter or Emitter[] to be used. If set, other options are irrelevant */ + customEmitter: () => Emitter | Array; +}; + +// @public +export interface DeviceTimestamp { + // (undocumented) + readonly type: "dtm"; + // (undocumented) + readonly value: number; +} + // @public export interface EcommerceTransactionEvent { affiliation?: string; @@ -195,21 +214,118 @@ export interface EcommerceTransactionItemEvent { sku: string; } -// @public (undocumented) +// @public export interface Emitter { + flush: () => Promise; + input: (payload: Payload) => Promise; + setAnonymousTracking: (anonymous: boolean) => void; + setBufferSize: (bufferSize: number) => void; + setCollectorUrl: (url: string) => void; +} + +// @public (undocumented) +export interface EmitterConfiguration extends EmitterConfigurationBase { + /* The collector URL to which events will be sent */ + // (undocumented) + endpoint: string; + /* http or https. Defaults to https */ // (undocumented) - flush: () => void; + port?: number; + /* http or https. Defaults to https */ // (undocumented) - input: (payload: Payload) => void; - setAnonymization?: (shouldAnonymize: boolean) => void; + protocol?: "http" | "https"; + /* http or https. Defaults to https */ + // (undocumented) + serverAnonymization?: boolean; +} + +// @public (undocumented) +export interface EmitterConfigurationBase { + bufferSize?: number; + connectionTimeout?: number; + credentials?: "omit" | "same-origin" | "include"; + customFetch?: (input: Request, options?: RequestInit) => Promise; + customHeaders?: Record; + dontRetryStatusCodes?: number[]; + eventMethod?: EventMethod; + eventStore?: EventStore; + idService?: string; + keepalive?: boolean; + maxGetBytes?: number; + maxPostBytes?: number; + onRequestFailure?: (data: RequestFailure, response?: Response) => void; + onRequestSuccess?: (data: EventBatch, response: Response) => void; + postPath?: string; + retryFailedRequests?: boolean; + retryStatusCodes?: number[]; + useStm?: boolean; } +// @public +export type EventBatch = Payload[]; + +// @public +export type EventJson = Array; + +// @public +export type EventJsonWithKeys = { + keyIfEncoded: string; + keyIfNotEncoded: string; + json: Record; +}; + +// @public (undocumented) +export type EventMethod = "post" | "get"; + // @public export interface EventPayloadAndContext { context: Array; event: PayloadBuilder; } +// @public +export interface EventStore { + add: (payload: EventStorePayload) => Promise; + count: () => Promise; + getAll: () => Promise; + getAllPayloads: () => Promise; + iterator: () => EventStoreIterator; + removeHead: (count: number) => Promise; +} + +// @public (undocumented) +export interface EventStoreConfiguration { + maxSize?: number; +} + +// @public (undocumented) +export interface EventStoreIterator { + next: () => Promise<{ + value: EventStorePayload | undefined; + done: boolean; + }>; +} + +// @public (undocumented) +export interface EventStorePayload { + payload: Payload; + svrAnon?: boolean; +} + +// @public +export type FilterProvider = [ +ContextFilter, +Array | ContextPrimitive +]; + +// @public +export type FormElement = { + name: string; + value: string | null; + nodeName: string; + type?: string | null; +}; + // @public export interface FormFocusOrChangeEvent { elementClasses?: Array | null; @@ -223,39 +339,56 @@ export interface FormFocusOrChangeEvent { // @public export interface FormSubmissionEvent { - // Warning: (ae-forgotten-export) The symbol "FormElement" needs to be exported by the entry point index.module.d.ts elements?: Array; formClasses?: Array; formId: string; } // @public -export function gotEmitter(endpoint: string, protocol?: HttpProtocol, port?: number, method?: HttpMethod, bufferSize?: number, retry?: number | Partial, cookieJar?: PromiseCookieJar | ToughCookieJar, callback?: (error?: RequestError, response?: Response) => void, agents?: Agents, serverAnonymization?: boolean): Emitter; +export type JsonProcessor = (payloadBuilder: PayloadBuilder, jsonForProcessing: EventJson, contextEntitiesForProcessing: SelfDescribingJson[]) => void; + +// @public +export interface LinkClickEvent { + elementClasses?: Array; + elementContent?: string; + elementId?: string; + elementTarget?: string; + targetUrl: string; +} // @public (undocumented) -export enum HttpMethod { +export enum LOG_LEVEL { // (undocumented) - GET = "get", + debug = 3, // (undocumented) - POST = "post" + error = 1, + // (undocumented) + info = 4, + // (undocumented) + none = 0, + // (undocumented) + warn = 2 } // @public (undocumented) -export enum HttpProtocol { +export interface Logger { + // (undocumented) + debug: (message: string, ...extraParams: unknown[]) => void; // (undocumented) - HTTP = "http", + error: (message: string, error?: unknown, ...extraParams: unknown[]) => void; // (undocumented) - HTTPS = "https" + info: (message: string, ...extraParams: unknown[]) => void; + // (undocumented) + setLogLevel: (level: LOG_LEVEL) => void; + // (undocumented) + warn: (message: string, error?: unknown, ...extraParams: unknown[]) => void; } -// @public -export interface LinkClickEvent { - elementClasses?: Array; - elementContent?: string; - elementId?: string; - elementTarget?: string; - targetUrl: string; -} +// @public (undocumented) +export function newTracker(trackerConfiguration: TrackerConfiguration, emitterConfiguration: NodeEmitterConfiguration | NodeEmitterConfiguration[]): Tracker; + +// @public (undocumented) +export type NodeEmitterConfiguration = CustomEmitter | EmitterConfiguration; // @public export interface PagePingEvent extends PageViewEvent { @@ -282,10 +415,8 @@ export interface PayloadBuilder { addDict: (dict: Payload) => void; addJson: (keyIfEncoded: string, keyIfNotEncoded: string, json: Record) => void; build: () => Payload; - // Warning: (ae-forgotten-export) The symbol "EventJson" needs to be exported by the entry point index.module.d.ts getJson: () => EventJson; getPayload: () => Payload; - // Warning: (ae-forgotten-export) The symbol "JsonProcessor" needs to be exported by the entry point index.module.d.ts withJsonProcessor: (jsonProcessor: JsonProcessor) => void; } @@ -299,6 +430,28 @@ export interface RemoveFromCartEvent { unitPrice?: number; } +// @public +export type RequestFailure = { + events: EventBatch; + status?: number; + message?: string; + willRetry: boolean; +}; + +// @public +export interface RuleSet { + // (undocumented) + accept?: Array | string; + // (undocumented) + reject?: Array | string; +} + +// @public +export type RuleSetProvider = [ +RuleSet, +Array | ContextPrimitive +]; + // @public export interface ScreenViewEvent { id?: string; @@ -306,14 +459,14 @@ export interface ScreenViewEvent { } // @public -export interface SelfDescribingEvent { - event: SelfDescribingJson; +export interface SelfDescribingEvent> { + event: SelfDescribingJson; } // @public -export type SelfDescribingJson = Record> = { +export type SelfDescribingJson> = { schema: string; - data: T; + data: T extends any[] ? never : T extends {} ? T : never; }; // @public @@ -345,22 +498,63 @@ export interface StructuredEvent { value?: number; } -// Warning: (ae-forgotten-export) The symbol "TrueTimestamp" needs to be exported by the entry point index.module.d.ts -// Warning: (ae-forgotten-export) The symbol "DeviceTimestamp" needs to be exported by the entry point index.module.d.ts -// // @public export type Timestamp = TrueTimestamp | DeviceTimestamp | number; // @public (undocumented) export interface Tracker extends TrackerCore { + flush: () => Promise; setDomainUserId: (userId: string) => void; setNetworkUserId: (userId: string) => void; setSessionId: (sessionId: string) => void; setSessionIndex: (sessionIndex: string | number) => void; } +// @public (undocumented) +export interface TrackerConfiguration { + /* The namespace of the tracker */ + // (undocumented) + appId: string; + /* The application ID */ + encodeBase64?: boolean; + /* The application ID */ + // (undocumented) + namespace: string; +} + // @public -export function tracker(emitters: Emitter | Array, namespace: string, appId: string, encodeBase64: boolean): Tracker; +export interface TrackerCore { + addGlobalContexts(contexts: Array | Record): void; + addPayloadDict(dict: Payload): void; + addPayloadPair: (key: string, value: unknown) => void; + addPlugin(configuration: CorePluginConfiguration): void; + clearGlobalContexts(): void; + getBase64Encoding(): boolean; + removeGlobalContexts(contexts: Array): void; + resetPayloadPairs(dict: Payload): void; + setAppId(appId: string): void; + setBase64Encoding(encode: boolean): void; + setColorDepth(depth: string): void; + setIpAddress(ip: string): void; + setLang(lang: string): void; + setPlatform(value: string): void; + setScreenResolution(width: string, height: string): void; + setTimezone(timezone: string): void; + setTrackerNamespace(name: string): void; + setTrackerVersion(version: string): void; + setUseragent(useragent: string): void; + setUserId(userId: string): void; + setViewport(width: string, height: string): void; + track: (pb: PayloadBuilder, context?: Array | null, timestamp?: Timestamp | null) => Payload | undefined; +} + +// @public +export interface TrueTimestamp { + // (undocumented) + readonly type: "ttm"; + // (undocumented) + readonly value: number; +} // @public (undocumented) export const version: string; diff --git a/common/changes/@snowplow/browser-plugin-ad-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/browser-plugin-ad-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..63ab22cfd --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-ad-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-ad-tracking", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-ad-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-browser-features/feature-revise-default-plugins_2023-02-06-13-02.json b/common/changes/@snowplow/browser-plugin-browser-features/feature-revise-default-plugins_2023-02-06-13-02.json new file mode 100644 index 000000000..41b45477e --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-browser-features/feature-revise-default-plugins_2023-02-06-13-02.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-browser-features", + "comment": "Add deprecation warning", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-browser-features" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-button-click-tracking/button-plugin-capture_2024-10-04-03-17.json b/common/changes/@snowplow/browser-plugin-button-click-tracking/button-plugin-capture_2024-10-04-03-17.json new file mode 100644 index 000000000..c77cf0c80 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-button-click-tracking/button-plugin-capture_2024-10-04-03-17.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-button-click-tracking", + "comment": "Use capture-phase event listeners", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-button-click-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-button-click-tracking/shadow-clicks_2024-10-04-05-19.json b/common/changes/@snowplow/browser-plugin-button-click-tracking/shadow-clicks_2024-10-04-05-19.json new file mode 100644 index 000000000..debb3bed2 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-button-click-tracking/shadow-clicks_2024-10-04-05-19.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-button-click-tracking", + "comment": "Detect button clicks within ShadowRoots", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-button-click-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-consent/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/browser-plugin-consent/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..b92b62a0a --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-consent/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-consent", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-consent" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-consent/feature-revise-default-plugins_2023-02-06-08-43.json b/common/changes/@snowplow/browser-plugin-consent/feature-revise-default-plugins_2023-02-06-08-43.json new file mode 100644 index 000000000..37fb44b58 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-consent/feature-revise-default-plugins_2023-02-06-08-43.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-consent", + "comment": "Add deprecation warning", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-consent" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-ecommerce/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/browser-plugin-ecommerce/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..b5c87e859 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-ecommerce/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-ecommerce", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-ecommerce" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-ecommerce/feature-revise-default-plugins_2023-02-06-08-43.json b/common/changes/@snowplow/browser-plugin-ecommerce/feature-revise-default-plugins_2023-02-06-08-43.json new file mode 100644 index 000000000..227d2f55d --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-ecommerce/feature-revise-default-plugins_2023-02-06-08-43.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-ecommerce", + "comment": "Add deprecation warning", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-ecommerce" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-enhanced-consent/2023-02-06-11-05.json b/common/changes/@snowplow/browser-plugin-enhanced-consent/2023-02-06-11-05.json new file mode 100644 index 000000000..68137719e --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-enhanced-consent/2023-02-06-11-05.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-enhanced-consent", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-enhanced-consent" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-enhanced-ecommerce/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/browser-plugin-enhanced-ecommerce/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..4b59237c0 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-enhanced-ecommerce/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-enhanced-ecommerce", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-enhanced-ecommerce" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-enhanced-ecommerce/feature-revise-default-plugins_2023-02-06-13-02.json b/common/changes/@snowplow/browser-plugin-enhanced-ecommerce/feature-revise-default-plugins_2023-02-06-13-02.json new file mode 100644 index 000000000..33d0167b3 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-enhanced-ecommerce/feature-revise-default-plugins_2023-02-06-13-02.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-enhanced-ecommerce", + "comment": "Add deprecation warning", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-enhanced-ecommerce" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-error-tracking/error-clarification_2024-09-06-06-28.json b/common/changes/@snowplow/browser-plugin-error-tracking/error-clarification_2024-09-06-06-28.json new file mode 100644 index 000000000..6692c99b5 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-error-tracking/error-clarification_2024-09-06-06-28.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-error-tracking", + "comment": "Fix message when a resource triggers error instead of script", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-error-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-error-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/browser-plugin-error-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..cfccb7027 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-error-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-error-tracking", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-error-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-focalmeter/2023-02-06-11-05.json b/common/changes/@snowplow/browser-plugin-focalmeter/2023-02-06-11-05.json new file mode 100644 index 000000000..4a64c0f99 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-focalmeter/2023-02-06-11-05.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-focalmeter", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-focalmeter" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-form-tracking/PE-6528-formsv4_2024-07-17-01-15.json b/common/changes/@snowplow/browser-plugin-form-tracking/PE-6528-formsv4_2024-07-17-01-15.json new file mode 100644 index 000000000..603fcbc0b --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-form-tracking/PE-6528-formsv4_2024-07-17-01-15.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-form-tracking", + "comment": "Add event delegation for link click tracking", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-form-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-form-tracking/shadow-clicks_2024-10-08-04-36.json b/common/changes/@snowplow/browser-plugin-form-tracking/shadow-clicks_2024-10-08-04-36.json new file mode 100644 index 000000000..2026fbb17 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-form-tracking/shadow-clicks_2024-10-08-04-36.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-form-tracking", + "comment": "Detect form events within ShadowRoots", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-form-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-ga-cookies/gacookies-switch-default_2024-10-04-03-42.json b/common/changes/@snowplow/browser-plugin-ga-cookies/gacookies-switch-default_2024-10-04-03-42.json new file mode 100644 index 000000000..87d2480b2 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-ga-cookies/gacookies-switch-default_2024-10-04-03-42.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-ga-cookies", + "comment": "Change default from UA to GA4", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-ga-cookies" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-link-click-tracking/PE-4138-link-delegation_2024-07-05-06-32.json b/common/changes/@snowplow/browser-plugin-link-click-tracking/PE-4138-link-delegation_2024-07-05-06-32.json new file mode 100644 index 000000000..405e8a5d5 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-link-click-tracking/PE-4138-link-delegation_2024-07-05-06-32.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-link-click-tracking", + "comment": "Update plugin to use global rather than per-element event listeners", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-link-click-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-link-click-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/browser-plugin-link-click-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..a9e777d09 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-link-click-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-link-click-tracking", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-link-click-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-link-click-tracking/shadow-clicks_2024-10-04-05-19.json b/common/changes/@snowplow/browser-plugin-link-click-tracking/shadow-clicks_2024-10-04-05-19.json new file mode 100644 index 000000000..82018a43f --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-link-click-tracking/shadow-clicks_2024-10-04-05-19.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-link-click-tracking", + "comment": "Detect link clicks within ShadowRoots", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-link-click-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-media-tracking/migrate-html5-plugin-to-new-media-plugin_2024-09-24-12-28.json b/common/changes/@snowplow/browser-plugin-media-tracking/migrate-html5-plugin-to-new-media-plugin_2024-09-24-12-28.json new file mode 100644 index 000000000..58cd5dda4 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-media-tracking/migrate-html5-plugin-to-new-media-plugin_2024-09-24-12-28.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-media-tracking", + "comment": "Migrate HTML Media Tracking to Snowplow Media Plugin", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-media-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-media/2023-08-16-09-05.json b/common/changes/@snowplow/browser-plugin-media/2023-08-16-09-05.json new file mode 100644 index 000000000..193d407ed --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-media/2023-08-16-09-05.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-media", + "comment": "Upgrade UUID to 8.3.2 (close #1138)", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-media" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-media/PE-5753-ytv4_2024-09-11-01-52.json b/common/changes/@snowplow/browser-plugin-media/PE-5753-ytv4_2024-09-11-01-52.json new file mode 100644 index 000000000..1aa75ab3c --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-media/PE-5753-ytv4_2024-09-11-01-52.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-media", + "comment": "Add support for context generator functions", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-media" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-optimizely/feature-revise-default-plugins_2023-02-06-13-02.json b/common/changes/@snowplow/browser-plugin-optimizely/feature-revise-default-plugins_2023-02-06-13-02.json new file mode 100644 index 000000000..738929318 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-optimizely/feature-revise-default-plugins_2023-02-06-13-02.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-optimizely", + "comment": "Add deprecation warning", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-optimizely" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-performance-navigation-timing/2023-08-16-09-05.json b/common/changes/@snowplow/browser-plugin-performance-navigation-timing/2023-08-16-09-05.json new file mode 100644 index 000000000..f3386fa4b --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-performance-navigation-timing/2023-08-16-09-05.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-performance-navigation-timing", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-performance-navigation-timing" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-performance-timing/issue-remove_deprecated_plugins_2024-09-25-10-05.json b/common/changes/@snowplow/browser-plugin-performance-timing/issue-remove_deprecated_plugins_2024-09-25-10-05.json new file mode 100644 index 000000000..e23d8ccc1 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-performance-timing/issue-remove_deprecated_plugins_2024-09-25-10-05.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-performance-timing", + "comment": "Deprecate performance-timing plugin", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-performance-timing" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-site-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/browser-plugin-site-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..c9f262ae2 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-site-tracking/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-site-tracking", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-site-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-snowplow-ecommerce/2023-02-06-11-05.json b/common/changes/@snowplow/browser-plugin-snowplow-ecommerce/2023-02-06-11-05.json new file mode 100644 index 000000000..cb4138a3b --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-snowplow-ecommerce/2023-02-06-11-05.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-snowplow-ecommerce", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-snowplow-ecommerce" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-snowplow-ecommerce/PE-4998-sdjtype_2024-07-17-01-28.json b/common/changes/@snowplow/browser-plugin-snowplow-ecommerce/PE-4998-sdjtype_2024-07-17-01-28.json new file mode 100644 index 000000000..cb4138a3b --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-snowplow-ecommerce/PE-4998-sdjtype_2024-07-17-01-28.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-snowplow-ecommerce", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-snowplow-ecommerce" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-snowplow-ecommerce/fix-sdj-regression_2024-10-04-03-07.json b/common/changes/@snowplow/browser-plugin-snowplow-ecommerce/fix-sdj-regression_2024-10-04-03-07.json new file mode 100644 index 000000000..cb4138a3b --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-snowplow-ecommerce/fix-sdj-regression_2024-10-04-03-07.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-snowplow-ecommerce", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-snowplow-ecommerce" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-plugin-youtube-tracking/PE-5753-ytv4_2024-09-11-01-52.json b/common/changes/@snowplow/browser-plugin-youtube-tracking/PE-5753-ytv4_2024-09-11-01-52.json new file mode 100644 index 000000000..8efcbd056 --- /dev/null +++ b/common/changes/@snowplow/browser-plugin-youtube-tracking/PE-5753-ytv4_2024-09-11-01-52.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-plugin-youtube-tracking", + "comment": "Migrate to v2 Media Tracking schemas", + "type": "none" + } + ], + "packageName": "@snowplow/browser-plugin-youtube-tracking" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker-core/discover-root-default_2024-10-04-03-51.json b/common/changes/@snowplow/browser-tracker-core/discover-root-default_2024-10-04-03-51.json new file mode 100644 index 000000000..63c1c035e --- /dev/null +++ b/common/changes/@snowplow/browser-tracker-core/discover-root-default_2024-10-04-03-51.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker-core", + "comment": "Re-allow unspecified cookies domains", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker-core/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/browser-tracker-core/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..c9766e06c --- /dev/null +++ b/common/changes/@snowplow/browser-tracker-core/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker-core", + "comment": "Upgrade UUID to 8.3.2 (close #1138)", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker-core/feature-v4-change-default-initialization-params-browser_2023-01-26-08-53.json b/common/changes/@snowplow/browser-tracker-core/feature-v4-change-default-initialization-params-browser_2023-01-26-08-53.json new file mode 100644 index 000000000..7b0868bdf --- /dev/null +++ b/common/changes/@snowplow/browser-tracker-core/feature-v4-change-default-initialization-params-browser_2023-01-26-08-53.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker-core", + "comment": "Change default tracker initialization params (discoverRootDomain, cookieSameSite)", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker-core/issue-cookie_optimization_2024-09-03-12-13.json b/common/changes/@snowplow/browser-tracker-core/issue-cookie_optimization_2024-09-03-12-13.json new file mode 100644 index 000000000..b56ad4f64 --- /dev/null +++ b/common/changes/@snowplow/browser-tracker-core/issue-cookie_optimization_2024-09-03-12-13.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker-core", + "comment": "Make cookie writes async by default to improve tracker performance (#1340)", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker-core/issue-fetch_api_2024-08-20-06-45.json b/common/changes/@snowplow/browser-tracker-core/issue-fetch_api_2024-08-20-06-45.json new file mode 100644 index 000000000..46e8df7aa --- /dev/null +++ b/common/changes/@snowplow/browser-tracker-core/issue-fetch_api_2024-08-20-06-45.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker-core", + "comment": "Add an emitter and event store interface in the tracker core to be used both by the browser and node trackers and use fetch for making requests", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker-core/issue-plugin_filter_2024-07-09-11-54.json b/common/changes/@snowplow/browser-tracker-core/issue-plugin_filter_2024-07-09-11-54.json new file mode 100644 index 000000000..927053c15 --- /dev/null +++ b/common/changes/@snowplow/browser-tracker-core/issue-plugin_filter_2024-07-09-11-54.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker-core", + "comment": "Add a filter function to plugins to filter out events so that they are not tracked (#1326)", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker-core/ostz_default_2024-08-30-04-14.json b/common/changes/@snowplow/browser-tracker-core/ostz_default_2024-08-30-04-14.json new file mode 100644 index 000000000..9de6e47f1 --- /dev/null +++ b/common/changes/@snowplow/browser-tracker-core/ostz_default_2024-08-30-04-14.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker-core", + "comment": "Add browser-tracker-core default for os_timezone", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker/PE-5311-namedctx_2024-07-17-02-36.json b/common/changes/@snowplow/browser-tracker/PE-5311-namedctx_2024-07-17-02-36.json new file mode 100644 index 000000000..70c4488da --- /dev/null +++ b/common/changes/@snowplow/browser-tracker/PE-5311-namedctx_2024-07-17-02-36.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker", + "comment": "Add support for named global context", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/browser-tracker/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..5665ac7f0 --- /dev/null +++ b/common/changes/@snowplow/browser-tracker/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker/fix-1167-correct-newTracker-function-on-browser-tracker_2023-04-17-07-28.json b/common/changes/@snowplow/browser-tracker/fix-1167-correct-newTracker-function-on-browser-tracker_2023-04-17-07-28.json new file mode 100644 index 000000000..151e2bcfb --- /dev/null +++ b/common/changes/@snowplow/browser-tracker/fix-1167-correct-newTracker-function-on-browser-tracker_2023-04-17-07-28.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker", + "comment": "Fix newTracker typing to accurately return null or undefined (fix #1167)", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker/fix-remove-newTracker-faulty-typing_2023-09-13-09-52.json b/common/changes/@snowplow/browser-tracker/fix-remove-newTracker-faulty-typing_2023-09-13-09-52.json new file mode 100644 index 000000000..c1b673840 --- /dev/null +++ b/common/changes/@snowplow/browser-tracker/fix-remove-newTracker-faulty-typing_2023-09-13-09-52.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker", + "comment": "Fix newTracker typing for better understanding of arguments", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker/issue-cookie_optimization_2024-09-03-12-13.json b/common/changes/@snowplow/browser-tracker/issue-cookie_optimization_2024-09-03-12-13.json new file mode 100644 index 000000000..8e2bf89d1 --- /dev/null +++ b/common/changes/@snowplow/browser-tracker/issue-cookie_optimization_2024-09-03-12-13.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker", + "comment": "Make cookie writes async by default to improve tracker performance (#1340)", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker/issue-fetch_api_2024-08-20-06-45.json b/common/changes/@snowplow/browser-tracker/issue-fetch_api_2024-08-20-06-45.json new file mode 100644 index 000000000..67f92be6b --- /dev/null +++ b/common/changes/@snowplow/browser-tracker/issue-fetch_api_2024-08-20-06-45.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker", + "comment": "Add an emitter and event store interface in the tracker core to be used both by the browser and node trackers and use fetch for making requests", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/browser-tracker/issue-plugin_filter_2024-07-09-11-54.json b/common/changes/@snowplow/browser-tracker/issue-plugin_filter_2024-07-09-11-54.json new file mode 100644 index 000000000..035e343b1 --- /dev/null +++ b/common/changes/@snowplow/browser-tracker/issue-plugin_filter_2024-07-09-11-54.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/browser-tracker", + "comment": "Add a filter function to plugins to filter out events so that they are not tracked (#1326)", + "type": "none" + } + ], + "packageName": "@snowplow/browser-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/javascript-tracker/PE-5753-ytv4_2024-09-11-01-52.json b/common/changes/@snowplow/javascript-tracker/PE-5753-ytv4_2024-09-11-01-52.json new file mode 100644 index 000000000..7c9a6fc9d --- /dev/null +++ b/common/changes/@snowplow/javascript-tracker/PE-5753-ytv4_2024-09-11-01-52.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/javascript-tracker", + "comment": "Adjust e2e tests for v2 Media support in YouTube plugin", + "type": "none" + } + ], + "packageName": "@snowplow/javascript-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/javascript-tracker/PE-6528-formsv4_2024-07-17-01-15.json b/common/changes/@snowplow/javascript-tracker/PE-6528-formsv4_2024-07-17-01-15.json new file mode 100644 index 000000000..ae2f53795 --- /dev/null +++ b/common/changes/@snowplow/javascript-tracker/PE-6528-formsv4_2024-07-17-01-15.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/javascript-tracker", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/javascript-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/javascript-tracker/feature-revise-default-plugins_2023-02-06-08-43.json b/common/changes/@snowplow/javascript-tracker/feature-revise-default-plugins_2023-02-06-08-43.json new file mode 100644 index 000000000..30e44e68e --- /dev/null +++ b/common/changes/@snowplow/javascript-tracker/feature-revise-default-plugins_2023-02-06-08-43.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/javascript-tracker", + "comment": "Revise default plugins", + "type": "none" + } + ], + "packageName": "@snowplow/javascript-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/javascript-tracker/feature-v4-change-default-initialization-params-browser_2023-01-26-08-53.json b/common/changes/@snowplow/javascript-tracker/feature-v4-change-default-initialization-params-browser_2023-01-26-08-53.json new file mode 100644 index 000000000..ae2f53795 --- /dev/null +++ b/common/changes/@snowplow/javascript-tracker/feature-v4-change-default-initialization-params-browser_2023-01-26-08-53.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/javascript-tracker", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/javascript-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/javascript-tracker/issue-cookie_optimization_2024-09-03-12-13.json b/common/changes/@snowplow/javascript-tracker/issue-cookie_optimization_2024-09-03-12-13.json new file mode 100644 index 000000000..c8a4588c9 --- /dev/null +++ b/common/changes/@snowplow/javascript-tracker/issue-cookie_optimization_2024-09-03-12-13.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/javascript-tracker", + "comment": "Make cookie writes async by default to improve tracker performance (#1340)", + "type": "none" + } + ], + "packageName": "@snowplow/javascript-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/javascript-tracker/issue-fetch_api_2024-08-20-06-45.json b/common/changes/@snowplow/javascript-tracker/issue-fetch_api_2024-08-20-06-45.json new file mode 100644 index 000000000..992f38099 --- /dev/null +++ b/common/changes/@snowplow/javascript-tracker/issue-fetch_api_2024-08-20-06-45.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/javascript-tracker", + "comment": "Add an emitter and event store interface in the tracker core to be used both by the browser and node trackers and use fetch for making requests", + "type": "none" + } + ], + "packageName": "@snowplow/javascript-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/javascript-tracker/issue-plugin_filter_2024-07-09-11-54.json b/common/changes/@snowplow/javascript-tracker/issue-plugin_filter_2024-07-09-11-54.json new file mode 100644 index 000000000..3ef2242ed --- /dev/null +++ b/common/changes/@snowplow/javascript-tracker/issue-plugin_filter_2024-07-09-11-54.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/javascript-tracker", + "comment": "Add a filter function to plugins to filter out events so that they are not tracked (#1326)", + "type": "none" + } + ], + "packageName": "@snowplow/javascript-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/javascript-tracker/shadow-clicks_2024-10-08-04-36.json b/common/changes/@snowplow/javascript-tracker/shadow-clicks_2024-10-08-04-36.json new file mode 100644 index 000000000..ae2f53795 --- /dev/null +++ b/common/changes/@snowplow/javascript-tracker/shadow-clicks_2024-10-08-04-36.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/javascript-tracker", + "comment": "", + "type": "none" + } + ], + "packageName": "@snowplow/javascript-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/node-tracker/feature-1129-update-supported-node-v14_2022-11-29-08-48.json b/common/changes/@snowplow/node-tracker/feature-1129-update-supported-node-v14_2022-11-29-08-48.json new file mode 100644 index 000000000..7d68cc9b4 --- /dev/null +++ b/common/changes/@snowplow/node-tracker/feature-1129-update-supported-node-v14_2022-11-29-08-48.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/node-tracker", + "comment": "Update Node.js to v14 (closes #1129)", + "type": "none" + } + ], + "packageName": "@snowplow/node-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/node-tracker/feature-revise-nodejs-initialization_2023-01-30-17-24.json b/common/changes/@snowplow/node-tracker/feature-revise-nodejs-initialization_2023-01-30-17-24.json new file mode 100644 index 000000000..47e16db1c --- /dev/null +++ b/common/changes/@snowplow/node-tracker/feature-revise-nodejs-initialization_2023-01-30-17-24.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/node-tracker", + "comment": "Revise Node.js initialization API & upgrade got to @12", + "type": "none" + } + ], + "packageName": "@snowplow/node-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/node-tracker/issue-fetch_api_2024-08-20-06-45.json b/common/changes/@snowplow/node-tracker/issue-fetch_api_2024-08-20-06-45.json new file mode 100644 index 000000000..ce1f4c264 --- /dev/null +++ b/common/changes/@snowplow/node-tracker/issue-fetch_api_2024-08-20-06-45.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/node-tracker", + "comment": "Add an emitter and event store interface in the tracker core to be used both by the browser and node trackers and use fetch for making requests", + "type": "none" + } + ], + "packageName": "@snowplow/node-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/node-tracker/issue-node_tracker_defaults_2024-10-15-06-13.json b/common/changes/@snowplow/node-tracker/issue-node_tracker_defaults_2024-10-15-06-13.json new file mode 100644 index 000000000..b450c3972 --- /dev/null +++ b/common/changes/@snowplow/node-tracker/issue-node_tracker_defaults_2024-10-15-06-13.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/node-tracker", + "comment": "Make base64 encoding an optional parameter in Node newTracker call", + "type": "none" + } + ], + "packageName": "@snowplow/node-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/node-tracker/issue-plugin_filter_2024-07-09-11-54.json b/common/changes/@snowplow/node-tracker/issue-plugin_filter_2024-07-09-11-54.json new file mode 100644 index 000000000..53f8e7c04 --- /dev/null +++ b/common/changes/@snowplow/node-tracker/issue-plugin_filter_2024-07-09-11-54.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/node-tracker", + "comment": "Add a filter function to plugins to filter out events so that they are not tracked (#1326)", + "type": "none" + } + ], + "packageName": "@snowplow/node-tracker" +} \ No newline at end of file diff --git a/common/changes/@snowplow/tracker-core/PE-4998-sdjtype_2024-07-17-01-28.json b/common/changes/@snowplow/tracker-core/PE-4998-sdjtype_2024-07-17-01-28.json new file mode 100644 index 000000000..8ddb6fc27 --- /dev/null +++ b/common/changes/@snowplow/tracker-core/PE-4998-sdjtype_2024-07-17-01-28.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/tracker-core", + "comment": "Fix SelfDescribingJson type to allow optional keys in type parameter", + "type": "none" + } + ], + "packageName": "@snowplow/tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/tracker-core/PE-5311-namedctx_2024-07-17-02-36.json b/common/changes/@snowplow/tracker-core/PE-5311-namedctx_2024-07-17-02-36.json new file mode 100644 index 000000000..dfc5b88b1 --- /dev/null +++ b/common/changes/@snowplow/tracker-core/PE-5311-namedctx_2024-07-17-02-36.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/tracker-core", + "comment": "Add support for named global context", + "type": "none" + } + ], + "packageName": "@snowplow/tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/tracker-core/feature-1138-upgrade-uuid_2023-01-10-10-11.json b/common/changes/@snowplow/tracker-core/feature-1138-upgrade-uuid_2023-01-10-10-11.json new file mode 100644 index 000000000..a41e9dd3f --- /dev/null +++ b/common/changes/@snowplow/tracker-core/feature-1138-upgrade-uuid_2023-01-10-10-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/tracker-core", + "comment": "Upgrade UUID to 8.3.2 (close #1138)", + "type": "none" + } + ], + "packageName": "@snowplow/tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/tracker-core/feature-revise-nodejs-initialization_2023-01-31-10-50.json b/common/changes/@snowplow/tracker-core/feature-revise-nodejs-initialization_2023-01-31-10-50.json new file mode 100644 index 000000000..3d3ff5a8c --- /dev/null +++ b/common/changes/@snowplow/tracker-core/feature-revise-nodejs-initialization_2023-01-31-10-50.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/tracker-core", + "comment": "Upgrade ava to @5", + "type": "none" + } + ], + "packageName": "@snowplow/tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/tracker-core/fix-sdj-regression_2024-10-04-03-07.json b/common/changes/@snowplow/tracker-core/fix-sdj-regression_2024-10-04-03-07.json new file mode 100644 index 000000000..6d6fd6aef --- /dev/null +++ b/common/changes/@snowplow/tracker-core/fix-sdj-regression_2024-10-04-03-07.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/tracker-core", + "comment": "Fix regression in SelfDescribingJson type from #1330", + "type": "none" + } + ], + "packageName": "@snowplow/tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/tracker-core/issue-fetch_api_2024-08-20-06-45.json b/common/changes/@snowplow/tracker-core/issue-fetch_api_2024-08-20-06-45.json new file mode 100644 index 000000000..d43d48616 --- /dev/null +++ b/common/changes/@snowplow/tracker-core/issue-fetch_api_2024-08-20-06-45.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/tracker-core", + "comment": "Add an emitter and event store interface in the tracker core to be used both by the browser and node trackers and use fetch for making requests", + "type": "none" + } + ], + "packageName": "@snowplow/tracker-core" +} \ No newline at end of file diff --git a/common/changes/@snowplow/tracker-core/issue-plugin_filter_2024-07-09-11-54.json b/common/changes/@snowplow/tracker-core/issue-plugin_filter_2024-07-09-11-54.json new file mode 100644 index 000000000..28b4a64ac --- /dev/null +++ b/common/changes/@snowplow/tracker-core/issue-plugin_filter_2024-07-09-11-54.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@snowplow/tracker-core", + "comment": "Add a filter function to plugins to filter out events so that they are not tracked (#1326)", + "type": "none" + } + ], + "packageName": "@snowplow/tracker-core" +} \ No newline at end of file diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 86f6c70a5..60f7e3426 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -102,6 +102,10 @@ "name": "@snowplow/browser-plugin-optimizely-x", "allowedCategories": [ "trackers" ] }, + { + "name": "@snowplow/browser-plugin-performance-navigation-timing", + "allowedCategories": [ "trackers" ] + }, { "name": "@snowplow/browser-plugin-performance-timing", "allowedCategories": [ "trackers" ] @@ -126,6 +130,10 @@ "name": "@snowplow/browser-plugin-vimeo-tracking", "allowedCategories": [ "trackers" ] }, + { + "name": "@snowplow/browser-plugin-web-vitals", + "allowedCategories": [ "trackers" ] + }, { "name": "@snowplow/browser-plugin-youtube-tracking", "allowedCategories": [ "trackers" ] @@ -374,6 +382,10 @@ "name": "tslib", "allowedCategories": [ "libraries", "plugins", "trackers" ] }, + { + "name": "tsx", + "allowedCategories": [ "trackers" ] + }, { "name": "typescript", "allowedCategories": [ "libraries", "plugins", "trackers" ] @@ -401,6 +413,10 @@ { "name": "webdriverio", "allowedCategories": [ "trackers" ] + }, + { + "name": "whatwg-fetch", + "allowedCategories": [ "libraries", "trackers" ] } ] } diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 86aaccb2f..e7b0a4c7f 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -22,8 +22,8 @@ importers: specifier: ^2.3.1 version: 2.7.0 uuid: - specifier: ^3.4.0 - version: 3.4.0 + specifier: ^10.0.0 + version: 10.0.0 devDependencies: '@ampproject/rollup-plugin-closure-compiler': specifier: ~0.27.0 @@ -47,8 +47,8 @@ importers: specifier: ~1.1.3 version: 1.1.5 '@types/uuid': - specifier: ~3.4.6 - version: 3.4.13 + specifier: ^10.0.0 + version: 10.0.0 '@typescript-eslint/eslint-plugin': specifier: ~5.15.0 version: 5.15.0(@typescript-eslint/parser@5.15.0(eslint@8.11.0)(typescript@4.6.4))(eslint@8.11.0)(typescript@4.6.4) @@ -91,6 +91,9 @@ importers: typescript: specifier: ~4.6.2 version: 4.6.4 + whatwg-fetch: + specifier: ~3.6.20 + version: 3.6.20 ../../libraries/tracker-core: dependencies: @@ -98,8 +101,8 @@ importers: specifier: ^2.3.1 version: 2.7.0 uuid: - specifier: ^3.4.0 - version: 3.4.0 + specifier: ^10.0.0 + version: 10.0.0 devDependencies: '@ampproject/rollup-plugin-closure-compiler': specifier: ~0.27.0 @@ -117,8 +120,8 @@ importers: specifier: ~14.6.0 version: 14.6.4 '@types/uuid': - specifier: ~3.4.6 - version: 3.4.13 + specifier: ^10.0.0 + version: 10.0.0 '@typescript-eslint/eslint-plugin': specifier: ~5.15.0 version: 5.15.0(@typescript-eslint/parser@5.15.0(eslint@8.11.0)(typescript@4.6.4))(eslint@8.11.0)(typescript@4.6.4) @@ -126,8 +129,8 @@ importers: specifier: ~5.15.0 version: 5.15.0(eslint@8.11.0)(typescript@4.6.4) ava: - specifier: ~4.1.0 - version: 4.1.0 + specifier: ~5.1.1 + version: 5.1.1 eslint: specifier: ~8.11.0 version: 8.11.0 @@ -232,76 +235,6 @@ importers: specifier: ~4.6.2 version: 4.6.4 - ../../plugins/browser-plugin-browser-features: - dependencies: - '@snowplow/browser-tracker-core': - specifier: workspace:* - version: link:../../libraries/browser-tracker-core - tslib: - specifier: ^2.3.1 - version: 2.7.0 - devDependencies: - '@ampproject/rollup-plugin-closure-compiler': - specifier: ~0.27.0 - version: 0.27.0(rollup@2.70.2) - '@rollup/plugin-commonjs': - specifier: ~21.0.2 - version: 21.0.3(rollup@2.70.2) - '@rollup/plugin-node-resolve': - specifier: ~13.1.3 - version: 13.1.3(rollup@2.70.2) - '@snowplow/tracker-core': - specifier: workspace:* - version: link:../../libraries/tracker-core - '@types/jest': - specifier: ~27.4.1 - version: 27.4.1 - '@types/jsdom': - specifier: ~16.2.14 - version: 16.2.15 - '@typescript-eslint/eslint-plugin': - specifier: ~5.15.0 - version: 5.15.0(@typescript-eslint/parser@5.15.0(eslint@8.11.0)(typescript@4.6.4))(eslint@8.11.0)(typescript@4.6.4) - '@typescript-eslint/parser': - specifier: ~5.15.0 - version: 5.15.0(eslint@8.11.0)(typescript@4.6.4) - eslint: - specifier: ~8.11.0 - version: 8.11.0 - jest: - specifier: ~27.5.1 - version: 27.5.1(ts-node@10.9.2(@types/node@20.16.3)(typescript@4.6.4)) - jest-environment-jsdom: - specifier: ~27.5.1 - version: 27.5.1 - jest-environment-jsdom-global: - specifier: ~3.0.0 - version: 3.0.0(jest-environment-jsdom@27.5.1) - jest-standard-reporter: - specifier: ~2.0.0 - version: 2.0.0 - rollup: - specifier: ~2.70.1 - version: 2.70.2 - rollup-plugin-cleanup: - specifier: ~3.2.1 - version: 3.2.1(rollup@2.70.2) - rollup-plugin-license: - specifier: ~2.6.1 - version: 2.6.1(rollup@2.70.2) - rollup-plugin-terser: - specifier: ~7.0.2 - version: 7.0.2(rollup@2.70.2) - rollup-plugin-ts: - specifier: ~2.0.5 - version: 2.0.7(@babel/core@7.25.2)(@babel/runtime@7.25.6)(rollup@2.70.2)(typescript@4.6.4) - ts-jest: - specifier: ~27.1.3 - version: 27.1.5(@babel/core@7.25.2)(@types/jest@27.4.1)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.2(@types/node@20.16.3)(typescript@4.6.4)))(typescript@4.6.4) - typescript: - specifier: ~4.6.2 - version: 4.6.4 - ../../plugins/browser-plugin-button-click-tracking: dependencies: '@snowplow/browser-tracker-core': @@ -448,82 +381,6 @@ importers: specifier: ~4.6.2 version: 4.6.4 - ../../plugins/browser-plugin-consent: - dependencies: - '@snowplow/browser-tracker-core': - specifier: workspace:* - version: link:../../libraries/browser-tracker-core - '@snowplow/tracker-core': - specifier: workspace:* - version: link:../../libraries/tracker-core - tslib: - specifier: ^2.3.1 - version: 2.7.0 - devDependencies: - '@ampproject/rollup-plugin-closure-compiler': - specifier: ~0.27.0 - version: 0.27.0(rollup@2.70.2) - '@rollup/plugin-commonjs': - specifier: ~21.0.2 - version: 21.0.3(rollup@2.70.2) - '@rollup/plugin-node-resolve': - specifier: ~13.1.3 - version: 13.1.3(rollup@2.70.2) - '@types/jest': - specifier: ~27.4.1 - version: 27.4.1 - '@types/jsdom': - specifier: ~16.2.14 - version: 16.2.15 - '@types/lodash': - specifier: ~4.14.180 - version: 4.14.202 - '@typescript-eslint/eslint-plugin': - specifier: ~5.15.0 - version: 5.15.0(@typescript-eslint/parser@5.15.0(eslint@8.11.0)(typescript@4.6.4))(eslint@8.11.0)(typescript@4.6.4) - '@typescript-eslint/parser': - specifier: ~5.15.0 - version: 5.15.0(eslint@8.11.0)(typescript@4.6.4) - eslint: - specifier: ~8.11.0 - version: 8.11.0 - jest: - specifier: ~27.5.1 - version: 27.5.1(ts-node@10.9.2(@types/node@20.16.3)(typescript@4.6.4)) - jest-environment-jsdom: - specifier: ~27.5.1 - version: 27.5.1 - jest-environment-jsdom-global: - specifier: ~3.0.0 - version: 3.0.0(jest-environment-jsdom@27.5.1) - jest-standard-reporter: - specifier: ~2.0.0 - version: 2.0.0 - lodash: - specifier: ~4.17.21 - version: 4.17.21 - rollup: - specifier: ~2.70.1 - version: 2.70.2 - rollup-plugin-cleanup: - specifier: ~3.2.1 - version: 3.2.1(rollup@2.70.2) - rollup-plugin-license: - specifier: ~2.6.1 - version: 2.6.1(rollup@2.70.2) - rollup-plugin-terser: - specifier: ~7.0.2 - version: 7.0.2(rollup@2.70.2) - rollup-plugin-ts: - specifier: ~2.0.5 - version: 2.0.7(@babel/core@7.25.2)(@babel/runtime@7.25.6)(rollup@2.70.2)(typescript@4.6.4) - ts-jest: - specifier: ~27.1.3 - version: 27.1.5(@babel/core@7.25.2)(@types/jest@27.4.1)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.2(@types/node@20.16.3)(typescript@4.6.4)))(typescript@4.6.4) - typescript: - specifier: ~4.6.2 - version: 4.6.4 - ../../plugins/browser-plugin-debugger: dependencies: '@snowplow/browser-tracker-core': @@ -600,82 +457,6 @@ importers: specifier: ~4.6.2 version: 4.6.4 - ../../plugins/browser-plugin-ecommerce: - dependencies: - '@snowplow/browser-tracker-core': - specifier: workspace:* - version: link:../../libraries/browser-tracker-core - '@snowplow/tracker-core': - specifier: workspace:* - version: link:../../libraries/tracker-core - tslib: - specifier: ^2.3.1 - version: 2.7.0 - devDependencies: - '@ampproject/rollup-plugin-closure-compiler': - specifier: ~0.27.0 - version: 0.27.0(rollup@2.70.2) - '@rollup/plugin-commonjs': - specifier: ~21.0.2 - version: 21.0.3(rollup@2.70.2) - '@rollup/plugin-node-resolve': - specifier: ~13.1.3 - version: 13.1.3(rollup@2.70.2) - '@types/jest': - specifier: ~27.4.1 - version: 27.4.1 - '@types/jsdom': - specifier: ~16.2.14 - version: 16.2.15 - '@types/lodash': - specifier: ~4.14.180 - version: 4.14.202 - '@typescript-eslint/eslint-plugin': - specifier: ~5.15.0 - version: 5.15.0(@typescript-eslint/parser@5.15.0(eslint@8.11.0)(typescript@4.6.4))(eslint@8.11.0)(typescript@4.6.4) - '@typescript-eslint/parser': - specifier: ~5.15.0 - version: 5.15.0(eslint@8.11.0)(typescript@4.6.4) - eslint: - specifier: ~8.11.0 - version: 8.11.0 - jest: - specifier: ~27.5.1 - version: 27.5.1(ts-node@10.9.2(@types/node@20.16.3)(typescript@4.6.4)) - jest-environment-jsdom: - specifier: ~27.5.1 - version: 27.5.1 - jest-environment-jsdom-global: - specifier: ~3.0.0 - version: 3.0.0(jest-environment-jsdom@27.5.1) - jest-standard-reporter: - specifier: ~2.0.0 - version: 2.0.0 - lodash: - specifier: ~4.17.21 - version: 4.17.21 - rollup: - specifier: ~2.70.1 - version: 2.70.2 - rollup-plugin-cleanup: - specifier: ~3.2.1 - version: 3.2.1(rollup@2.70.2) - rollup-plugin-license: - specifier: ~2.6.1 - version: 2.6.1(rollup@2.70.2) - rollup-plugin-terser: - specifier: ~7.0.2 - version: 7.0.2(rollup@2.70.2) - rollup-plugin-ts: - specifier: ~2.0.5 - version: 2.0.7(@babel/core@7.25.2)(@babel/runtime@7.25.6)(rollup@2.70.2)(typescript@4.6.4) - ts-jest: - specifier: ~27.1.3 - version: 27.1.5(@babel/core@7.25.2)(@types/jest@27.4.1)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.2(@types/node@20.16.3)(typescript@4.6.4)))(typescript@4.6.4) - typescript: - specifier: ~4.6.2 - version: 4.6.4 - ../../plugins/browser-plugin-enhanced-consent: dependencies: '@snowplow/browser-tracker-core': @@ -1342,8 +1123,8 @@ importers: specifier: ^2.3.1 version: 2.7.0 uuid: - specifier: ^3.4.0 - version: 3.4.0 + specifier: ^10.0.0 + version: 10.0.0 devDependencies: '@ampproject/rollup-plugin-closure-compiler': specifier: ~0.27.0 @@ -1361,8 +1142,8 @@ importers: specifier: ~16.2.14 version: 16.2.15 '@types/uuid': - specifier: ~3.4.6 - version: 3.4.13 + specifier: ^10.0.0 + version: 10.0.0 '@typescript-eslint/eslint-plugin': specifier: ~5.15.0 version: 5.15.0(@typescript-eslint/parser@5.15.0(eslint@8.11.0)(typescript@4.6.4))(eslint@8.11.0)(typescript@4.6.4) @@ -1408,76 +1189,9 @@ importers: ../../plugins/browser-plugin-media-tracking: dependencies: - '@snowplow/browser-tracker-core': - specifier: workspace:* - version: link:../../libraries/browser-tracker-core - '@snowplow/tracker-core': + '@snowplow/browser-plugin-media': specifier: workspace:* - version: link:../../libraries/tracker-core - tslib: - specifier: ^2.3.1 - version: 2.7.0 - devDependencies: - '@ampproject/rollup-plugin-closure-compiler': - specifier: ~0.27.0 - version: 0.27.0(rollup@2.70.2) - '@rollup/plugin-commonjs': - specifier: ~21.0.2 - version: 21.0.3(rollup@2.70.2) - '@rollup/plugin-node-resolve': - specifier: ~13.1.3 - version: 13.1.3(rollup@2.70.2) - '@types/jest': - specifier: ~27.4.1 - version: 27.4.1 - '@types/jsdom': - specifier: ~16.2.14 - version: 16.2.15 - '@typescript-eslint/eslint-plugin': - specifier: ~5.15.0 - version: 5.15.0(@typescript-eslint/parser@5.15.0(eslint@8.11.0)(typescript@4.6.4))(eslint@8.11.0)(typescript@4.6.4) - '@typescript-eslint/parser': - specifier: ~5.15.0 - version: 5.15.0(eslint@8.11.0)(typescript@4.6.4) - eslint: - specifier: ~8.11.0 - version: 8.11.0 - jest: - specifier: ~27.5.1 - version: 27.5.1(ts-node@10.9.2(@types/node@20.16.3)(typescript@4.6.4)) - jest-environment-jsdom: - specifier: ~27.5.1 - version: 27.5.1 - jest-environment-jsdom-global: - specifier: ~3.0.0 - version: 3.0.0(jest-environment-jsdom@27.5.1) - jest-standard-reporter: - specifier: ~2.0.0 - version: 2.0.0 - rollup: - specifier: ~2.70.1 - version: 2.70.2 - rollup-plugin-cleanup: - specifier: ~3.2.1 - version: 3.2.1(rollup@2.70.2) - rollup-plugin-license: - specifier: ~2.6.1 - version: 2.6.1(rollup@2.70.2) - rollup-plugin-terser: - specifier: ~7.0.2 - version: 7.0.2(rollup@2.70.2) - rollup-plugin-ts: - specifier: ~2.0.5 - version: 2.0.7(@babel/core@7.25.2)(@babel/runtime@7.25.6)(rollup@2.70.2)(typescript@4.6.4) - ts-jest: - specifier: ~27.1.3 - version: 27.1.5(@babel/core@7.25.2)(@types/jest@27.4.1)(babel-jest@27.5.1(@babel/core@7.25.2))(jest@27.5.1(ts-node@10.9.2(@types/node@20.16.3)(typescript@4.6.4)))(typescript@4.6.4) - typescript: - specifier: ~4.6.2 - version: 4.6.4 - - ../../plugins/browser-plugin-optimizely: - dependencies: + version: link:../browser-plugin-media '@snowplow/browser-tracker-core': specifier: workspace:* version: link:../../libraries/browser-tracker-core @@ -1487,6 +1201,9 @@ importers: tslib: specifier: ^2.3.1 version: 2.7.0 + uuid: + specifier: ^10.0.0 + version: 10.0.0 devDependencies: '@ampproject/rollup-plugin-closure-compiler': specifier: ~0.27.0 @@ -1503,6 +1220,9 @@ importers: '@types/jsdom': specifier: ~16.2.14 version: 16.2.15 + '@types/uuid': + specifier: ^10.0.0 + version: 10.0.0 '@typescript-eslint/eslint-plugin': specifier: ~5.15.0 version: 5.15.0(@typescript-eslint/parser@5.15.0(eslint@8.11.0)(typescript@4.6.4))(eslint@8.11.0)(typescript@4.6.4) @@ -2211,6 +1931,9 @@ importers: ../../plugins/browser-plugin-youtube-tracking: dependencies: + '@snowplow/browser-plugin-media': + specifier: workspace:* + version: link:../browser-plugin-media '@snowplow/browser-tracker-core': specifier: workspace:* version: link:../../libraries/browser-tracker-core @@ -2220,6 +1943,9 @@ importers: tslib: specifier: ^2.3.1 version: 2.7.0 + uuid: + specifier: ^10.0.0 + version: 10.0.0 devDependencies: '@ampproject/rollup-plugin-closure-compiler': specifier: ~0.27.0 @@ -2236,6 +1962,9 @@ importers: '@types/jsdom': specifier: ~16.2.14 version: 16.2.15 + '@types/uuid': + specifier: ^10.0.0 + version: 10.0.0 '@types/youtube': specifier: ~0.0.46 version: 0.0.50 @@ -2357,27 +2086,21 @@ importers: typescript: specifier: ~4.6.2 version: 4.6.4 + whatwg-fetch: + specifier: ~3.6.20 + version: 3.6.20 ../../trackers/javascript-tracker: dependencies: '@snowplow/browser-plugin-ad-tracking': specifier: workspace:* version: link:../../plugins/browser-plugin-ad-tracking - '@snowplow/browser-plugin-browser-features': - specifier: workspace:* - version: link:../../plugins/browser-plugin-browser-features '@snowplow/browser-plugin-button-click-tracking': specifier: workspace:* version: link:../../plugins/browser-plugin-button-click-tracking '@snowplow/browser-plugin-client-hints': specifier: workspace:* version: link:../../plugins/browser-plugin-client-hints - '@snowplow/browser-plugin-consent': - specifier: workspace:* - version: link:../../plugins/browser-plugin-consent - '@snowplow/browser-plugin-ecommerce': - specifier: workspace:* - version: link:../../plugins/browser-plugin-ecommerce '@snowplow/browser-plugin-enhanced-consent': specifier: workspace:* version: link:../../plugins/browser-plugin-enhanced-consent @@ -2408,12 +2131,12 @@ importers: '@snowplow/browser-plugin-media-tracking': specifier: workspace:* version: link:../../plugins/browser-plugin-media-tracking - '@snowplow/browser-plugin-optimizely': - specifier: workspace:* - version: link:../../plugins/browser-plugin-optimizely '@snowplow/browser-plugin-optimizely-x': specifier: workspace:* version: link:../../plugins/browser-plugin-optimizely-x + '@snowplow/browser-plugin-performance-navigation-timing': + specifier: workspace:* + version: link:../../plugins/browser-plugin-performance-navigation-timing '@snowplow/browser-plugin-performance-timing': specifier: workspace:* version: link:../../plugins/browser-plugin-performance-timing @@ -2432,6 +2155,9 @@ importers: '@snowplow/browser-plugin-vimeo-tracking': specifier: workspace:* version: link:../../plugins/browser-plugin-vimeo-tracking + '@snowplow/browser-plugin-web-vitals': + specifier: workspace:* + version: link:../../plugins/browser-plugin-web-vitals '@snowplow/browser-plugin-youtube-tracking': specifier: workspace:* version: link:../../plugins/browser-plugin-youtube-tracking @@ -2515,8 +2241,8 @@ importers: specifier: 4.1.2 version: 4.1.2 chromedriver: - specifier: ~126.0.4 - version: 126.0.5 + specifier: ~129.0.0 + version: 129.0.4 dockerode: specifier: ~3.3.1 version: 3.3.5 @@ -2573,7 +2299,7 @@ importers: version: 4.6.4 wdio-chromedriver-service: specifier: ~8.1.1 - version: 8.1.1(@wdio/types@8.39.0)(chromedriver@126.0.5)(webdriverio@8.39.1(encoding@0.1.13)(typescript@4.6.4)) + version: 8.1.1(@wdio/types@8.39.0)(chromedriver@129.0.4)(webdriverio@8.39.1(encoding@0.1.13)(typescript@4.6.4)) wdio-edgedriver-service: specifier: ~3.0.3 version: 3.0.3(@wdio/types@8.39.0) @@ -2589,9 +2315,6 @@ importers: '@snowplow/tracker-core': specifier: workspace:* version: link:../../libraries/tracker-core - got: - specifier: ^11.8.5 - version: 11.8.6 tslib: specifier: ^2.3.1 version: 2.7.0 @@ -2602,9 +2325,6 @@ importers: '@types/node': specifier: ~14.6.0 version: 14.6.4 - '@types/sinon': - specifier: ~10.0.11 - version: 10.0.20 '@typescript-eslint/eslint-plugin': specifier: ~5.15.0 version: 5.15.0(@typescript-eslint/parser@5.15.0(eslint@8.11.0)(typescript@4.6.4))(eslint@8.11.0)(typescript@4.6.4) @@ -2612,17 +2332,14 @@ importers: specifier: ~5.15.0 version: 5.15.0(eslint@8.11.0)(typescript@4.6.4) ava: - specifier: ~4.1.0 - version: 4.1.0 + specifier: ~5.1.1 + version: 5.1.1 eslint: specifier: ~8.11.0 version: 8.11.0 eslint-plugin-ava: specifier: ~13.2.0 version: 13.2.0(eslint@8.11.0) - nock: - specifier: ~13.2.4 - version: 13.2.9 rollup: specifier: ~2.70.1 version: 2.70.2 @@ -2632,9 +2349,6 @@ importers: rollup-plugin-ts: specifier: ~2.0.5 version: 2.0.7(@babel/core@7.25.2)(@babel/runtime@7.25.6)(rollup@2.70.2)(typescript@4.6.4) - sinon: - specifier: ~13.0.1 - version: 13.0.2 ts-node: specifier: ~10.9.1 version: 10.9.2(@types/node@14.6.4)(typescript@4.6.4) @@ -3093,24 +2807,9 @@ packages: '@sinonjs/commons@1.8.6': resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} - '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - - '@sinonjs/fake-timers@11.3.1': - resolution: {integrity: sha512-EVJO7nW5M/F5Tur0Rf2z/QoMo+1Ia963RiMtapiQrEWvY0iBUvADo8Beegwjpnle5BHkyHuoxSTW3jF43H1XRA==} - '@sinonjs/fake-timers@8.1.0': resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==} - '@sinonjs/fake-timers@9.1.2': - resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} - - '@sinonjs/samsam@6.1.3': - resolution: {integrity: sha512-nhOb2dWPeb1sd3IQXL/dVPnKHDOAFfvichtBf4xV00/rU1QbPCQqKMbvIheIjqwVjh7qIgf2AHTHi391yMOMpQ==} - - '@sinonjs/text-encoding@0.7.3': - resolution: {integrity: sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==} - '@szmarczak/http-timer@4.0.6': resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} @@ -3247,12 +2946,6 @@ packages: '@types/sha1@1.1.5': resolution: {integrity: sha512-eE1PzjW7u2VfxI+bTsvjzjBfpwqvxSpgfUmnRNVY+PJU1NBsdGZlaO/qnVnPKHzzpgIl9YyBIxvrgBvt1mzt2A==} - '@types/sinon@10.0.20': - resolution: {integrity: sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==} - - '@types/sinonjs__fake-timers@8.1.5': - resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} - '@types/split2@4.2.3': resolution: {integrity: sha512-59OXIlfUsi2k++H6CHgUQKEb2HKRokUA39HY1i1dS8/AIcqVjtAAFdf8u+HxTWK/4FUHMJQlKSZ4I6irCBJ1Zw==} @@ -3271,8 +2964,8 @@ packages: '@types/ua-parser-js@0.7.39': resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==} - '@types/uuid@3.4.13': - resolution: {integrity: sha512-pAeZeUbLE4Z9Vi9wsWV2bYPTweEHeJJy0G4pEjOA/FSvy1Ad5U5Km8iDV6TKre1mjBiVNfAdVHKruP8bAh4Q5A==} + '@types/uuid@10.0.0': + resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} '@types/vimeo__player@2.16.3': resolution: {integrity: sha512-hsOe6CZFTNyfjRjQUrNHBF4LDmjvjcU2yQIPWp5AglKeGxt11JYGToQhKUPM876gBXggqR6rMQ0/sNI06ec2Rg==} @@ -3637,9 +3330,9 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - ava@4.1.0: - resolution: {integrity: sha512-QD6MBWHzagAwb9vxduXzVWx6Q77DUHLxvIebSY6+enL+Ri6KzSZYj0IBOFifA26wfpJPZnWKLUh3vwx1LyVh/g==} - engines: {node: '>=12.22 <13 || >=14.17 <15 || >=16.4 <17 || >=17'} + ava@5.1.1: + resolution: {integrity: sha512-od1CWgWVIKZSdEc1dhQWhbsd6KBs0EYjek7eqZNGPvy+NyC9Q1bXixcadlgOXwDG9aM0zLMQZwRXfe9gMb1LQQ==} + engines: {node: '>=14.19 <15 || >=16.15 <17 || >=18'} hasBin: true peerDependencies: '@ava/typescript': '*' @@ -3911,8 +3604,8 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} - chromedriver@126.0.5: - resolution: {integrity: sha512-xXVxwxd8CJ6yg2KEvFqLQi7V0RvF78xFnLB+xo9g9MoJNHMQccD7b4OWaxtKDy5RXrMgQ6Jb6vUN3SjTYXHLEQ==} + chromedriver@129.0.4: + resolution: {integrity: sha512-j5I55cQwodFJUaYa1tWUmj2ss9KcPRBWmUa5Qonq3X8kqv2ASPyTboFYb4YB/YLztkYTUUw2E43txXw0wYzT/A==} engines: {node: '>=18'} hasBin: true @@ -4278,9 +3971,9 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} - del@6.1.1: - resolution: {integrity: sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==} - engines: {node: '>=10'} + del@7.1.0: + resolution: {integrity: sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==} + engines: {node: '>=14.16'} delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} @@ -4392,14 +4085,14 @@ packages: electron-to-chromium@1.5.13: resolution: {integrity: sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==} - emittery@0.10.2: - resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} - engines: {node: '>=12'} - emittery@0.8.1: resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==} engines: {node: '>=10'} + emittery@1.0.3: + resolution: {integrity: sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA==} + engines: {node: '>=14.16'} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -4660,10 +4353,6 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} - figures@4.0.1: - resolution: {integrity: sha512-rElJwkA/xS04Vfg+CaZodpso7VqBknOYbzi6I76hI4X80RUjkSxO2oAyPmGbuXUppywjqndOrQDl817hDnI++w==} - engines: {node: '>=12'} - figures@5.0.0: resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} engines: {node: '>=14'} @@ -5255,13 +4944,13 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-path-cwd@2.2.0: - resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==} - engines: {node: '>=6'} + is-path-cwd@3.0.0: + resolution: {integrity: sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} @@ -5661,9 +5350,6 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - just-extend@6.2.0: - resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -5726,9 +5412,6 @@ packages: lodash.flattendeep@4.4.0: resolution: {integrity: sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==} - lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} @@ -6022,16 +5705,9 @@ packages: nice-try@1.0.5: resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - nise@5.1.9: - resolution: {integrity: sha512-qOnoujW4SV6e40dYxJOb3uvuoPHtmLzIk4TFo+j0jPJoC+5Z9xja5qH5JZobEPsa8+YYphMrOSwnrshEhG2qww==} - no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - nock@13.2.9: - resolution: {integrity: sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA==} - engines: {node: '>= 10.13'} - node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -6302,6 +5978,10 @@ packages: resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==} engines: {node: '>=6'} + parse-ms@3.0.0: + resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==} + engines: {node: '>=12'} + parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} @@ -6349,9 +6029,6 @@ packages: path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} - path-to-regexp@6.2.2: - resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} - path-type@3.0.0: resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} engines: {node: '>=4'} @@ -6439,6 +6116,10 @@ packages: resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==} engines: {node: '>=10'} + pretty-ms@8.0.0: + resolution: {integrity: sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==} + engines: {node: '>=14.16'} + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -6461,10 +6142,6 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - propagate@2.0.1: - resolution: {integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==} - engines: {node: '>= 8'} - proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -6859,10 +6536,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sinon@13.0.2: - resolution: {integrity: sha512-KvOrztAVqzSJWMDoxM4vM+GPys1df2VBoXm+YciyB/OLMamfS3VXh3oGh5WtrAGSzrgczNWFFY22oKb7Fi5eeA==} - deprecated: 16.1.1 - sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -7130,9 +6803,9 @@ packages: tcp-port-used@1.0.2: resolution: {integrity: sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==} - temp-dir@2.0.0: - resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} - engines: {node: '>=8'} + temp-dir@3.0.0: + resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} + engines: {node: '>=14.16'} terminal-link@2.1.1: resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} @@ -7279,10 +6952,6 @@ packages: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} - type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - type-fest@0.13.1: resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} engines: {node: '>=10'} @@ -7404,6 +7073,10 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} + hasBin: true + uuid@3.4.0: resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. @@ -7537,6 +7210,9 @@ packages: whatwg-encoding@1.0.5: resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==} + whatwg-fetch@3.6.20: + resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} + whatwg-mimetype@2.3.0: resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==} @@ -7601,9 +7277,9 @@ packages: write-file-atomic@3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} ws@7.5.10: resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} @@ -8385,30 +8061,10 @@ snapshots: dependencies: type-detect: 4.0.8 - '@sinonjs/commons@3.0.1': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@11.3.1': - dependencies: - '@sinonjs/commons': 3.0.1 - '@sinonjs/fake-timers@8.1.0': dependencies: '@sinonjs/commons': 1.8.6 - '@sinonjs/fake-timers@9.1.2': - dependencies: - '@sinonjs/commons': 1.8.6 - - '@sinonjs/samsam@6.1.3': - dependencies: - '@sinonjs/commons': 1.8.6 - lodash.get: 4.4.2 - type-detect: 4.1.0 - - '@sinonjs/text-encoding@0.7.3': {} - '@szmarczak/http-timer@4.0.6': dependencies: defer-to-connect: 2.0.1 @@ -8560,12 +8216,6 @@ snapshots: dependencies: '@types/node': 14.6.4 - '@types/sinon@10.0.20': - dependencies: - '@types/sinonjs__fake-timers': 8.1.5 - - '@types/sinonjs__fake-timers@8.1.5': {} - '@types/split2@4.2.3': dependencies: '@types/node': 14.6.4 @@ -8582,7 +8232,7 @@ snapshots: '@types/ua-parser-js@0.7.39': {} - '@types/uuid@3.4.13': {} + '@types/uuid@10.0.0': {} '@types/vimeo__player@2.16.3': {} @@ -9114,7 +8764,7 @@ snapshots: asynckit@0.4.0: {} - ava@4.1.0: + ava@5.1.1: dependencies: acorn: 8.12.1 acorn-walk: 8.3.3 @@ -9135,9 +8785,9 @@ snapshots: concordance: 5.0.4 currently-unhandled: 0.4.1 debug: 4.3.6 - del: 6.1.1 - emittery: 0.10.2 - figures: 4.0.1 + del: 7.1.0 + emittery: 1.0.3 + figures: 5.0.0 globby: 13.2.2 ignore-by-default: 2.1.0 indent-string: 5.0.0 @@ -9152,14 +8802,14 @@ snapshots: picomatch: 2.3.1 pkg-conf: 4.0.0 plur: 5.1.0 - pretty-ms: 7.0.1 + pretty-ms: 8.0.0 resolve-cwd: 3.0.0 slash: 3.0.0 stack-utils: 2.0.6 strip-ansi: 7.1.0 supertap: 3.0.1 - temp-dir: 2.0.0 - write-file-atomic: 4.0.2 + temp-dir: 3.0.0 + write-file-atomic: 5.0.1 yargs: 17.7.2 transitivePeerDependencies: - supports-color @@ -9535,7 +9185,7 @@ snapshots: chownr@2.0.0: {} - chromedriver@126.0.5: + chromedriver@129.0.4: dependencies: '@testim/chrome-version': 1.1.4 axios: 1.7.7 @@ -9894,16 +9544,16 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 - del@6.1.1: + del@7.1.0: dependencies: - globby: 11.1.0 + globby: 13.2.2 graceful-fs: 4.2.11 is-glob: 4.0.3 - is-path-cwd: 2.2.0 - is-path-inside: 3.0.3 - p-map: 4.0.0 + is-path-cwd: 3.0.0 + is-path-inside: 4.0.0 + p-map: 5.5.0 rimraf: 3.0.2 - slash: 3.0.0 + slash: 4.0.0 delayed-stream@1.0.0: {} @@ -10007,10 +9657,10 @@ snapshots: electron-to-chromium@1.5.13: {} - emittery@0.10.2: {} - emittery@0.8.1: {} + emittery@1.0.3: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -10408,11 +10058,6 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 - figures@4.0.1: - dependencies: - escape-string-regexp: 5.0.0 - is-unicode-supported: 1.3.0 - figures@5.0.0: dependencies: escape-string-regexp: 5.0.0 @@ -11048,9 +10693,9 @@ snapshots: is-number@7.0.0: {} - is-path-cwd@2.2.0: {} + is-path-cwd@3.0.0: {} - is-path-inside@3.0.3: {} + is-path-inside@4.0.0: {} is-plain-obj@4.1.0: {} @@ -11783,8 +11428,6 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 - just-extend@6.2.0: {} - keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -11843,8 +11486,6 @@ snapshots: lodash.flattendeep@4.4.0: {} - lodash.get@4.4.2: {} - lodash.isequal@4.5.0: {} lodash.memoize@4.1.2: {} @@ -12106,28 +11747,11 @@ snapshots: nice-try@1.0.5: {} - nise@5.1.9: - dependencies: - '@sinonjs/commons': 3.0.1 - '@sinonjs/fake-timers': 11.3.1 - '@sinonjs/text-encoding': 0.7.3 - just-extend: 6.2.0 - path-to-regexp: 6.2.2 - no-case@3.0.4: dependencies: lower-case: 2.0.2 tslib: 2.7.0 - nock@13.2.9: - dependencies: - debug: 4.3.6 - json-stringify-safe: 5.0.1 - lodash: 4.17.21 - propagate: 2.0.1 - transitivePeerDependencies: - - supports-color - node-domexception@1.0.0: {} node-fetch@2.7.0(encoding@0.1.13): @@ -12448,6 +12072,8 @@ snapshots: parse-ms@2.1.0: {} + parse-ms@3.0.0: {} + parse5@6.0.1: {} parseurl@1.3.3: {} @@ -12483,8 +12109,6 @@ snapshots: path-to-regexp@0.1.7: {} - path-to-regexp@6.2.2: {} - path-type@3.0.0: dependencies: pify: 3.0.0 @@ -12563,6 +12187,10 @@ snapshots: dependencies: parse-ms: 2.1.0 + pretty-ms@8.0.0: + dependencies: + parse-ms: 3.0.0 + process-nextick-args@2.0.1: {} process@0.11.10: {} @@ -12581,8 +12209,6 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 - propagate@2.0.1: {} - proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -12591,7 +12217,7 @@ snapshots: proxy-agent@6.3.0: dependencies: agent-base: 7.1.1 - debug: 4.3.4 + debug: 4.3.6 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.5 lru-cache: 7.18.3 @@ -12604,7 +12230,7 @@ snapshots: proxy-agent@6.3.1: dependencies: agent-base: 7.1.1 - debug: 4.3.4 + debug: 4.3.6 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.5 lru-cache: 7.18.3 @@ -13062,15 +12688,6 @@ snapshots: signal-exit@4.1.0: {} - sinon@13.0.2: - dependencies: - '@sinonjs/commons': 1.8.6 - '@sinonjs/fake-timers': 9.1.2 - '@sinonjs/samsam': 6.1.3 - diff: 5.2.0 - nise: 5.1.9 - supports-color: 7.2.0 - sisteransi@1.0.5: {} skip-regex@1.0.2: {} @@ -13387,7 +13004,7 @@ snapshots: transitivePeerDependencies: - supports-color - temp-dir@2.0.0: {} + temp-dir@3.0.0: {} terminal-link@2.1.1: dependencies: @@ -13558,8 +13175,6 @@ snapshots: type-detect@4.0.8: {} - type-detect@4.1.0: {} - type-fest@0.13.1: {} type-fest@0.20.2: {} @@ -13682,6 +13297,8 @@ snapshots: utils-merge@1.0.1: {} + uuid@10.0.0: {} + uuid@3.4.0: {} uuid@8.1.0: {} @@ -13750,7 +13367,7 @@ snapshots: dependencies: defaults: 1.0.4 - wdio-chromedriver-service@8.1.1(@wdio/types@8.39.0)(chromedriver@126.0.5)(webdriverio@8.39.1(encoding@0.1.13)(typescript@4.6.4)): + wdio-chromedriver-service@8.1.1(@wdio/types@8.39.0)(chromedriver@129.0.4)(webdriverio@8.39.1(encoding@0.1.13)(typescript@4.6.4)): dependencies: '@wdio/logger': 8.38.0 fs-extra: 11.2.0 @@ -13759,7 +13376,7 @@ snapshots: webdriverio: 8.39.1(encoding@0.1.13)(typescript@4.6.4) optionalDependencies: '@wdio/types': 8.39.0 - chromedriver: 126.0.5 + chromedriver: 129.0.4 transitivePeerDependencies: - supports-color @@ -13860,6 +13477,8 @@ snapshots: dependencies: iconv-lite: 0.4.24 + whatwg-fetch@3.6.20: {} + whatwg-mimetype@2.3.0: {} whatwg-url@5.0.0: @@ -13945,10 +13564,10 @@ snapshots: signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 - write-file-atomic@4.0.2: + write-file-atomic@5.0.1: dependencies: imurmurhash: 0.1.4 - signal-exit: 3.0.7 + signal-exit: 4.1.0 ws@7.5.10: {} diff --git a/common/config/rush/repo-state.json b/common/config/rush/repo-state.json index 98aeae6bc..ccb6e240d 100644 --- a/common/config/rush/repo-state.json +++ b/common/config/rush/repo-state.json @@ -1,5 +1,5 @@ // DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. { - "pnpmShrinkwrapHash": "d2b7857ec8d95c90fd800c6b48ae3be27839523f", + "pnpmShrinkwrapHash": "6693fc661ad5b6461d8a0fbbecf81c5f61d703bb", "preferredVersionsHash": "bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f" } diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index fe57d43e3..d4a8b2caf 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -42,6 +42,6 @@ * * Valid values are: "prerelease", "release", "minor", "patch", "major" */ - "nextBump": "patch" + "nextBump": "major" } ] diff --git a/libraries/browser-tracker-core/jest.config.js b/libraries/browser-tracker-core/jest.config.js index 504feb7ef..71b642154 100644 --- a/libraries/browser-tracker-core/jest.config.js +++ b/libraries/browser-tracker-core/jest.config.js @@ -2,6 +2,7 @@ module.exports = { preset: 'ts-jest', reporters: ['jest-standard-reporter'], testEnvironment: 'jest-environment-jsdom-global', + setupFilesAfterEnv: ['../../setupTestGlobals.ts'], testEnvironmentOptions: { url: 'https://snowplow-js-tracker.local/test/page.html', referrer: 'https://example.com/', diff --git a/libraries/browser-tracker-core/package.json b/libraries/browser-tracker-core/package.json index 910960b6e..fe05badbe 100644 --- a/libraries/browser-tracker-core/package.json +++ b/libraries/browser-tracker-core/package.json @@ -25,7 +25,7 @@ "@snowplow/tracker-core": "workspace:*", "sha1": "^1.1.1", "tslib": "^2.3.1", - "uuid": "^3.4.0" + "uuid": "^10.0.0" }, "devDependencies": { "@ampproject/rollup-plugin-closure-compiler": "~0.27.0", @@ -34,7 +34,7 @@ "@types/jest": "~27.4.1", "@types/jsdom": "~16.2.14", "@types/sha1": "~1.1.3", - "@types/uuid": "~3.4.6", + "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "~5.15.0", "@typescript-eslint/parser": "~5.15.0", "eslint": "~8.11.0", @@ -49,6 +49,7 @@ "rollup-plugin-ts": "~2.0.5", "ts-jest": "~27.1.3", "typescript": "~4.6.2", - "@testing-library/dom": "~9.3.1" + "@testing-library/dom": "~9.3.1", + "whatwg-fetch": "~3.6.20" } } diff --git a/libraries/browser-tracker-core/src/helpers/index.ts b/libraries/browser-tracker-core/src/helpers/index.ts index 817780a86..b21da64df 100755 --- a/libraries/browser-tracker-core/src/helpers/index.ts +++ b/libraries/browser-tracker-core/src/helpers/index.ts @@ -81,6 +81,18 @@ export function isFunction(func: unknown) { return false; } +/** + * Lightweight configured runtime timezone detection + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions#getting_the_users_time_zone_and_locale_preferences + * @returns IANA timezone name of current runtime preference + */ +export function getTimeZone(): string | void { + if (typeof Intl === 'object' && typeof Intl.DateTimeFormat === 'function') { + const systemPreferences = new Intl.DateTimeFormat().resolvedOptions(); + return systemPreferences.timeZone; + } +} + /** * Cleans up the page title */ @@ -242,10 +254,10 @@ export function findRootDomain(sameSite: string, secure: boolean) { cookie(cookieName, cookieValue, 0, '/', currentDomain, sameSite, secure); if (cookie(cookieName) === cookieValue) { // Clean up created cookie(s) - deleteCookie(cookieName, currentDomain, sameSite, secure); + deleteCookie(cookieName, '/', currentDomain, sameSite, secure); const cookieNames = getCookiesWithPrefix(cookiePrefix); for (let i = 0; i < cookieNames.length; i++) { - deleteCookie(cookieNames[i], currentDomain, sameSite, secure); + deleteCookie(cookieNames[i], '/', currentDomain, sameSite, secure); } return currentDomain; @@ -278,8 +290,8 @@ export function isValueInArray(val: T, array: T[]) { * @param cookieName - The name of the cookie to delete * @param domainName - The domain the cookie is in */ -export function deleteCookie(cookieName: string, domainName?: string, sameSite?: string, secure?: boolean) { - cookie(cookieName, '', -1, '/', domainName, sameSite, secure); +export function deleteCookie(cookieName: string, path?: string, domainName?: string, sameSite?: string, secure?: boolean) { + cookie(cookieName, '', -1, path, domainName, sameSite, secure); } /** diff --git a/libraries/browser-tracker-core/src/snowplow.ts b/libraries/browser-tracker-core/src/snowplow.ts index 955574b21..3161f50e8 100644 --- a/libraries/browser-tracker-core/src/snowplow.ts +++ b/libraries/browser-tracker-core/src/snowplow.ts @@ -32,6 +32,7 @@ import { LOG } from '@snowplow/tracker-core'; import { SharedState } from './state'; import { Tracker } from './tracker'; import { BrowserTracker, TrackerConfiguration } from './tracker/types'; +import { asyncCookieStorage } from './tracker/cookie_storage'; const namedTrackers: Record = {}; @@ -151,3 +152,12 @@ function getTrackersFromCollection( } return trackers; } + +/** + * Write all pending cookies to the browser. + * Useful if you track events just before the page is unloaded. + * This call is not necessary if `synchronousCookieWrite` is set to `true`. + */ +export function flushPendingCookies() { + asyncCookieStorage.flush(); +} diff --git a/libraries/browser-tracker-core/src/state.ts b/libraries/browser-tracker-core/src/state.ts index d53a5ceba..6aca815a4 100644 --- a/libraries/browser-tracker-core/src/state.ts +++ b/libraries/browser-tracker-core/src/state.ts @@ -41,8 +41,6 @@ declare global { * A set of variables which are shared among all initialised trackers */ export class SharedState { - /* List of request queues - one per Tracker instance */ - outQueues: Array = []; bufferFlushers: Array<(sync: boolean) => void> = []; /* DOM Ready */ diff --git a/libraries/browser-tracker-core/src/tracker/cookie_storage.ts b/libraries/browser-tracker-core/src/tracker/cookie_storage.ts new file mode 100644 index 000000000..b7b2dfd07 --- /dev/null +++ b/libraries/browser-tracker-core/src/tracker/cookie_storage.ts @@ -0,0 +1,207 @@ +import { cookie, deleteCookie } from '../helpers'; + + +/** + * Cookie storage interface for reading and writing cookies. + */ +export interface CookieStorage { + /** + * Get the value of a cookie + * + * @param name - The name of the cookie + * @returns The cookie value + */ + getCookie(name: string): string; + + /** + * Set a cookie + * + * @param name - The cookie name (required) + * @param value - The cookie value + * @param ttl - The cookie Time To Live (seconds) + * @param path - The cookies path + * @param domain - The cookies domain + * @param samesite - The cookies samesite attribute + * @param secure - Boolean to specify if cookie should be secure + * @returns true if the cookie was set, false otherwise + */ + setCookie( + name: string, + value?: string, + ttl?: number, + path?: string, + domain?: string, + samesite?: string, + secure?: boolean + ): boolean; + + /** + * Delete a cookie + * + * @param name - The cookie name + * @param domainName - The cookie domain name + * @param sameSite - The cookie same site attribute + * @param secure - Boolean to specify if cookie should be secure + */ + deleteCookie(name: string, path?: string, domainName?: string, sameSite?: string, secure?: boolean): void; +} + +export interface AsyncCookieStorage extends CookieStorage { + /** + * Clear the cookie storage cache (does not delete any cookies) + */ + clearCache(): void; + + /** + * Write all pending cookies. + */ + flush(): void; +} + +interface Cookie { + getValue: () => string; + setValue: (value?: string, ttl?: number, path?: string, domain?: string, samesite?: string, secure?: boolean) => boolean; + deleteValue: (path?: string, domainName?: string, sameSite?: string, secure?: boolean) => void; + flush: () => void; +} + +function newCookie(name: string): Cookie { + let flushTimer: ReturnType | undefined; + let lastSetValueArgs: Parameters | undefined; + let cacheExpireAt: Date | undefined; + let flushed = true; + const flushTimeout = 10; // milliseconds + const maxCacheTtl = 0.05; // seconds + + function getValue(): string { + // Note: we can't cache the cookie value as we don't know the expiration date + if (lastSetValueArgs && (!cacheExpireAt || cacheExpireAt > new Date())) { + return lastSetValueArgs[0] ?? cookie(name); + } + return cookie(name); + } + + function setValue(value?: string, ttl?: number, path?: string, domain?: string, samesite?: string, secure?: boolean): boolean { + lastSetValueArgs = [value, ttl, path, domain, samesite, secure]; + flushed = false; + + // throttle setting the cookie + if (flushTimer === undefined) { + flushTimer = setTimeout(() => { + flushTimer = undefined; + flush(); + }, flushTimeout); + } + + cacheExpireAt = new Date(Date.now() + Math.min(maxCacheTtl, ttl ?? maxCacheTtl) * 1000); + return true; + } + + function deleteValue(path?: string, domainName?: string, sameSite?: string, secure?: boolean): void { + lastSetValueArgs = undefined; + flushed = true; + + // cancel setting the cookie + if (flushTimer !== undefined) { + clearTimeout(flushTimer); + flushTimer = undefined; + } + + deleteCookie(name, path, domainName, sameSite, secure); + } + + function flush(): void { + if (flushTimer !== undefined) { + clearTimeout(flushTimer); + flushTimer = undefined; + } + + if (flushed) { + return; + } + flushed = true; + + if (lastSetValueArgs !== undefined) { + const [value, ttl, path, domain, samesite, secure] = lastSetValueArgs; + cookie(name, value, ttl, path, domain, samesite, secure); + } + } + + return { + getValue, + setValue, + deleteValue, + flush, + }; +} + +/** + * Create a new async cookie storage + * + * @returns A new cookie storage + */ +export function newCookieStorage(): AsyncCookieStorage { + let cache: Record = {}; + + function getOrInitCookie(name: string): Cookie { + if (!cache[name]) { + cache[name] = newCookie(name); + } + return cache[name]; + } + + function getCookie(name: string): string { + return getOrInitCookie(name).getValue(); + } + + function setCookie( + name: string, + value?: string, + ttl?: number, + path?: string, + domain?: string, + samesite?: string, + secure?: boolean + ): boolean { + return getOrInitCookie(name).setValue(value, ttl, path, domain, samesite, secure); + } + + function deleteCookie(name: string, path?: string, domainName?: string, sameSite?: string, secure?: boolean): void { + getOrInitCookie(name).deleteValue(path, domainName, sameSite, secure); + } + + function clearCache(): void { + cache = {}; + } + + function flush(): void { + for (const cookie of Object.values(cache)) { + cookie.flush(); + } + } + + return { + getCookie, + setCookie, + deleteCookie, + clearCache, + flush, + }; +} + +/** + * Cookie storage instance with asynchronous cookie writes + */ +export const asyncCookieStorage = newCookieStorage(); + +/** + * Cookie storage instance with synchronous cookie writes + */ +export const syncCookieStorage: CookieStorage = { + getCookie: cookie, + setCookie: (name, value, ttl, path, domain, samesite, secure) => { + cookie(name, value, ttl, path, domain, samesite, secure); + return document.cookie.indexOf(`${name}=`) !== -1; + }, + deleteCookie +}; diff --git a/libraries/browser-tracker-core/src/tracker/id_cookie.ts b/libraries/browser-tracker-core/src/tracker/id_cookie.ts index 9dee95792..853721dc7 100644 --- a/libraries/browser-tracker-core/src/tracker/id_cookie.ts +++ b/libraries/browser-tracker-core/src/tracker/id_cookie.ts @@ -214,8 +214,8 @@ export function updateNowTsInIdCookie(idCookie: ParsedIdCookie) { /** * Updates the first event references according to the event payload if first event in session. * - * @param idCookie Parsed cookie - * @param payloadBuilder Event payload builder + * @param idCookie - Parsed cookie + * @param payloadBuilder - Event payload builder */ export function updateFirstEventInIdCookie(idCookie: ParsedIdCookie, payloadBuilder: PayloadBuilder) { // Update first event references if new session or not present diff --git a/libraries/browser-tracker-core/src/tracker/index.ts b/libraries/browser-tracker-core/src/tracker/index.ts index a29950758..555b0a240 100755 --- a/libraries/browser-tracker-core/src/tracker/index.ts +++ b/libraries/browser-tracker-core/src/tracker/index.ts @@ -16,20 +16,19 @@ import { getReferrer, addEventListener, getHostName, - cookie, attemptGetLocalStorage, attemptWriteLocalStorage, attemptDeleteLocalStorage, - deleteCookie, fixupTitle, fromQuerystring, isInteger, attemptGetSessionStorage, attemptWriteSessionStorage, createCrossDomainParameterValue, + getTimeZone, } from '../helpers'; import { BrowserPlugin } from '../plugins'; -import { OutQueueManager } from './out_queue'; +import { newOutQueue } from './out_queue'; import { fixupUrl } from '../proxies'; import { SharedState } from '../state'; import { @@ -68,6 +67,7 @@ import { } from './id_cookie'; import { CLIENT_SESSION_SCHEMA, WEB_PAGE_SCHEMA, BROWSER_CONTEXT_SCHEMA } from './schemata'; import { getBrowserProperties } from '../helpers/browser_props'; +import { asyncCookieStorage, syncCookieStorage } from './cookie_storage'; declare global { interface Navigator { @@ -171,6 +171,9 @@ export function Tracker( }; }; + // Create a new cookie storage instance with synchronous cookie write if configured + const cookieStorage = trackerConfiguration.synchronousCookieWrite ? syncCookieStorage : asyncCookieStorage; + // Get all injected plugins browserPlugins.push(getBrowserDataPlugin()); /* When including the Web Page context, we add the relevant internal plugins */ @@ -184,7 +187,7 @@ export function Tracker( let // Tracker core core = trackerCore({ - base64: trackerConfiguration.encodeBase64, + base64: trackerConfiguration.encodeBase64 ?? trackerConfiguration.eventMethod !== 'post', corePlugins: browserPlugins, callback: sendRequest, }), @@ -198,10 +201,6 @@ export function Tracker( customReferrer: string, // Platform defaults to web for this tracker configPlatform = trackerConfiguration.platform ?? 'web', - // Snowplow collector URL - configCollectorUrl = asCollectorUrl(endpoint), - // Custom path for post requests (to get around adblockers) - configPostPath = trackerConfiguration.postPath ?? '/com.snowplowanalytics.snowplow/tp2', // Site ID configTrackerSiteId = trackerConfiguration.appId ?? '', // Document URL @@ -225,11 +224,12 @@ export function Tracker( // First-party cookie domain // User agent defaults to origin hostname configCookieDomain = trackerConfiguration.cookieDomain ?? undefined, + discoverRootDomain = trackerConfiguration.discoverRootDomain ?? configCookieDomain === undefined, // First-party cookie path // Default is user agent defined. configCookiePath = '/', // First-party cookie samesite attribute - configCookieSameSite = trackerConfiguration.cookieSameSite ?? 'None', + configCookieSameSite = trackerConfiguration.cookieSameSite ?? 'Lax', // First-party cookie secure attribute configCookieSecure = trackerConfiguration.cookieSecure ?? true, // Do Not Track browser feature @@ -273,27 +273,16 @@ export function Tracker( // Business-defined unique user ID businessUserId: string | null | undefined, // Manager for local storage queue - outQueue = OutQueueManager( - trackerId, - state, - configStateStorageStrategy == 'localStorage' || configStateStorageStrategy == 'cookieAndLocalStorage', - trackerConfiguration.eventMethod, - configPostPath, - trackerConfiguration.bufferSize ?? 1, - trackerConfiguration.maxPostBytes ?? 40000, - trackerConfiguration.maxGetBytes ?? 0, - trackerConfiguration.useStm ?? true, - trackerConfiguration.maxLocalStorageQueueSize ?? 1000, - trackerConfiguration.connectionTimeout ?? 5000, - configAnonymousServerTracking, - trackerConfiguration.customHeaders ?? {}, - trackerConfiguration.withCredentials ?? true, - trackerConfiguration.retryStatusCodes ?? [], - (trackerConfiguration.dontRetryStatusCodes ?? []).concat([400, 401, 403, 410, 422]), - trackerConfiguration.idService, - trackerConfiguration.retryFailedRequests, - trackerConfiguration.onRequestSuccess, - trackerConfiguration.onRequestFailure + outQueue = newOutQueue( + { + trackerId, + endpoint: asCollectorUrl(endpoint), + serverAnonymization: configAnonymousServerTracking, + useLocalStorage: + configStateStorageStrategy == 'localStorage' || configStateStorageStrategy == 'cookieAndLocalStorage', + ...trackerConfiguration, + }, + state ), // Whether pageViewId should be regenerated after each trackPageView. Affect web_page context preservePageViewId = false, @@ -315,11 +304,12 @@ export function Tracker( trackerConfiguration.useExtendedCrossDomainLinker || false ); - if (trackerConfiguration.hasOwnProperty('discoverRootDomain') && trackerConfiguration.discoverRootDomain) { + if (discoverRootDomain && !configCookieDomain) { configCookieDomain = findRootDomain(configCookieSameSite, configCookieSecure); } const { browserLanguage, resolution, colorDepth, cookiesEnabled } = getBrowserProperties(); + const timeZone = getTimeZone(); // Set up unchanging name-value pairs core.setTrackerVersion(version); @@ -331,6 +321,7 @@ export function Tracker( core.addPayloadPair('lang', browserLanguage); core.addPayloadPair('res', resolution); core.addPayloadPair('cd', colorDepth); + if (timeZone) core.addPayloadPair('tz', timeZone); /* * Initialize tracker @@ -471,7 +462,7 @@ export function Tracker( */ function sendRequest(request: PayloadBuilder) { if (!(configDoNotTrack || toOptoutByCookie)) { - outQueue.enqueueRequest(request.build(), configCollectorUrl); + outQueue.enqueueRequest(request.build()); } } @@ -490,7 +481,7 @@ export function Tracker( if (configStateStorageStrategy == 'localStorage') { return attemptGetLocalStorage(fullName); } else if (configStateStorageStrategy == 'cookie' || configStateStorageStrategy == 'cookieAndLocalStorage') { - return cookie(fullName); + return cookieStorage.getCookie(fullName); } return undefined; } @@ -617,8 +608,15 @@ export function Tracker( if (configStateStorageStrategy == 'localStorage') { return attemptWriteLocalStorage(name, value, timeout); } else if (configStateStorageStrategy == 'cookie' || configStateStorageStrategy == 'cookieAndLocalStorage') { - cookie(name, value, timeout, configCookiePath, configCookieDomain, configCookieSameSite, configCookieSecure); - return document.cookie.indexOf(`${name}=`) !== -1 ? true : false; + return cookieStorage.setCookie( + name, + value, + timeout, + configCookiePath, + configCookieDomain, + configCookieSameSite, + configCookieSecure + ); } return false; } @@ -631,8 +629,20 @@ export function Tracker( const sesname = getSnowplowCookieName('ses'); attemptDeleteLocalStorage(idname); attemptDeleteLocalStorage(sesname); - deleteCookie(idname, configCookieDomain, configCookieSameSite, configCookieSecure); - deleteCookie(sesname, configCookieDomain, configCookieSameSite, configCookieSecure); + cookieStorage.deleteCookie( + idname, + configCookiePath, + configCookieDomain, + configCookieSameSite, + configCookieSecure + ); + cookieStorage.deleteCookie( + sesname, + configCookiePath, + configCookieDomain, + configCookieSameSite, + configCookieSecure + ); if (!configuration?.preserveSession) { memorizedSessionId = uuid(); memorizedVisitCount = 1; @@ -843,7 +853,7 @@ export function Tracker( const isFirstEventInSession = eventIndexFromIdCookie(idCookie) === 0; if (configOptOutCookie) { - toOptoutByCookie = !!cookie(configOptOutCookie); + toOptoutByCookie = !!cookieStorage.getCookie(configOptOutCookie); } else { toOptoutByCookie = false; } @@ -1310,12 +1320,11 @@ export function Tracker( }, setUserIdFromCookie: function (cookieName: string) { - businessUserId = cookie(cookieName); + businessUserId = cookieStorage.getCookie(cookieName); }, setCollectorUrl: function (collectorUrl: string) { - configCollectorUrl = asCollectorUrl(collectorUrl); - outQueue.setCollectorUrl(configCollectorUrl); + outQueue.setCollectorUrl(asCollectorUrl(collectorUrl)); }, setBufferSize: function (newBufferSize: number) { diff --git a/libraries/browser-tracker-core/src/tracker/local_storage_event_store.ts b/libraries/browser-tracker-core/src/tracker/local_storage_event_store.ts new file mode 100644 index 000000000..52f7db9aa --- /dev/null +++ b/libraries/browser-tracker-core/src/tracker/local_storage_event_store.ts @@ -0,0 +1,61 @@ +import { EventStore, newInMemoryEventStore, EventStorePayload } from '@snowplow/tracker-core'; +import { LocalStorageEventStoreConfigurationBase } from './types'; + +export interface LocalStorageEventStoreConfiguration extends LocalStorageEventStoreConfigurationBase { + /** + * The unique identifier for the event store + */ + trackerId: string; +} + +export interface LocalStorageEventStore extends EventStore { + setUseLocalStorage: (localStorage: boolean) => void; +} + +export function newLocalStorageEventStore({ + trackerId, + maxLocalStorageQueueSize = 1000, + useLocalStorage = true, +}: LocalStorageEventStoreConfiguration): LocalStorageEventStore { + const queueName = `snowplowOutQueue_${trackerId}`; + + function newInMemoryEventStoreFromLocalStorage() { + if (useLocalStorage) { + const localStorageQueue = window.localStorage.getItem(queueName); + const events: EventStorePayload[] = localStorageQueue ? JSON.parse(localStorageQueue) : []; + return newInMemoryEventStore({ maxSize: maxLocalStorageQueueSize, events }); + } else { + return newInMemoryEventStore({ maxSize: maxLocalStorageQueueSize }); + } + } + + const { getAll, getAllPayloads, add, count, iterator, removeHead } = newInMemoryEventStoreFromLocalStorage(); + + function sync(): Promise { + if (useLocalStorage) { + return getAll().then((events) => { + window.localStorage.setItem(queueName, JSON.stringify(events)); + }); + } else { + return Promise.resolve(); + } + } + + return { + count, + add: (payload: EventStorePayload) => { + add(payload); + return sync().then(count); + }, + removeHead: (count: number) => { + removeHead(count); + return sync(); + }, + iterator, + getAll, + getAllPayloads, + setUseLocalStorage: (use: boolean) => { + useLocalStorage = use; + }, + }; +} diff --git a/libraries/browser-tracker-core/src/tracker/out_queue.ts b/libraries/browser-tracker-core/src/tracker/out_queue.ts index 5aee68f63..37327b0ec 100644 --- a/libraries/browser-tracker-core/src/tracker/out_queue.ts +++ b/libraries/browser-tracker-core/src/tracker/out_queue.ts @@ -1,614 +1,42 @@ -import { attemptWriteLocalStorage, isString } from '../helpers'; +import { newEmitter, EmitterConfiguration } from '@snowplow/tracker-core'; +import { + newLocalStorageEventStore, + LocalStorageEventStoreConfiguration, + LocalStorageEventStore, +} from './local_storage_event_store'; +import { Payload } from '@snowplow/tracker-core'; import { SharedState } from '../state'; -import { localStorageAccessible } from '../detectors'; -import { LOG, Payload } from '@snowplow/tracker-core'; -import { PAYLOAD_DATA_SCHEMA } from './schemata'; -import { EventBatch, RequestFailure } from './types'; export interface OutQueue { - enqueueRequest: (request: Payload, url: string) => void; - executeQueue: () => void; + enqueueRequest: (request: Payload) => Promise; + executeQueue: () => Promise; setUseLocalStorage: (localStorage: boolean) => void; setAnonymousTracking: (anonymous: boolean) => void; setCollectorUrl: (url: string) => void; setBufferSize: (bufferSize: number) => void; } -/** - * Object handling sending events to a collector. - * Instantiated once per tracker instance. - * - * @param id - The Snowplow function name (used to generate the localStorage key) - * @param sharedSate - Stores reference to the outbound queue so it can unload the page when all queues are empty - * @param useLocalStorage - Whether to use localStorage at all - * @param eventMethod - if null will use 'beacon' otherwise can be set to 'post', 'get', or 'beacon' to force. - * @param postPath - The path where events are to be posted - * @param bufferSize - How many events to batch in localStorage before sending them all - * @param maxPostBytes - Maximum combined size in bytes of the event JSONs in a POST request - * @param maxGetBytes - Maximum size in bytes of the complete event URL string in a GET request. 0 for no limit. - * @param useStm - Whether to add timestamp to events - * @param maxLocalStorageQueueSize - Maximum number of queued events we will attempt to store in local storage - * @param connectionTimeout - Defines how long to wait before aborting the request - * @param anonymousTracking - Defines whether to set the SP-Anonymous header for anonymous tracking on GET and POST - * @param customHeaders - Allows custom headers to be defined and passed on XMLHttpRequest requests - * @param withCredentials - Sets the value of the withCredentials flag on XMLHttpRequest (GET and POST) requests - * @param retryStatusCodes – Failure HTTP response status codes from Collector for which sending events should be retried (they can override the `dontRetryStatusCodes`) - * @param dontRetryStatusCodes – Failure HTTP response status codes from Collector for which sending events should not be retried - * @param idService - Id service full URL. This URL will be added to the queue and will be called using a GET method. - * @param retryFailedRequests - Whether to retry failed requests - Takes precedent over `retryStatusCodes` and `dontRetryStatusCodes` - * @param onRequestSuccess - Function called when a request succeeds - * @param onRequestFailure - Function called when a request does not succeed - * @returns object OutQueueManager instance - */ -export function OutQueueManager( - id: string, - sharedSate: SharedState, - useLocalStorage: boolean, - eventMethod: string | boolean, - postPath: string, - bufferSize: number, - maxPostBytes: number, - maxGetBytes: number, - useStm: boolean, - maxLocalStorageQueueSize: number, - connectionTimeout: number, - anonymousTracking: boolean, - customHeaders: Record, - withCredentials: boolean, - retryStatusCodes: number[], - dontRetryStatusCodes: number[], - idService?: string, - retryFailedRequests: boolean = true, - onRequestSuccess?: (data: EventBatch) => void, - onRequestFailure?: (data: RequestFailure) => void +export function newOutQueue( + configuration: EmitterConfiguration & LocalStorageEventStoreConfiguration, + sharedState: SharedState ): OutQueue { - type PostEvent = { - evt: Record; - bytes: number; - }; - - let executingQueue = false, - configCollectorUrl: string, - outQueue: Array | Array = [], - idServiceCalled = false; - - //Force to lower case if its a string - eventMethod = typeof eventMethod === 'string' ? eventMethod.toLowerCase() : eventMethod; - - // Use the Beacon API if eventMethod is set true, 'true', or 'beacon'. - const isBeaconRequested = eventMethod === true || eventMethod === 'beacon' || eventMethod === 'true', - // Fall back to POST or GET for browsers which don't support Beacon API - isBeaconAvailable = Boolean( - isBeaconRequested && - window.navigator && - typeof window.navigator.sendBeacon === 'function' && - !hasWebKitBeaconBug(window.navigator.userAgent) - ), - useBeacon = isBeaconAvailable && isBeaconRequested, - // Use GET if specified - isGetRequested = eventMethod === 'get', - // Don't use XhrHttpRequest for browsers which don't support CORS XMLHttpRequests (e.g. IE <= 9) - useXhr = Boolean(window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest()), - // Use POST if specified - usePost = !isGetRequested && useXhr && (eventMethod === 'post' || isBeaconRequested), - // Resolve all options and capabilities and decide path - path = usePost ? postPath : '/i', - // Different queue names for GET and POST since they are stored differently - queueName = `snowplowOutQueue_${id}_${usePost ? 'post2' : 'get'}`; - - // Ensure we don't set headers when beacon is the requested eventMethod as we might fallback to POST - // and end up sending them in older browsers which don't support beacon leading to inconsistencies - if (isBeaconRequested) customHeaders = {}; - - // Get buffer size or set 1 if unable to buffer - bufferSize = (useLocalStorage && localStorageAccessible() && usePost && bufferSize) || 1; - - if (useLocalStorage) { - // Catch any JSON parse errors or localStorage that might be thrown - try { - const localStorageQueue = window.localStorage.getItem(queueName); - outQueue = localStorageQueue ? JSON.parse(localStorageQueue) : []; - } catch (e) {} - } - - // Initialize to and empty array if we didn't get anything out of localStorage - if (!Array.isArray(outQueue)) { - outQueue = []; - } - - // Used by pageUnloadGuard - sharedSate.outQueues.push(outQueue); - - if (useXhr && bufferSize > 1) { - sharedSate.bufferFlushers.push(function (sync) { - if (!executingQueue) { - executeQueue(sync); - } - }); - } - - /* - * Convert a dictionary to a querystring - * The context field is the last in the querystring - */ - function getQuerystring(request: Payload) { - let querystring = '?', - lowPriorityKeys = { co: true, cx: true }, - firstPair = true; - - for (const key in request) { - if (request.hasOwnProperty(key) && !lowPriorityKeys.hasOwnProperty(key)) { - if (!firstPair) { - querystring += '&'; - } else { - firstPair = false; - } - querystring += encodeURIComponent(key) + '=' + encodeURIComponent(request[key] as string | number | boolean); - } - } - - for (const contextKey in lowPriorityKeys) { - if (request.hasOwnProperty(contextKey) && lowPriorityKeys.hasOwnProperty(contextKey)) { - querystring += '&' + contextKey + '=' + encodeURIComponent(request[contextKey] as string | number | boolean); - } - } - - return querystring; - } - - /* - * Convert numeric fields to strings to match payload_data schema - */ - function getBody(request: Payload): PostEvent { - const cleanedRequest = Object.keys(request) - .map<[string, unknown]>((k) => [k, request[k]]) - .reduce((acc, [key, value]) => { - acc[key] = (value as Object).toString(); - return acc; - }, {} as Record); - return { - evt: cleanedRequest, - bytes: getUTF8Length(JSON.stringify(cleanedRequest)), - }; - } - - /** - * Count the number of bytes a string will occupy when UTF-8 encoded - * Taken from http://stackoverflow.com/questions/2848462/count-bytes-in-textarea-using-javascript/ - * - * @param string - s - * @returns number Length of s in bytes when UTF-8 encoded - */ - function getUTF8Length(s: string) { - let len = 0; - for (let i = 0; i < s.length; i++) { - const code = s.charCodeAt(i); - if (code <= 0x7f) { - len += 1; - } else if (code <= 0x7ff) { - len += 2; - } else if (code >= 0xd800 && code <= 0xdfff) { - // Surrogate pair: These take 4 bytes in UTF-8 and 2 chars in UCS-2 - // (Assume next char is the other [valid] half and just skip it) - len += 4; - i++; - } else if (code < 0xffff) { - len += 3; - } else { - len += 4; - } - } - return len; - } - - const postable = (queue: Array | Array): queue is Array => { - return typeof queue[0] === 'object' && 'evt' in queue[0]; - }; - - /** - * Send event as POST request right away without going to queue. Used when the request surpasses maxGetBytes or maxPostBytes - * @param body POST request body - * @param configCollectorUrl full collector URL with path - */ - function sendPostRequestWithoutQueueing(body: PostEvent, configCollectorUrl: string) { - const xhr = initializeXMLHttpRequest(configCollectorUrl, true, false); - const batch = attachStmToEvent([body.evt]); - - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - if (isSuccessfulRequest(xhr.status)) { - onRequestSuccess?.(batch); - } else { - onRequestFailure?.({ - status: xhr.status, - message: xhr.statusText, - events: batch, - willRetry: false, - }); - } - } - }; - - xhr.send(encloseInPayloadDataEnvelope(batch)); - } - - function removeEventsFromQueue(numberToSend: number): void { - for (let deleteCount = 0; deleteCount < numberToSend; deleteCount++) { - outQueue.shift(); - } - if (useLocalStorage) { - attemptWriteLocalStorage(queueName, JSON.stringify(outQueue.slice(0, maxLocalStorageQueueSize))); - } - } - - function setXhrCallbacks(xhr: XMLHttpRequest, numberToSend: number, batch: EventBatch) { - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - clearTimeout(xhrTimeout); - if (isSuccessfulRequest(xhr.status)) { - removeEventsFromQueue(numberToSend); - onRequestSuccess?.(batch); - executeQueue(); - } else { - const willRetry = shouldRetryForStatusCode(xhr.status); - if (!willRetry) { - LOG.error(`Status ${xhr.status}, will not retry.`); - removeEventsFromQueue(numberToSend); - } - onRequestFailure?.({ - status: xhr.status, - message: xhr.statusText, - events: batch, - willRetry, - }); - - executingQueue = false; - } - } - }; - - // Time out POST requests after connectionTimeout - const xhrTimeout = setTimeout(function () { - xhr.abort(); - if (!retryFailedRequests) { - removeEventsFromQueue(numberToSend); - } - onRequestFailure?.({ - status: 0, - message: 'timeout', - events: batch, - willRetry: retryFailedRequests, - }); - executingQueue = false; - }, connectionTimeout); - } - - /* - * Queue for submission to the collector and start processing queue - */ - function enqueueRequest(request: Payload, url: string) { - configCollectorUrl = url + path; - const eventTooBigWarning = (bytes: number, maxBytes: number) => - LOG.warn('Event (' + bytes + 'B) too big, max is ' + maxBytes); - - if (usePost) { - const body = getBody(request); - if (body.bytes >= maxPostBytes) { - eventTooBigWarning(body.bytes, maxPostBytes); - sendPostRequestWithoutQueueing(body, configCollectorUrl); - return; - } else { - (outQueue as Array).push(body); - } - } else { - const querystring = getQuerystring(request); - if (maxGetBytes > 0) { - const requestUrl = createGetUrl(querystring); - const bytes = getUTF8Length(requestUrl); - if (bytes >= maxGetBytes) { - eventTooBigWarning(bytes, maxGetBytes); - if (useXhr) { - const body = getBody(request); - const postUrl = url + postPath; - sendPostRequestWithoutQueueing(body, postUrl); - } - return; - } - } - (outQueue as Array).push(querystring); - } - let savedToLocalStorage = false; - if (useLocalStorage) { - savedToLocalStorage = attemptWriteLocalStorage( - queueName, - JSON.stringify(outQueue.slice(0, maxLocalStorageQueueSize)) - ); - } - - // If we're not processing the queue, we'll start. - if (!executingQueue && (!savedToLocalStorage || outQueue.length >= bufferSize)) { - executeQueue(); - } - } - - /* - * Run through the queue of requests, sending them one at a time. - * Stops processing when we run out of queued requests, or we get an error. - */ - function executeQueue(sync: boolean = false) { - // Failsafe in case there is some way for a bad value like "null" to end up in the outQueue - while (outQueue.length && typeof outQueue[0] !== 'string' && typeof outQueue[0] !== 'object') { - outQueue.shift(); - } - - if (!outQueue.length) { - executingQueue = false; - return; - } + const eventStore = configuration.eventStore ?? newLocalStorageEventStore(configuration); + configuration.eventStore = eventStore; + const emitter = newEmitter(configuration); - // Let's check that we have a URL - if (!isString(configCollectorUrl)) { - throw 'No collector configured'; - } - - executingQueue = true; - - if (idService && !idServiceCalled) { - const xhr = initializeXMLHttpRequest(idService, false, sync); - idServiceCalled = true; - xhr.timeout = connectionTimeout; - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - executeQueue(); - } - }; - xhr.send(); - return; - } - - if (useXhr) { - // Keep track of number of events to delete from queue - const chooseHowManyToSend = (queue: Array<{ bytes: number }>) => { - let numberToSend = 0, - byteCount = 0; - while (numberToSend < queue.length) { - byteCount += queue[numberToSend].bytes; - if (byteCount >= maxPostBytes) { - break; - } else { - numberToSend += 1; - } - } - return numberToSend; - }; - - let url: string, xhr: XMLHttpRequest, numberToSend: number; - if (postable(outQueue)) { - url = configCollectorUrl; - xhr = initializeXMLHttpRequest(url, true, sync); - numberToSend = chooseHowManyToSend(outQueue); - } else { - url = createGetUrl(outQueue[0]); - xhr = initializeXMLHttpRequest(url, false, sync); - numberToSend = 1; - } - - if (!postable(outQueue)) { - // If not postable then it's a GET so just send it - setXhrCallbacks(xhr, numberToSend, [url]); - xhr.send(); - } else { - let batch = outQueue.slice(0, numberToSend); - - if (batch.length > 0) { - let beaconStatus = false; - - const eventBatch = batch.map(function (x) { - return x.evt; - }); - - if (useBeacon) { - const blob = new Blob([encloseInPayloadDataEnvelope(attachStmToEvent(eventBatch))], { - type: 'application/json', - }); - try { - beaconStatus = window.navigator.sendBeacon(url, blob); - } catch (error) { - beaconStatus = false; - } - } - - // When beaconStatus is true, we can't _guarantee_ that it was successful (beacon queues asynchronously) - // but the browser has taken it out of our hands, so we want to flush the queue assuming it will do its job - if (beaconStatus === true) { - removeEventsFromQueue(numberToSend); - onRequestSuccess?.(batch); - executeQueue(); - } else { - const batch = attachStmToEvent(eventBatch); - setXhrCallbacks(xhr, numberToSend, batch); - xhr.send(encloseInPayloadDataEnvelope(batch)); - } - } - } - } else if (!anonymousTracking && !postable(outQueue)) { - // We can't send with this technique if anonymous tracking is on as we can't attach the header - let image = new Image(1, 1), - loading = true; - - image.onload = function () { - if (!loading) return; - loading = false; - outQueue.shift(); - if (useLocalStorage) { - attemptWriteLocalStorage(queueName, JSON.stringify(outQueue.slice(0, maxLocalStorageQueueSize))); - } - executeQueue(); - }; - - image.onerror = function () { - if (!loading) return; - loading = false; - executingQueue = false; - }; - - image.src = createGetUrl(outQueue[0]); - - setTimeout(function () { - if (loading && executingQueue) { - loading = false; - executeQueue(); - } - }, connectionTimeout); - } else { - executingQueue = false; - } - } - - /** - * Determines whether a request was successful, based on its status code - * Anything in the 2xx range is considered successful - * - * @param statusCode The status code of the request - * @returns Whether the request was successful - */ - function isSuccessfulRequest(statusCode: number): boolean { - return statusCode >= 200 && statusCode < 300; - } - - function shouldRetryForStatusCode(statusCode: number) { - // success, don't retry - if (isSuccessfulRequest(statusCode)) { - return false; - } - - if (!retryFailedRequests) { - return false; - } - - // retry if status code among custom user-supplied retry codes - if (retryStatusCodes.includes(statusCode)) { - return true; - } - - // retry if status code *not* among the don't retry codes - return !dontRetryStatusCodes.includes(statusCode); - } - - /** - * Open an XMLHttpRequest for a given endpoint with the correct credentials and header - * - * @param string - url The destination URL - * @returns object The XMLHttpRequest - */ - function initializeXMLHttpRequest(url: string, post: boolean, sync: boolean) { - const xhr = new XMLHttpRequest(); - if (post) { - xhr.open('POST', url, !sync); - xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - } else { - xhr.open('GET', url, !sync); - } - xhr.withCredentials = withCredentials; - if (anonymousTracking) { - xhr.setRequestHeader('SP-Anonymous', '*'); - } - for (const header in customHeaders) { - if (Object.prototype.hasOwnProperty.call(customHeaders, header)) { - xhr.setRequestHeader(header, customHeaders[header]); - } - } - return xhr; - } - - /** - * Enclose an array of events in a self-describing payload_data JSON string - * - * @param array - events Batch of events - * @returns string payload_data self-describing JSON - */ - function encloseInPayloadDataEnvelope(events: Array>) { - return JSON.stringify({ - schema: PAYLOAD_DATA_SCHEMA, - data: events, - }); - } - - /** - * Attaches the STM field to outbound POST events. - * - * @param events - the events to attach the STM to - */ - function attachStmToEvent(events: Array>) { - const stm = new Date().getTime().toString(); - for (let i = 0; i < events.length; i++) { - events[i]['stm'] = stm; - } - return events; - } - - /** - * Creates the full URL for sending the GET request. Will append `stm` if enabled - * - * @param nextRequest - the query string of the next request - */ - function createGetUrl(nextRequest: string) { - if (useStm) { - return configCollectorUrl + nextRequest.replace('?', '?stm=' + new Date().getTime() + '&'); - } - - return configCollectorUrl + nextRequest; - } + sharedState.bufferFlushers.push(emitter.flush); return { - enqueueRequest: enqueueRequest, - executeQueue: () => { - if (!executingQueue) { - executeQueue(); - } - }, + enqueueRequest: emitter.input, + executeQueue: emitter.flush, + setAnonymousTracking: emitter.setAnonymousTracking, + setCollectorUrl: emitter.setCollectorUrl, + setBufferSize: emitter.setBufferSize, setUseLocalStorage: (localStorage: boolean) => { - useLocalStorage = localStorage; - }, - setAnonymousTracking: (anonymous: boolean) => { - anonymousTracking = anonymous; - }, - setCollectorUrl: (url: string) => { - configCollectorUrl = url + path; - }, - setBufferSize: (newBufferSize: number) => { - bufferSize = newBufferSize; + if (eventStore.hasOwnProperty('setUseLocalStorage')) { + const localStorageStore = eventStore as LocalStorageEventStore; + localStorageStore.setUseLocalStorage(localStorage); + } }, }; - - function hasWebKitBeaconBug(useragent: string) { - return ( - isIosVersionLessThanOrEqualTo(13, useragent) || - (isMacosxVersionLessThanOrEqualTo(10, 15, useragent) && isSafari(useragent)) - ); - - function isIosVersionLessThanOrEqualTo(major: number, useragent: string) { - const match = useragent.match('(iP.+; CPU .*OS (d+)[_d]*.*) AppleWebKit/'); - if (match && match.length) { - return parseInt(match[0]) <= major; - } - return false; - } - - function isMacosxVersionLessThanOrEqualTo(major: number, minor: number, useragent: string) { - const match = useragent.match('(Macintosh;.*Mac OS X (d+)_(d+)[_d]*.*) AppleWebKit/'); - if (match && match.length) { - return parseInt(match[0]) <= major || (parseInt(match[0]) === major && parseInt(match[1]) <= minor); - } - return false; - } - - function isSafari(useragent: string) { - return useragent.match('Version/.* Safari/') && !isChromiumBased(useragent); - } - - function isChromiumBased(useragent: string) { - return useragent.match('Chrom(e|ium)'); - } - } } diff --git a/libraries/browser-tracker-core/src/tracker/schemata.ts b/libraries/browser-tracker-core/src/tracker/schemata.ts index ccd0df3c8..59e0482d4 100644 --- a/libraries/browser-tracker-core/src/tracker/schemata.ts +++ b/libraries/browser-tracker-core/src/tracker/schemata.ts @@ -1,4 +1,3 @@ export const WEB_PAGE_SCHEMA = 'iglu:com.snowplowanalytics.snowplow/web_page/jsonschema/1-0-0'; export const BROWSER_CONTEXT_SCHEMA = 'iglu:com.snowplowanalytics.snowplow/browser_context/jsonschema/2-0-0'; export const CLIENT_SESSION_SCHEMA = 'iglu:com.snowplowanalytics.snowplow/client_session/jsonschema/1-0-2'; -export const PAYLOAD_DATA_SCHEMA = 'iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-4'; diff --git a/libraries/browser-tracker-core/src/tracker/types.ts b/libraries/browser-tracker-core/src/tracker/types.ts index a93973bd6..2e95aa451 100755 --- a/libraries/browser-tracker-core/src/tracker/types.ts +++ b/libraries/browser-tracker-core/src/tracker/types.ts @@ -6,6 +6,7 @@ import { CorePluginConfiguration, } from '@snowplow/tracker-core'; import { SharedState } from '../state'; +import { EmitterConfigurationBase, EventStoreConfiguration } from '@snowplow/tracker-core'; type RequireAtLeastOne = { [K in keyof T]-?: Required> & Partial>> }[keyof T]; @@ -29,8 +30,6 @@ export type StateStorageStrategy = 'cookieAndLocalStorage' | 'cookie' | 'localSt export type Platform = 'web' | 'mob' | 'pc' | 'srv' | 'app' | 'tv' | 'cnsl' | 'iot'; /* The supported Cookie SameSite values */ export type CookieSameSite = 'None' | 'Lax' | 'Strict'; -/* The supported methods which events can be sent with */ -export type EventMethod = 'post' | 'get' | 'beacon'; /* Available configuration for the extended cross domain linker */ export type ExtendedCrossDomainLinkerAttributes = { @@ -49,6 +48,24 @@ export type ExtendedCrossDomainLinkerOptions = boolean | ExtendedCrossDomainLink /* Setting for the `preservePageViewIdForUrl` configuration that decides how to preserve the pageViewId on URL changes. */ export type PreservePageViewIdForUrl = boolean | 'full' | 'pathname' | 'pathnameAndSearch'; +export interface LocalStorageEventStoreConfigurationBase extends EventStoreConfiguration { + /** + * The maximum amount of events that will be buffered in local storage + * + * This is useful to ensure the Tracker doesn't fill the 5MB or 10MB available to + * each website should the collector be unavailable due to lost connectivity. + * Will drop events once the limit is hit + * @defaultValue 1000 + */ + maxLocalStorageQueueSize?: number; + + /** + * Whether to use localStorage at all + * Default is true + */ + useLocalStorage?: boolean; +} + /** * The configuration object for initialising the tracker * @example @@ -64,7 +81,7 @@ export type PreservePageViewIdForUrl = boolean | 'full' | 'pathname' | 'pathname export type TrackerConfiguration = { /** * Should event properties be base64 encoded where supported - * @defaultValue true + * @defaultValue false unless {@link EmitterConfigurationBase.eventMethod | eventMethod} is `get` */ encodeBase64?: boolean; /** @@ -80,7 +97,7 @@ export type TrackerConfiguration = { /** * The SameSite value for the cookie * {@link https://snowplowanalytics.com/blog/2020/09/07/pipeline-configuration-for-complete-and-accurate-data/} - * @defaultValue None + * @defaultValue Lax */ cookieSameSite?: CookieSameSite; /** @@ -93,12 +110,6 @@ export type TrackerConfiguration = { * @defaultValue 63072000 (2 years) */ cookieLifetime?: number; - /** - * Sets the value of the withCredentials flag - * on XMLHttpRequest (GET and POST) requests - * @defaultValue true - */ - withCredentials?: boolean; /** * How long until a session expires * @defaultValue 1800 (30 minutes) @@ -116,28 +127,6 @@ export type TrackerConfiguration = { * @defaultValue false */ respectDoNotTrack?: boolean; - /** - * The preferred technique to use to send events - * @defaultValue post - */ - eventMethod?: EventMethod; - /** - * The post path which events will be sent to - * Ensure your collector is configured to accept events on this post path - * @defaultValue '/com.snowplowanalytics.snowplow/tp2' - */ - postPath?: string; - /** - * Should the Sent Timestamp be attached to events - * @defaultValue true - */ - useStm?: boolean; - /** - * The amount of events that should be buffered before sending - * Recommended to leave as 1 to reduce change of losing events - * @defaultValue 1 - */ - bufferSize?: number; /** * Configure the cross domain linker which will add user identifiers to * links on the callback @@ -148,23 +137,13 @@ export type TrackerConfiguration = { * more user/session information to pass to the cross domain navigation. */ useExtendedCrossDomainLinker?: ExtendedCrossDomainLinkerOptions; - /** - * The max size a POST request can be before the tracker will force send it - * @defaultValue 40000 - */ - maxPostBytes?: number; - /** - * The max size a GET request (its complete URL) can be. Requests over this size will be tried as a POST request. - * @defaultValue unlimited - */ - maxGetBytes?: number; /** * Whether the tracker should attempt to figure out what the root * domain is to store cookies on * * This sets cookies to try to determine the root domain, and some cookies may * fail to save. This is expected behavior. - * @defaultValue false + * @defaultValue true */ discoverRootDomain?: boolean; /** @@ -173,15 +152,6 @@ export type TrackerConfiguration = { * @defaultValue cookieAndLocalStorage */ stateStorageStrategy?: StateStorageStrategy; - /** - * The maximum amount of events that will be buffered in local storage - * - * This is useful to ensure the Tracker doesn't fill the 5MB or 10MB available to - * each website should the collector be unavailable due to lost connectivity. - * Will drop events once the limit is hit - * @defaultValue 1000 - */ - maxLocalStorageQueueSize?: number; /** * Whether to reset the Activity Tracking counters on a new page view. * Disabling this leads to legacy behavior due to a "bug". @@ -189,11 +159,6 @@ export type TrackerConfiguration = { * @defaultValue true */ resetActivityTrackingOnPageView?: boolean; - /** - * How long to wait before aborting requests to the collector - * @defaultValue 5000 (milliseconds) - */ - connectionTimeout?: number; /** * Configuration for Anonymous Tracking * @defaultValue false @@ -209,66 +174,11 @@ export type TrackerConfiguration = { * @defaultValue [] */ plugins?: Array; - /** - * An object of key value pairs which represent headers to - * attach when sending a POST request, only works for POST - * @defaultValue `{}` - */ - customHeaders?: Record; - /** - * List of HTTP response status codes for which events sent to Collector should be retried in future requests. - * Only non-success status codes are considered (greater or equal to 300). - * The retry codes are only considered for GET and POST requests. - * By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422. - */ - retryStatusCodes?: number[]; - /** - * List of HTTP response status codes for which events sent to Collector should not be retried in future request. - * Only non-success status codes are considered (greater or equal to 300). - * The don't retry codes are only considered for GET and POST requests. - * By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422. - */ - dontRetryStatusCodes?: number[]; /** * Callback fired whenever the session identifier is updated. * @param updatedSession - On session update, the new session information plus the previous session id. */ onSessionUpdateCallback?: (updatedSession: ClientSession) => void; - /** - * Id service full URL. This URL will be added to the queue and will be called using a GET method. - * This option is there to allow the service URL to be called in order to set any required identifiers e.g. extra cookies. - * - * The request respects the `anonymousTracking` option, including the SP-Anonymous header if needed, and any additional custom headers from the customHeaders option. - */ - idService?: string; - /** - * Whether to retry failed requests to the collector. - * - * Failed requests are requests that failed due to - * [timeouts](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout_event), - * [network errors](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/error_event), - * and [abort events](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort_event). - * - * Takes precedent over `retryStatusCodes` and `dontRetryStatusCodes`. - * - * @defaultValue true - */ - retryFailedRequests?: boolean; - /** - * a callback function to be executed whenever a request is successfully sent to the collector. - * In practice this means any request which returns a 2xx status code will trigger this callback. - * - * @param data - The event batch that was successfully sent - */ - onRequestSuccess?: (data: EventBatch) => void; - - /** - * a callback function to be executed whenever a request fails to be sent to the collector. - * This is the inverse of the onRequestSuccess callback, so any non 2xx status code will trigger this callback. - * - * @param data - The data associated with the event(s) that failed to send - */ - onRequestFailure?: (data: RequestFailure) => void; /** * Decide how the `pageViewId` should be preserved based on the URL. @@ -280,7 +190,17 @@ export type TrackerConfiguration = { * Defaults to `false`. */ preservePageViewIdForUrl?: PreservePageViewIdForUrl; -}; + + /** + * Whether to write the cookies synchronously. + * This can be useful for testing purposes to ensure that the cookies are written before the test continues. + * It also has the benefit of making sure that the cookie is correctly set before session information is used in events. + * The downside is that it is slower and blocks the main thread. + * @defaultValue false + */ + synchronousCookieWrite?: boolean; +} & EmitterConfigurationBase & + LocalStorageEventStoreConfigurationBase; /** * The data which is passed to the Activity Tracking callback @@ -708,34 +628,13 @@ export interface ClientSession extends Record { firstEventTimestamp: string | null; } -/** - * A collection of GET events which are sent to the collector. - * This will be a collection of query strings. - */ -export type GetBatch = string[]; - -/** - * A collection of POST events which are sent to the collector. - * This will be a collection of JSON objects. - */ -export type PostBatch = Record[]; - -/** - * A collection of events which are sent to the collector. - * This can either be a collection of query strings or JSON objects. - */ -export type EventBatch = GetBatch | PostBatch; - -/** - * The data that will be available to the `onRequestFailure` callback - */ -export type RequestFailure = { - /** The batch of events that failed to send */ - events: EventBatch; - /** The status code of the failed request */ - status?: number; - /** The error message of the failed request */ - message?: string; - /** Whether the tracker will retry the request */ - willRetry: boolean; -}; +export { + RequestFailure, + EventBatch, + EventMethod, + EventStore, + EventStoreIterator, + EventStorePayload, + EventStoreConfiguration, + Payload, +} from '@snowplow/tracker-core'; diff --git a/libraries/browser-tracker-core/test/helpers/index.ts b/libraries/browser-tracker-core/test/helpers/index.ts index b2a3acff3..cff0d8bfb 100644 --- a/libraries/browser-tracker-core/test/helpers/index.ts +++ b/libraries/browser-tracker-core/test/helpers/index.ts @@ -60,7 +60,7 @@ export function createTestIdCookie(params: Partial) { const { domainHash } = cookieParams; // @ts-expect-error delete cookieParams.domainHash; - return `_sp_id.${domainHash}=${Object.values(cookieParams).join('.')}; Expires=; Path=/; SameSite=None; Secure;`; + return `_sp_id.${domainHash}=${Object.values(cookieParams).join('.')}; Expires=; Path=/; SameSite=Lax; Secure;`; } interface CreateTestSessionIdCookie { @@ -72,10 +72,11 @@ interface CreateTestSessionIdCookie { */ export function createTestSessionIdCookie(params?: CreateTestSessionIdCookie) { const domainHash = DEFAULT_DOMAIN_HASH || params?.domainHash; - return `_sp_ses.${domainHash}=*; Expires=; Path=/; SameSite=None; Secure;`; + return `_sp_ses.${domainHash}=*; Expires=; Path=/; SameSite=Lax; Secure;`; } export function createTracker(configuration?: TrackerConfiguration, sharedState?: SharedState) { let id = 'sp-' + Math.random(); + configuration = { ...configuration, synchronousCookieWrite: true }; return addTracker(id, id, '', '', sharedState ?? new SharedState(), configuration); } diff --git a/libraries/browser-tracker-core/test/out_queue.test.ts b/libraries/browser-tracker-core/test/out_queue.test.ts index c2550a40c..aae8e4667 100644 --- a/libraries/browser-tracker-core/test/out_queue.test.ts +++ b/libraries/browser-tracker-core/test/out_queue.test.ts @@ -28,214 +28,174 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import { OutQueueManager, OutQueue } from '../src/tracker/out_queue'; +import { newOutQueue, OutQueue } from '../src/tracker/out_queue'; import { SharedState } from '../src/state'; -import { EventBatch, RequestFailure } from '../src/tracker/types'; - -const readPostQueue = () => { - return JSON.parse( - window.localStorage.getItem('snowplowOutQueue_sp_post2') ?? fail('Unable to find local storage queue') - ); -}; - -const readGetQueue = () => - JSON.parse(window.localStorage.getItem('snowplowOutQueue_sp_get') ?? fail('Unable to find local storage queue')); - -const getQuerystring = (p: object) => - '?' + - Object.entries(p) - .map(([k, v]) => k + '=' + encodeURIComponent(v)) - .join('&'); +import { EventStore, newInMemoryEventStore, EventBatch, RequestFailure } from '@snowplow/tracker-core'; + +function newMockEventStore({ maxSize }: { maxSize: number }): EventStore & { addCount: () => number } { + let eventStore = newInMemoryEventStore({ maxSize }); + let addCount = 0; + + return { + add: (payload) => { + addCount++; + return eventStore.add(payload); + }, + removeHead: eventStore.removeHead, + count: eventStore.count, + iterator: eventStore.iterator, + getAll: eventStore.getAll, + getAllPayloads: eventStore.getAllPayloads, + addCount: () => addCount, + }; +} describe('OutQueueManager', () => { const maxQueueSize = 2; - var xhrMock: Partial; - var xhrOpenMock: jest.Mock; + let eventStore: EventStore & { addCount: () => number }; + let responseStatusCode: number; + let requests: Request[]; + const customFetch = async (request: Request) => { + requests.push(request); + return new Response(null, { status: responseStatusCode }); + }; + beforeEach(() => { - localStorage.clear(); - - xhrOpenMock = jest.fn(); - xhrMock = { - open: xhrOpenMock, - send: jest.fn(), - setRequestHeader: jest.fn(), - withCredentials: true, - abort: jest.fn(), - }; - - jest.spyOn(window, 'XMLHttpRequest').mockImplementation(() => xhrMock as XMLHttpRequest); + requests = []; + responseStatusCode = 200; + eventStore = newMockEventStore({ maxSize: maxQueueSize }); }); - const respondMockRequest = (status: number, statusText: string = '') => { - (xhrMock as any).status = status; - (xhrMock as any).response = ''; - (xhrMock as any).statusText = statusText; - (xhrMock as any).readyState = 4; - (xhrMock as any).onreadystatechange(); - }; - describe('POST requests', () => { - var outQueue: OutQueue; - - const getQueue = () => { - return JSON.parse( - window.localStorage.getItem('snowplowOutQueue_sp_post2') ?? fail('Unable to find local storage queue') - ); - }; + let outQueue: OutQueue; beforeEach(() => { - outQueue = OutQueueManager( - 'sp', - new SharedState(), - true, - 'post', - '/com.snowplowanalytics.snowplow/tp2', - 1, - 40000, - 0, // maxGetBytes – 0 for no limit - false, - maxQueueSize, - 5000, - false, - {}, - true, - [401], // retry status codes - override don't retry ones - [401, 505] // don't retry status codes + outQueue = newOutQueue( + { + endpoint: 'http://example.com', + trackerId: 'sp', + useStm: false, + retryStatusCodes: [401], // retry status codes - override don't retry ones + dontRetryStatusCodes: [401, 505], // don't retry status codes + eventStore, + customFetch, + }, + new SharedState() ); }); - it('should add event to outQueue and store event in local storage', () => { + it('should add event to outQueue and store event in local storage', async () => { const expected = { e: 'pv', eid: '20269f92-f07c-44a6-87ef-43e171305076' }; - outQueue.enqueueRequest(expected, 'http://example.com'); + outQueue.setBufferSize(5); + await outQueue.enqueueRequest(expected); - const retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(1); - expect(retrievedQueue[0]).toMatchObject({ bytes: 55, evt: expected }); + expect(await eventStore.count()).toEqual(1); + const events = await eventStore.getAllPayloads(); + expect(events[0]).toMatchObject(expected); }); - it('should add event to outQueue and store only events up to max local storage queue size in local storage', () => { + it('should add event to outQueue and store only events up to max local storage queue size in local storage', async () => { const expected1 = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; const expected2 = { e: 'pv', eid: '6000c7bd-08a6-49c2-b61c-9531d3d46200' }; const unexpected = { e: 'pv', eid: '7a3391a8-622b-4ce4-80ed-c941aa05baf5' }; - outQueue.enqueueRequest(expected1, 'http://example.com'); - outQueue.enqueueRequest(expected2, 'http://example.com'); - outQueue.enqueueRequest(unexpected, 'http://example.com'); + outQueue.setBufferSize(5); + await outQueue.enqueueRequest(unexpected); + await outQueue.enqueueRequest(expected1); + await outQueue.enqueueRequest(expected2); - const retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(maxQueueSize); - expect(retrievedQueue[0]).toMatchObject({ bytes: 55, evt: expected1 }); - expect(retrievedQueue[1]).toMatchObject({ bytes: 55, evt: expected2 }); + expect(await eventStore.count()).toEqual(maxQueueSize); + const events = await eventStore.getAllPayloads(); + expect(events[0]).toMatchObject(expected1); + expect(events[1]).toMatchObject(expected2); }); - it('should remove events from event queue on success', () => { + it('should remove events from event queue on success', async () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; - outQueue.enqueueRequest(request, 'http://example.com'); - - let retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(1); + await outQueue.enqueueRequest(request); - respondMockRequest(200); - retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(0); + expect(await eventStore.count()).toEqual(0); + expect(requests).toHaveLength(1); }); - it('should keep events in queue on failure', () => { + it('should keep events in queue on failure', async () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; - outQueue.enqueueRequest(request, 'http://example.com'); + responseStatusCode = 500; + await outQueue.enqueueRequest(request); - let retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(1); - - respondMockRequest(500); - retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(1); + expect(await eventStore.count()).toEqual(1); + expect(requests).toHaveLength(1); }); - it('should retry on custom retry status code', () => { + it('should retry on custom retry status code', async () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; - outQueue.enqueueRequest(request, 'http://example.com'); - - let retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(1); + responseStatusCode = 401; + await outQueue.enqueueRequest(request); - respondMockRequest(401); - retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(1); + expect(requests).toHaveLength(1); + expect(await eventStore.count()).toEqual(1); }); - it("should not retry on custom don't retry status code", () => { + it("should not retry on custom don't retry status code", async () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; - outQueue.enqueueRequest(request, 'http://example.com'); - - let retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(1); + responseStatusCode = 505; + await outQueue.enqueueRequest(request); - respondMockRequest(505); - retrievedQueue = getQueue(); - expect(retrievedQueue).toHaveLength(0); + expect(requests).toHaveLength(1); + expect(await eventStore.count()).toEqual(0); }); }); describe('GET requests', () => { - var getOutQueue: (maxGetBytes: number) => OutQueue; + let getOutQueue: (maxGetBytes?: number) => OutQueue; beforeEach(() => { getOutQueue = (maxGetBytes) => - OutQueueManager( - 'sp', - new SharedState(), - true, - 'get', - '/com.snowplowanalytics.snowplow/tp2', - 1, - 40000, - maxGetBytes, - false, - maxQueueSize, - 5000, - false, - {}, - true, - [], - [] + newOutQueue( + { + endpoint: 'http://example.com', + trackerId: 'sp', + eventMethod: 'get', + maxGetBytes, + useStm: false, + maxLocalStorageQueueSize: maxQueueSize, + eventStore, + customFetch, + }, + new SharedState() ); }); - it('should add large event to out queue without bytes limit', () => { - var outQueue = getOutQueue(0); + it('should add large event to out queue without bytes limit', async () => { + let outQueue = getOutQueue(undefined); const expected = { e: 'pv', eid: '20269f92-f07c-44a6-87ef-43e171305076', aid: 'x'.repeat(1000) }; - outQueue.enqueueRequest(expected, ''); + await outQueue.enqueueRequest(expected); - const retrievedQueue = JSON.parse( - window.localStorage.getItem('snowplowOutQueue_sp_get') ?? fail('Unable to find local storage queue') - ); - expect(retrievedQueue).toHaveLength(1); - expect(retrievedQueue[0]).toEqual(getQuerystring(expected)); + expect(eventStore.addCount()).toEqual(1); + expect(await eventStore.count()).toEqual(0); + expect(requests).toHaveLength(1); }); - it('should not add event larger than max bytes limit to queue but should try to send it as POST', () => { - var outQueue = getOutQueue(100); + it('should not add event larger than max bytes limit to queue but should try to send it as POST', async () => { + let outQueue = getOutQueue(100); const consoleWarn = jest.fn(); global.console.warn = consoleWarn; const expected = { e: 'pv', eid: '20269f92-f07c-44a6-87ef-43e171305076', aid: 'x'.repeat(1000) }; - outQueue.enqueueRequest(expected, 'http://acme.com'); + outQueue.enqueueRequest(expected); - expect(window.localStorage.getItem('snowplowOutQueue_sp_get')).toBeNull; // should not save to local storage + expect(eventStore.addCount()).toEqual(0); + expect(await eventStore.count()).toEqual(0); expect(consoleWarn.mock.calls.length).toEqual(1); // should log a warning message - expect(xhrOpenMock).toHaveBeenCalledWith('POST', 'http://acme.com/com.snowplowanalytics.snowplow/tp2', true); // should make the POST request + expect(requests).toHaveLength(1); }); }); describe('idService requests', () => { const idServiceEndpoint = 'http://example.com/id'; - const readGetQueue = () => - JSON.parse(window.localStorage.getItem('snowplowOutQueue_sp_get') ?? fail('Unable to find local storage queue')); - const getQuerystring = (p: object) => '?' + Object.entries(p) @@ -243,193 +203,122 @@ describe('OutQueueManager', () => { .join('&'); describe('GET requests', () => { - const createGetQueue = () => - OutQueueManager( - 'sp', - new SharedState(), - true, - 'get', - '/com.snowplowanalytics.snowplow/tp2', - 1, - 40000, - 0, - false, - maxQueueSize, - 5000, - false, - {}, - true, - [], - [], - idServiceEndpoint - ); + let createGetQueue = () => newOutQueue( + { + endpoint: 'http://example.com', + trackerId: 'sp', + eventMethod: 'get', + useStm: false, + maxLocalStorageQueueSize: maxQueueSize, + eventStore, + customFetch, + idService: idServiceEndpoint, + }, + new SharedState() + ); + - it('should first execute the idService request and in the same `enqueueRequest` the tracking request', () => { + it('should first execute the idService request and in the same `enqueueRequest` the tracking request', async () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; const getQueue = createGetQueue(); - getQueue.enqueueRequest(request, 'http://example.com'); - - let retrievedQueue = readGetQueue(); - expect(retrievedQueue).toHaveLength(1); - /* The first XHR is for the idService */ - respondMockRequest(200); - retrievedQueue = readGetQueue(); - expect(retrievedQueue).toHaveLength(1); - expect(retrievedQueue[0]).toEqual(getQuerystring(request)); - /* The second XHR is the event request */ - respondMockRequest(200); - retrievedQueue = readGetQueue(); - expect(retrievedQueue).toHaveLength(0); + await getQueue.enqueueRequest(request); + + expect(requests).toHaveLength(2); + expect(requests[0].url).toEqual(idServiceEndpoint); + expect(requests[1].url).toEqual('http://example.com/i' + getQuerystring(request)); }); - it('should first execute the idService request and in the same `enqueueRequest` the tracking request irregardless of failure of the idService endpoint', () => { + it('should first execute the idService request and in the same `enqueueRequest` the tracking request irregardless of failure of the idService endpoint', async () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; const getQueue = createGetQueue(); - getQueue.enqueueRequest(request, 'http://example.com'); - - let retrievedQueue = readGetQueue(); - expect(retrievedQueue).toHaveLength(1); - /* The first XHR is for the idService */ - respondMockRequest(500); - retrievedQueue = readGetQueue(); - expect(retrievedQueue).toHaveLength(1); - expect(retrievedQueue[0]).toEqual(getQuerystring(request)); - /* The second XHR is the event request */ - respondMockRequest(200); - retrievedQueue = readGetQueue(); - expect(retrievedQueue).toHaveLength(0); + responseStatusCode = 500; + await getQueue.enqueueRequest(request); + + expect(requests).toHaveLength(2); + expect(requests[0].url).toEqual(idServiceEndpoint); + expect(requests[1].url).toEqual('http://example.com/i' + getQuerystring(request)); + expect(await eventStore.count()).toEqual(1); }); }); describe('POST requests', () => { - const createPostQueue = () => - OutQueueManager( - 'sp', - new SharedState(), - true, - 'post', - '/com.snowplowanalytics.snowplow/tp2', - 1, - 40000, - 0, - false, - maxQueueSize, - 5000, - false, - {}, - true, - [], - [], - idServiceEndpoint - ); + let createPostQueue = () => newOutQueue( + { + endpoint: 'http://example.com', + trackerId: 'sp', + eventMethod: 'post', + eventStore, + customFetch, + idService: idServiceEndpoint, + }, + new SharedState() + ); - it('should first execute the idService request and in the same `enqueueRequest` the tracking request irregardless of failure of the idService endpoint', () => { + it('should first execute the idService request and in the same `enqueueRequest` the tracking request irregardless of failure of the idService endpoint', async () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; const postQueue = createPostQueue(); - postQueue.enqueueRequest(request, 'http://example.com'); - - let retrievedQueue = readPostQueue(); - expect(retrievedQueue).toHaveLength(1); - /* The first XHR is for the idService */ - respondMockRequest(500); - retrievedQueue = readPostQueue(); - expect(retrievedQueue).toHaveLength(1); - expect(retrievedQueue[0].evt).toEqual(request); - /* The second XHR is the event request */ - respondMockRequest(200); - retrievedQueue = readPostQueue(); - expect(retrievedQueue).toHaveLength(0); + await postQueue.enqueueRequest(request); + + expect(requests).toHaveLength(2); + expect(requests[0].url).toEqual(idServiceEndpoint); + expect(requests[1].url).toEqual('http://example.com/com.snowplowanalytics.snowplow/tp2'); }); }); }); describe('retryFailures = true', () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; - let createOutQueue = () => - OutQueueManager( - 'sp', - new SharedState(), - true, - 'post', - '/com.snowplowanalytics.snowplow/tp2', - 1, - 40000, - 0, - false, - maxQueueSize, - 10, - false, - {}, - true, - [], - [], - '', - true - ); + let createOutQueue = () => newOutQueue( + { + endpoint: 'http://example.com', + trackerId: 'sp', + eventMethod: 'post', + retryFailedRequests: true, + eventStore, + customFetch, + }, + new SharedState() + ); - it('should remain in queue on failure', (done) => { + it('should remain in queue on failure', async () => { let outQueue = createOutQueue(); - outQueue.enqueueRequest(request, 'http://example.com'); + responseStatusCode = 500; + await outQueue.enqueueRequest(request); - let retrievedQueue = readPostQueue(); - expect(retrievedQueue).toHaveLength(1); - - respondMockRequest(0); - - setTimeout(() => { - retrievedQueue = readPostQueue(); - expect(retrievedQueue).toHaveLength(1); - done(); - }, 20); + expect(requests).toHaveLength(1); + expect(await eventStore.count()).toEqual(1); }); }); describe('retryFailures = false', () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; - let createOutQueue = () => - OutQueueManager( - 'sp', - new SharedState(), - true, - 'post', - '/com.snowplowanalytics.snowplow/tp2', - 1, - 40000, - 0, - false, - maxQueueSize, - 0, - false, - {}, - true, - [], - [], - '', - false - ); + let createOutQueue = () => newOutQueue( + { + endpoint: 'http://example.com', + trackerId: 'sp', + eventMethod: 'post', + retryFailedRequests: false, + eventStore, + customFetch, + }, + new SharedState() + ); - it('should remove from queue on failure', (done) => { + it('should remove from queue on failure', async () => { let outQueue = createOutQueue(); - outQueue.enqueueRequest(request, 'http://example.com'); - - let retrievedQueue = readPostQueue(); - expect(retrievedQueue).toHaveLength(1); + responseStatusCode = 500; + await outQueue.enqueueRequest(request); - respondMockRequest(0); - - setTimeout(() => { - retrievedQueue = readPostQueue(); - expect(retrievedQueue).toHaveLength(0); - done(); - }, 20); + expect(requests).toHaveLength(1); + expect(await eventStore.count()).toEqual(0); }); }); type createQueueArgs = { - method: string; + method: 'get' | 'post'; onSuccess?: (data: EventBatch) => void; onFailure?: (data: RequestFailure) => void; maxPostBytes?: number; @@ -437,117 +326,114 @@ describe('OutQueueManager', () => { }; const createQueue = (args: createQueueArgs) => - OutQueueManager( - 'sp', - new SharedState(), - true, - args.method, - '/com.snowplowanalytics.snowplow/tp2', - 1, - args.maxPostBytes ?? 40000, - args.maxGetBytes ?? 0, - true, - maxQueueSize, - 5000, - false, - {}, - true, - [], - [], - '', - false, - args.onSuccess, - args.onFailure + newOutQueue( + { + endpoint: 'http://example.com', + trackerId: 'sp', + eventMethod: args.method, + maxPostBytes: args.maxPostBytes, + maxGetBytes: args.maxGetBytes, + maxLocalStorageQueueSize: maxQueueSize, + onRequestSuccess: args.onSuccess, + onRequestFailure: args.onFailure, + customFetch, + eventStore, + }, + new SharedState() ); describe('onRequestSuccess', () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; describe('POST requests', () => { - const method = 'POST'; + const method = 'post'; - it('should fire on a successful request', () => { - const callbackStorage: EventBatch[] = []; - const onSuccess = (e: EventBatch) => { - callbackStorage.push(e); - }; + it('should fire on a successful request', async () => { + const callbacks: EventBatch[] = []; - const postQueue = createQueue({ method, onSuccess }); - postQueue.enqueueRequest(request, 'http://example.com'); + await new Promise(async (resolve) => { + const onSuccess = (e: EventBatch) => { + callbacks.push(e); + resolve(null); + }; - expect(readPostQueue()).toHaveLength(1); + const postQueue = createQueue({ method, onSuccess }); + await postQueue.enqueueRequest(request); + }); - respondMockRequest(200); + expect(requests).toHaveLength(1); + expect(eventStore.addCount()).toEqual(1); + expect(callbacks).toHaveLength(1); + expect(callbacks[0]).toHaveLength(1); - expect(readPostQueue()).toHaveLength(0); - expect(callbackStorage).toHaveLength(1); - - let dataFromCallback = callbackStorage[0][0] as Record; - expect(dataFromCallback.e).toEqual(request.e); - expect(dataFromCallback.eid).toEqual(request.eid); - expect(dataFromCallback.stm).toMatch(/\d{13}/); + let dataFromCallback = callbacks[0][0]; + expect(dataFromCallback).toEqual(request); }); // Oversized events don't get placed in the queue, but the callback should still fire - it('should fire on a successful oversized request', () => { + it('should fire on a successful oversized request', async () => { const callbackStorage: EventBatch[] = []; - const onSuccess = (e: EventBatch) => { - callbackStorage.push(e); - }; - - const postQueue = createQueue({ method, onSuccess, maxPostBytes: 1 }); - postQueue.enqueueRequest(request, 'http://example.com'); - - respondMockRequest(200); - - expect(callbackStorage).toHaveLength(1); - - let dataFromCallback = callbackStorage[0][0] as Record; - expect(dataFromCallback.e).toEqual(request.e); - expect(dataFromCallback.eid).toEqual(request.eid); - expect(dataFromCallback.stm).toMatch(/\d{13}/); + await new Promise(async (resolve) => { + const onSuccess = (e: EventBatch) => { + callbackStorage.push(e); + resolve(null); + }; + + const postQueue = createQueue({ method, onSuccess, maxPostBytes: 1 }); + await postQueue.enqueueRequest(request); + }); + + expect(requests).toHaveLength(1); + expect(eventStore.addCount()).toEqual(0); + let dataFromCallback = callbackStorage[0][0]; + expect(dataFromCallback).toEqual(request); }); }); describe('GET requests', () => { - const method = 'GET'; + const method = 'get'; - it('should fire on a successful request', () => { + it('should fire on a successful request', async () => { let callbackStorage: EventBatch[] = []; - const onSuccess = (e: EventBatch) => { - callbackStorage.push(e); - }; - - const getQueue = createQueue({ method, onSuccess }); - getQueue.enqueueRequest(request, 'http://example.com'); - - expect(readGetQueue()).toHaveLength(1); - - respondMockRequest(200); - - expect(readGetQueue()).toHaveLength(0); + await new Promise(async (resolve) => { + const onSuccess = (e: EventBatch) => { + callbackStorage.push(e); + resolve(null); + }; + + const getQueue = createQueue({ method, onSuccess }); + await getQueue.enqueueRequest(request); + }); + + expect(requests).toHaveLength(1); + expect(eventStore.addCount()).toEqual(1); expect(callbackStorage).toHaveLength(1); + expect(callbackStorage[0]).toHaveLength(1); - let dataFromCallback = callbackStorage[0][0] as string; - expect(dataFromCallback).toMatch(/\?stm=\d{13}&e=pv&eid=65cb78de-470c-4764-8c10-02bd79477a3a/); + let dataFromCallback = callbackStorage[0][0]; + expect(dataFromCallback).toEqual(request); }); // A single oversized events means no queue, but the callback should still fire - it('should fire the onRequestSuccess on a successful oversized request', () => { + it('should fire the onRequestSuccess on a successful oversized request', async () => { let callbackStorage: EventBatch[] = []; - const onSuccess = (e: EventBatch) => { - callbackStorage.push(e); - }; - - const getQueue = createQueue({ method, onSuccess }); - getQueue.enqueueRequest(request, 'http://example.com'); - - respondMockRequest(200); - + await new Promise(async (resolve) => { + const onSuccess = (e: EventBatch) => { + callbackStorage.push(e); + resolve(null); + }; + + const getQueue = createQueue({ method, onSuccess, maxGetBytes: 1 }); + await getQueue.enqueueRequest(request); + }); + + expect(requests).toHaveLength(1); + expect(eventStore.addCount()).toEqual(0); expect(callbackStorage).toHaveLength(1); + expect(callbackStorage[0]).toHaveLength(1); - let dataFromCallback = callbackStorage[0][0] as string; - expect(dataFromCallback).toMatch(/\?stm=\d{13}&e=pv&eid=65cb78de-470c-4764-8c10-02bd79477a3a/); + let dataFromCallback = callbackStorage[0][0]; + expect(dataFromCallback).toEqual(request); }); }); }); @@ -555,137 +441,131 @@ describe('OutQueueManager', () => { describe('onRequestFailure', () => { const request = { e: 'pv', eid: '65cb78de-470c-4764-8c10-02bd79477a3a' }; - const createQueue = (args: createQueueArgs) => - OutQueueManager( - 'sp', - new SharedState(), - true, - args.method, - '/com.snowplowanalytics.snowplow/tp2', - 1, - 40000, - 0, - true, - maxQueueSize, - 5000, - false, - {}, - true, - [], - [500], - '', - false, - args.onSuccess, - args.onFailure - ); + const createQueue = (args: createQueueArgs) => + newOutQueue( + { + endpoint: 'http://example.com', + trackerId: 'sp', + eventMethod: args.method, + maxPostBytes: args.maxPostBytes, + maxGetBytes: args.maxGetBytes, + maxLocalStorageQueueSize: maxQueueSize, + onRequestSuccess: args.onSuccess, + onRequestFailure: args.onFailure, + dontRetryStatusCodes: [500], + customFetch, + eventStore, + }, + new SharedState() + ); describe('POST requests', () => { - const method = 'POST'; + const method = 'post'; - it('should fire on a failed request', () => { + it('should fire on a failed request', async () => { const callbackStorage: RequestFailure[] = []; - const onFailure = (e: RequestFailure) => { - callbackStorage.push(e); - }; + await new Promise(async (resolve) => { + const onFailure = (e: RequestFailure) => { + callbackStorage.push(e); + resolve(null); + }; - const postQueue = createQueue({ method, onFailure }); - postQueue.enqueueRequest(request, 'http://example.com'); + responseStatusCode = 500; - expect(readPostQueue()).toHaveLength(1); + const postQueue = createQueue({ method, onFailure }); + await postQueue.enqueueRequest(request); + }); - respondMockRequest(500, 'Internal Server Error'); - - expect(readPostQueue()).toHaveLength(0); + expect(requests).toHaveLength(1); expect(callbackStorage).toHaveLength(1); + expect(await eventStore.count()).toEqual(0); let dataFromCallback = callbackStorage[0] as RequestFailure; - const event = dataFromCallback.events[0] as Record; - expect(event.e).toEqual(request.e); - expect(event.eid).toEqual(request.eid); - expect(event.stm).toMatch(/\d{13}/); + const event = dataFromCallback.events[0]; + expect(event).toEqual(request); expect(dataFromCallback.status).toEqual(500); - expect(dataFromCallback.message).toEqual('Internal Server Error'); expect(dataFromCallback.willRetry).toEqual(false); }); // A single oversized events means no queue, but the callback should still fire - it('should fire on a failed oversized request', () => { + it('should fire on a failed oversized request', async () => { const callbackStorage: RequestFailure[] = []; - const onFailure = (e: RequestFailure) => { - callbackStorage.push(e); - }; + await new Promise(async (resolve) => { + const onFailure = (e: RequestFailure) => { + callbackStorage.push(e); + resolve(null); + }; - const postQueue = createQueue({ method, onFailure, maxPostBytes: 1 }); - postQueue.enqueueRequest(request, 'http://example.com'); + responseStatusCode = 501; - respondMockRequest(0, 'Request failed'); + const postQueue = createQueue({ method, onFailure, maxPostBytes: 1 }); + await postQueue.enqueueRequest(request); + }); + expect(requests).toHaveLength(1); expect(callbackStorage).toHaveLength(1); + expect(eventStore.addCount()).toEqual(0); + expect(await eventStore.count()).toEqual(0); - let dataFromCallback = callbackStorage[0].events[0] as Record; - expect(dataFromCallback.e).toEqual(request.e); - expect(dataFromCallback.eid).toEqual(request.eid); - - // The payload will have had `stm` added to it - expect(dataFromCallback.stm).toMatch(/\d{13}/); - expect(callbackStorage[0].status).toEqual(0); - expect(callbackStorage[0].message).toEqual('Request failed'); + let dataFromCallback = callbackStorage[0].events[0]; + expect(dataFromCallback).toEqual(request); + expect(callbackStorage[0].status).toEqual(501); }); }); describe('GET requests', () => { - const method = 'GET'; + const method = 'get'; - it('should fire on a failed request', () => { + it('should fire on a failed request', async () => { let callbackStorage: RequestFailure[] = []; - const onFailure = (e: RequestFailure) => { - callbackStorage.push(e); - }; + await new Promise(async (resolve) => { + const onFailure = (e: RequestFailure) => { + callbackStorage.push(e); + resolve(null); + }; - const getQueue = createQueue({ method, onFailure }); - getQueue.enqueueRequest(request, 'http://example.com'); + responseStatusCode = 500; - expect(readGetQueue()).toHaveLength(1); + const getQueue = createQueue({ method, onFailure }); + await getQueue.enqueueRequest(request); + }); - respondMockRequest(500, 'Internal Server Error'); - - expect(readGetQueue()).toHaveLength(0); + expect(requests).toHaveLength(1); expect(callbackStorage).toHaveLength(1); + expect(await eventStore.count()).toEqual(0); let dataFromCallback = callbackStorage[0] as RequestFailure; - expect(dataFromCallback.events[0]).toMatch(/\?stm=\d{13}&e=pv&eid=65cb78de-470c-4764-8c10-02bd79477a3a/); - + expect(dataFromCallback.events[0]).toEqual(request); expect(dataFromCallback.status).toEqual(500); - expect(dataFromCallback.message).toEqual('Internal Server Error'); expect(dataFromCallback.willRetry).toEqual(false); }); // A single oversized events means no queue, but the callback should still fire - it('should fire on a failed oversized request', () => { + it('should fire on a failed oversized request', async () => { let callbackStorage: RequestFailure[] = []; - const onFailure = (e: RequestFailure) => { - callbackStorage.push(e); - }; + await new Promise(async (resolve) => { + const onFailure = (e: RequestFailure) => { + callbackStorage.push(e); + resolve(null); + }; - const getQueue = createQueue({ method, onFailure, maxPostBytes: 1 }); - getQueue.enqueueRequest(request, 'http://example.com'); + responseStatusCode = 500; - expect(readGetQueue()).toHaveLength(1); + const getQueue = createQueue({ method, onFailure, maxGetBytes: 1 }); + await getQueue.enqueueRequest(request); + }); - respondMockRequest(500, 'Internal Server Error'); - - expect(readGetQueue()).toHaveLength(0); + expect(requests).toHaveLength(1); expect(callbackStorage).toHaveLength(1); + expect(eventStore.addCount()).toEqual(0); - let dataFromCallback = callbackStorage[0] as RequestFailure; - - expect(dataFromCallback.events[0]).toMatch(/\?stm=\d{13}&e=pv&eid=65cb78de-470c-4764-8c10-02bd79477a3a/); + let dataFromCallback = callbackStorage[0]; + expect(dataFromCallback.events[0]).toEqual(request); expect(dataFromCallback.status).toEqual(500); - expect(dataFromCallback.message).toEqual('Internal Server Error'); expect(dataFromCallback.willRetry).toEqual(false); }); }); diff --git a/libraries/browser-tracker-core/test/tracker/cookie_storage.test.ts b/libraries/browser-tracker-core/test/tracker/cookie_storage.test.ts new file mode 100644 index 000000000..ce48e9bee --- /dev/null +++ b/libraries/browser-tracker-core/test/tracker/cookie_storage.test.ts @@ -0,0 +1,69 @@ +import { asyncCookieStorage, newCookieStorage, syncCookieStorage } from "../../src/tracker/cookie_storage"; + +test("cookieStorage sets, gets, and deletes value", () => { + const cookieStorage = newCookieStorage(); + cookieStorage.setCookie("test", "value"); + expect(cookieStorage.getCookie("test")).toBe("value"); + + cookieStorage.deleteCookie("test"); + expect(cookieStorage.getCookie("test")).toBeFalsy(); +}); + +test("cookieStorage sets value with ttl and clears cache after ttl", (done) => { + const cookieStorage = newCookieStorage(); + const ttl = 1; + cookieStorage.setCookie("test", "value", ttl); + + expect(cookieStorage.getCookie("test")).toBe("value"); + + setTimeout(() => { + expect(cookieStorage.getCookie("test")).toBeFalsy(); + done(); + }, ttl * 1000 + 100); +}); + +test("cookieStorage sets value with path, domain, samesite, and secure", () => { + const cookieStorage = newCookieStorage(); + const path = "/"; + const domain = "example.com"; + const samesite = "Strict"; + const secure = true; + + cookieStorage.setCookie("test", "value", undefined, path, domain, samesite, secure); + expect(cookieStorage.getCookie("test")).toBe("value"); +}); + +test("cookieStorage sets value with synchronous cookie write", () => { + const cookieStorage = syncCookieStorage; + cookieStorage.setCookie("test", "value"); + expect(cookieStorage.getCookie("test")).toBe("value"); +}); + +test("asyncCookieStorage flushes pending cookies", () => { + let cookieJar = ''; + + jest.spyOn(document, 'cookie', 'set').mockImplementation((cookieValue) => { + cookieJar = cookieValue; + }); + + asyncCookieStorage.setCookie("test", "value"); + expect(cookieJar).toBe(""); + asyncCookieStorage.flush(); + expect(cookieJar).toBe("test=value"); +}); + +test('writes the latest cookie value', (done) => { + let cookieJar = ''; + + jest.spyOn(document, 'cookie', 'set').mockImplementation((cookieValue) => { + cookieJar = cookieValue; + }); + + for (let i = 0; i < 100; i++) { + asyncCookieStorage.setCookie("test", `value${i}`); + } + setTimeout(() => { + expect(cookieJar).toBe('test=value99'); + done(); + }, 100); +}); diff --git a/libraries/tracker-core/package.json b/libraries/tracker-core/package.json index afe271ad5..b8c88d881 100644 --- a/libraries/tracker-core/package.json +++ b/libraries/tracker-core/package.json @@ -45,7 +45,7 @@ }, "dependencies": { "tslib": "^2.3.1", - "uuid": "^3.4.0" + "uuid": "^10.0.0" }, "devDependencies": { "@ampproject/rollup-plugin-closure-compiler": "~0.27.0", @@ -53,10 +53,10 @@ "@rollup/plugin-json": "~4.1.0", "@rollup/plugin-node-resolve": "~13.1.3", "@types/node": "~14.6.0", - "@types/uuid": "~3.4.6", + "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "~5.15.0", "@typescript-eslint/parser": "~5.15.0", - "ava": "~4.1.0", + "ava": "~5.1.1", "eslint": "~8.11.0", "jest-standard-reporter": "~2.0.0", "rollup": "~2.70.1", diff --git a/libraries/tracker-core/src/contexts.ts b/libraries/tracker-core/src/contexts.ts index bed5a9b0c..71718b296 100644 --- a/libraries/tracker-core/src/contexts.ts +++ b/libraries/tracker-core/src/contexts.ts @@ -116,7 +116,11 @@ export interface GlobalContexts { * Adds conditional or primitive global contexts * @param contexts - An Array of either Conditional Contexts or Primitive Contexts */ - addGlobalContexts(contexts: Array): void; + addGlobalContexts( + contexts: + | Array + | Record + ): void; /** * Removes all global contexts @@ -127,7 +131,7 @@ export interface GlobalContexts { * Removes previously added global context, performs a deep comparison of the contexts or conditional contexts * @param contexts - An Array of either Condition Contexts or Primitive Contexts */ - removeGlobalContexts(contexts: Array): void; + removeGlobalContexts(contexts: Array): void; /** * Returns all applicable global contexts for a specified event @@ -142,6 +146,8 @@ export interface GlobalContexts { export function globalContexts(): GlobalContexts { let globalPrimitives: Array = []; let conditionalProviders: Array = []; + let namedPrimitives: Record = {}; + let namedConditionalProviders: Record = {}; /** * Returns all applicable global contexts for a specified event @@ -152,10 +158,20 @@ export function globalContexts(): GlobalContexts { const eventSchema = getUsefulSchema(event); const eventType = getEventType(event); const contexts: Array = []; - const generatedPrimitives = generatePrimitives(globalPrimitives, event, eventType, eventSchema); + const generatedPrimitives = generatePrimitives( + globalPrimitives.concat(Object.values(namedPrimitives)), + event, + eventType, + eventSchema + ); contexts.push(...generatedPrimitives); - const generatedConditionals = generateConditionals(conditionalProviders, event, eventType, eventSchema); + const generatedConditionals = generateConditionals( + conditionalProviders.concat(Object.values(namedConditionalProviders)), + event, + eventType, + eventSchema + ); contexts.push(...generatedConditionals); return contexts; @@ -163,35 +179,54 @@ export function globalContexts(): GlobalContexts { return { getGlobalPrimitives(): Array { - return globalPrimitives; + return globalPrimitives.concat(Object.values(namedPrimitives)); }, getConditionalProviders(): Array { - return conditionalProviders; + return conditionalProviders.concat(Object.values(namedConditionalProviders)); }, - addGlobalContexts(contexts: Array): void { - const acceptedConditionalContexts: ConditionalContextProvider[] = []; - const acceptedContextPrimitives: ContextPrimitive[] = []; - for (const context of contexts) { - if (isConditionalContextProvider(context)) { - acceptedConditionalContexts.push(context); - } else if (isContextPrimitive(context)) { - acceptedContextPrimitives.push(context); + addGlobalContexts( + contexts: + | Array + | Record + ): void { + if (Array.isArray(contexts)) { + const acceptedConditionalContexts: ConditionalContextProvider[] = []; + const acceptedContextPrimitives: ContextPrimitive[] = []; + for (const context of contexts) { + if (isConditionalContextProvider(context)) { + acceptedConditionalContexts.push(context); + } else if (isContextPrimitive(context)) { + acceptedContextPrimitives.push(context); + } + } + globalPrimitives = globalPrimitives.concat(acceptedContextPrimitives); + conditionalProviders = conditionalProviders.concat(acceptedConditionalContexts); + } else { + for (const [name, context] of Object.entries(contexts)) { + if (isConditionalContextProvider(context)) { + namedConditionalProviders[name] = context; + } else if (isContextPrimitive(context)) { + namedPrimitives[name] = context; + } } } - globalPrimitives = globalPrimitives.concat(acceptedContextPrimitives); - conditionalProviders = conditionalProviders.concat(acceptedConditionalContexts); }, clearGlobalContexts(): void { conditionalProviders = []; globalPrimitives = []; + namedConditionalProviders = {}; + namedPrimitives = {}; }, - removeGlobalContexts(contexts: Array): void { + removeGlobalContexts(contexts: Array): void { for (const context of contexts) { - if (isConditionalContextProvider(context)) { + if (typeof context === 'string') { + delete namedConditionalProviders[context]; + delete namedPrimitives[context]; + } else if (isConditionalContextProvider(context)) { conditionalProviders = conditionalProviders.filter((item) => !compareProvider(context, item)); } else if (isContextPrimitive(context)) { globalPrimitives = globalPrimitives.filter((item) => !compareProvider(context, item)); @@ -209,7 +244,9 @@ export interface PluginContexts { /** * Returns list of contexts from all active plugins */ - addPluginContexts: (additionalContexts?: SelfDescribingJson[] | null) => SelfDescribingJson[]; + addPluginContexts: >( + additionalContexts?: SelfDescribingJson[] | null + ) => SelfDescribingJson[]; } export function pluginContexts(plugins: Array): PluginContexts { @@ -220,8 +257,10 @@ export function pluginContexts(plugins: Array): PluginContexts { * @returns userContexts combined with commonContexts */ return { - addPluginContexts: (additionalContexts?: SelfDescribingJson[] | null): SelfDescribingJson[] => { - const combinedContexts: SelfDescribingJson[] = additionalContexts ? [...additionalContexts] : []; + addPluginContexts: >(additionalContexts?: SelfDescribingJson[] | null) => { + const combinedContexts: SelfDescribingJson>[] = additionalContexts + ? [...additionalContexts] + : []; plugins.forEach((plugin) => { try { @@ -233,7 +272,7 @@ export function pluginContexts(plugins: Array): PluginContexts { } }); - return combinedContexts; + return combinedContexts as SelfDescribingJson[]; }, }; } diff --git a/libraries/tracker-core/src/core.ts b/libraries/tracker-core/src/core.ts index b4072e29f..0dfff0d59 100644 --- a/libraries/tracker-core/src/core.ts +++ b/libraries/tracker-core/src/core.ts @@ -45,7 +45,7 @@ import { LOG } from './logger'; * Export interface for any Self-Describing JSON such as context or Self Describing events * @typeParam T - The type of the data object within a SelfDescribingJson */ -export type SelfDescribingJson = Record> = { +export type SelfDescribingJson> = { /** * The schema string * @example 'iglu:com.snowplowanalytics.snowplow/web_page/jsonschema/1-0-0' @@ -54,14 +54,14 @@ export type SelfDescribingJson = Record = Record> = { +export type SelfDescribingJsonArray> = { /** * The schema string * @example 'iglu:com.snowplowanalytics.snowplow/contexts/jsonschema/1-0-1' @@ -70,7 +70,7 @@ export type SelfDescribingJsonArray = Record< /** * The data array which should conform to the supplied schema */ - data: Array; + data: (T extends SelfDescribingJson ? T : SelfDescribingJson)[]; }; /** @@ -138,7 +138,7 @@ export interface TrackerCore { * @param pb - Payload * @param context - Custom contexts relating to the event * @param timestamp - Timestamp of the event - * @returns Payload after the callback is applied + * @returns Payload after the callback is applied or undefined if the event is skipped */ track: ( /** A PayloadBuilder created by one of the `buildX` functions */ @@ -147,7 +147,7 @@ export interface TrackerCore { context?: Array | null, /** Timestamp override */ timestamp?: Timestamp | null - ) => Payload; + ) => Payload | undefined; /** * Set a persistent key-value pair to be added to every payload @@ -273,7 +273,11 @@ export interface TrackerCore { * Adds contexts globally, contexts added here will be attached to all applicable events * @param contexts - An array containing either contexts or a conditional contexts */ - addGlobalContexts(contexts: Array): void; + addGlobalContexts( + contexts: + | Array + | Record + ): void; /** * Removes all global contexts @@ -284,7 +288,7 @@ export interface TrackerCore { * Removes previously added global context, performs a deep comparison of the contexts or conditional contexts * @param contexts - An array containing either contexts or a conditional contexts */ - removeGlobalContexts(contexts: Array): void; + removeGlobalContexts(contexts: Array): void; /** * Add a plugin into the plugin collection after Core has already been initialised @@ -376,13 +380,13 @@ export function trackerCore(configuration: CoreConfiguration = {}): TrackerCore * @param pb - Payload * @param context - Custom contexts relating to the event * @param timestamp - Timestamp of the event - * @returns Payload after the callback is applied + * @returns Payload after the callback is applied or undefined if the event is skipped */ - function track( + function track>( pb: PayloadBuilder, - context?: Array | null, + context?: Array> | null, timestamp?: Timestamp | null - ): Payload { + ): Payload | undefined { pb.withJsonProcessor(payloadJsonProcessor(encodeBase64)); pb.add('eid', uuid()); pb.addDict(payloadPairs); @@ -404,6 +408,19 @@ export function trackerCore(configuration: CoreConfiguration = {}): TrackerCore } }); + // Call the filter on plugins to determine if the event should be tracked + const skip = corePlugins.find((plugin) => { + try { + return plugin.filter && plugin.filter(pb.build()) === false; + } catch (ex) { + LOG.error('Plugin filter', ex); + return false; + } + }); + if (skip) { + return undefined; + } + if (typeof callback === 'function') { callback(pb); } @@ -548,9 +565,9 @@ export function trackerCore(configuration: CoreConfiguration = {}): TrackerCore * A custom event type, allowing for an event to be tracked using your own custom schema * and a data object which conforms to the supplied schema */ -export interface SelfDescribingEvent { +export interface SelfDescribingEvent> { /** The Self Describing JSON which describes the event */ - event: SelfDescribingJson; + event: SelfDescribingJson; } /** @@ -561,7 +578,7 @@ export interface SelfDescribingEvent { * @param event - Contains the properties and schema location for the event * @returns PayloadBuilder to be sent to {@link @snowplow/tracker-core#TrackerCore.track} */ -export function buildSelfDescribingEvent(event: SelfDescribingEvent): PayloadBuilder { +export function buildSelfDescribingEvent>(event: SelfDescribingEvent): PayloadBuilder { const { event: { schema, data }, } = event, diff --git a/libraries/tracker-core/src/emitter/emitter_event.ts b/libraries/tracker-core/src/emitter/emitter_event.ts new file mode 100644 index 000000000..1f0db2899 --- /dev/null +++ b/libraries/tracker-core/src/emitter/emitter_event.ts @@ -0,0 +1,160 @@ +import { EventStorePayload } from "../event_store_payload"; +import { Payload } from "../payload"; + +/** + * Wraps a payload and provides methods to get the payload ready for a GET or POST request + */ +export interface EmitterEvent { + /** + * Get the original payload + */ + getPayload: () => Payload; + /** + * Get the server anonymization setting + * @returns true if the server should anonymize the IP address + */ + getServerAnonymization: () => boolean; + /** + * Prepare the payload for a POST request + */ + getPOSTRequestBody: () => Record; + /** + * Calculate the byte size of the payload when POSTed + */ + getPOSTRequestBytesCount: () => number; + /** + * Get the URL for a GET request + */ + getGETRequestURL: (collectorUrl: string, useStm: boolean) => string; + /** + * Calculate the byte size of the payload when sent via GET + */ + getGETRequestBytesCount: () => number; +} + +/** + * Count the number of bytes a string will occupy when UTF-8 encoded + * Taken from http://stackoverflow.com/questions/2848462/count-bytes-in-textarea-using-javascript/ + * + * @param s - The string + * @returns number Length of s in bytes when UTF-8 encoded + */ +function getUTF8Length(s: string) { + let len = 0; + for (let i = 0; i < s.length; i++) { + const code = s.charCodeAt(i); + if (code <= 0x7f) { + len += 1; + } else if (code <= 0x7ff) { + len += 2; + } else if (code >= 0xd800 && code <= 0xdfff) { + // Surrogate pair: These take 4 bytes in UTF-8 and 2 chars in UCS-2 + // (Assume next char is the other [valid] half and just skip it) + len += 4; + i++; + } else if (code < 0xffff) { + len += 3; + } else { + len += 4; + } + } + return len; +} + +/* + * Convert a dictionary to a querystring + * The context field is the last in the querystring + */ +function getQuerystring(request: Payload) { + let lowPriorityKeys: { [key: string]: boolean } = { co: true, cx: true }; + + let args: string[] = []; + + for (const key in request) { + if (request.hasOwnProperty(key) && !lowPriorityKeys[key]) { + args.push(key + '=' + encodeURIComponent(request[key] as string | number | boolean)); + } + } + + for (const contextKey in lowPriorityKeys) { + if (request.hasOwnProperty(contextKey) && lowPriorityKeys[contextKey]) { + args.push(contextKey + '=' + encodeURIComponent(request[contextKey] as string | number | boolean)); + } + } + + return '?' + args.join('&'); +} + +/* + * Convert numeric fields to strings to match payload_data schema + */ +function preparePostBody(request: Payload): Record { + const cleanedRequest = Object.keys(request) + .map<[string, unknown]>((k) => [k, request[k]]) + .reduce((acc, [key, value]) => { + acc[key] = (value as Object).toString(); + return acc; + }, {} as Record); + return cleanedRequest; +} + +export function newEmitterEvent(eventStorePayload: EventStorePayload): EmitterEvent { + let querystring: string | null = null; + let postBody: Record | null = null; + let byteCountGET: number | null = null; + let byteCountPOST: number | null = null; + + function getPayload(): Payload { + return eventStorePayload.payload; + } + + function getServerAnonymization(): boolean { + return eventStorePayload.svrAnon ?? false; + } + + function getCachedQuerystring(payload: Payload): string { + if (querystring === null) { + querystring = getQuerystring(payload); + } + return querystring; + } + + function getGETRequestURL(collectorUrl: string, useStm: boolean): string { + const querystring = getCachedQuerystring(getPayload()); + if (useStm) { + return collectorUrl + querystring.replace('?', '?stm=' + new Date().getTime() + '&'); + } + + return collectorUrl + querystring; + } + + function getGETRequestBytesCount(): number { + if (byteCountGET === null) { + const querystring = getCachedQuerystring(getPayload()); + byteCountGET = getUTF8Length(querystring); + } + return byteCountGET; + } + + function getPOSTRequestBody(): Record { + if (postBody === null) { + postBody = preparePostBody(getPayload()); + } + return postBody; + } + + function getPOSTRequestBytesCount(): number { + if (byteCountPOST === null) { + byteCountPOST = getUTF8Length(JSON.stringify(getPOSTRequestBody())); + } + return byteCountPOST; + } + return { + getPayload, + getServerAnonymization, + getGETRequestURL, + getGETRequestBytesCount, + getPOSTRequestBody, + getPOSTRequestBytesCount, + }; +} diff --git a/libraries/tracker-core/src/emitter/emitter_request.ts b/libraries/tracker-core/src/emitter/emitter_request.ts new file mode 100644 index 000000000..869dc9528 --- /dev/null +++ b/libraries/tracker-core/src/emitter/emitter_request.ts @@ -0,0 +1,251 @@ +import { PAYLOAD_DATA_SCHEMA } from '../schemata'; +import { EmitterEvent } from "./emitter_event"; + +/** + * Wrapper around a request with events to the collector. + * Provides helpers to manage the request and its events. + * Prepare the request to be sent to the collector. + */ +export interface EmitterRequest { + /** + * Add an event to the request + * @returns true if the event was added, false if the server anonymization setting does not match the existing events + */ + addEvent: (event: EmitterEvent) => boolean; + /** + * Get the events attached to the request + */ + getEvents: () => EmitterEvent[]; + /** + * Creates a fetch Request object from the events + */ + toRequest: () => Request | undefined; + /** + * Whether the request is full or events can still be added + */ + isFull: () => boolean; + /** + * Size of the request in bytes + */ + countBytes: () => number; + /** + * The number of events attached to the request + */ + countEvents: () => number; + /** + * Cancel timeout timer if it is still pending. + * If not successful, the request will be aborted. + * @param successful - Whether the request was successful + * @param reason - Reason for aborting the request + */ + closeRequest: (successful: boolean, reason?: string) => void; +} + +export interface EmitterRequestConfiguration { + endpoint: string; + port?: number; + protocol?: 'http' | 'https'; + eventMethod?: 'get' | 'post'; + customHeaders?: Record; + connectionTimeout?: number; + keepalive?: boolean; + postPath?: string; + useStm?: boolean; + maxPostBytes?: number, + credentials?: 'omit' | 'same-origin' | 'include'; +} + +/** + * Enclose an array of events in a self-describing payload_data JSON string + * + * @param array - events Batch of events + * @returns string payload_data self-describing JSON + */ +export function encloseInPayloadDataEnvelope(events: Array>) { + return JSON.stringify({ + schema: PAYLOAD_DATA_SCHEMA, + data: events, + }); +} + +/** + * Attaches the STM field to outbound POST events. + * + * @param events - the events to attach the STM to + */ +export function attachStmToEvent(events: Array>) { + const stm = new Date().getTime().toString(); + for (let i = 0; i < events.length; i++) { + events[i]['stm'] = stm; + } + return events; +} + +export function newEmitterRequest({ + endpoint, + protocol = 'https', + port, + eventMethod = 'post', + customHeaders, + connectionTimeout, + keepalive = false, + postPath = '/com.snowplowanalytics.snowplow/tp2', + useStm = true, + maxPostBytes = 40000, + credentials = 'include', +}: EmitterRequestConfiguration): EmitterRequest { + let events: EmitterEvent[] = []; + let usePost = eventMethod.toLowerCase() === 'post'; + let timer: ReturnType | undefined; + let abortController: AbortController | undefined; + + function countBytes(): number { + let count = events.reduce( + (acc, event) => acc + (usePost ? event.getPOSTRequestBytesCount() : event.getGETRequestBytesCount()), + 0 + ); + if (usePost) { + count += 88; // 88 bytes for the payload_data envelope + } + return count; + } + + function countEvents(): number { + return events.length; + } + + function getServerAnonymizationOfExistingEvents(): boolean | undefined { + return events.length > 0 ? events[0].getServerAnonymization() : undefined; + } + + function addEvent(event: EmitterEvent) { + if (events.length > 0 && getServerAnonymizationOfExistingEvents() !== event.getServerAnonymization()) { + return false; + } else { + events.push(event); + return true; + } + } + + function getEvents(): EmitterEvent[] { + return events; + } + + function isFull(): boolean { + if (usePost) { + return countBytes() >= maxPostBytes; + } else { + return events.length >= 1; + } + } + + function createHeaders(): Headers { + const headers = new Headers(); + if (usePost) { + headers.append('Content-Type', 'application/json; charset=UTF-8'); + } + if (customHeaders) { + Object.keys(customHeaders).forEach((key) => { + headers.append(key, customHeaders[key]); + }); + } + if (getServerAnonymizationOfExistingEvents()) { + headers.append('SP-Anonymous', '*'); + } + return headers; + } + + function getFullCollectorUrl(): string { + let collectorUrl = endpoint; + if (!endpoint.includes('://')) { + collectorUrl = `${protocol}://${endpoint}`; + } + if (port) { + collectorUrl = `${collectorUrl}:${port}`; + } + + const path = usePost ? postPath : '/i'; + return collectorUrl + path; + } + + function makeRequest(url: string, options: RequestInit): Request { + closeRequest(false); + + abortController = new AbortController(); + timer = setTimeout(() => { + const reason = 'Request timed out'; + console.error(reason); + timer = undefined; + closeRequest(false, reason); + }, connectionTimeout ?? 5000); + + const requestOptions: RequestInit = { + headers: createHeaders(), + signal: abortController.signal, + keepalive, + credentials, + ...options, + }; + + const request = new Request(url, requestOptions); + return request; + } + + function makePostRequest(): Request { + const batch = attachStmToEvent(events.map((event) => event.getPOSTRequestBody())); + + return makeRequest(getFullCollectorUrl(), { + method: 'POST', + body: encloseInPayloadDataEnvelope(batch), + }); + } + + function makeGetRequest(): Request { + if (events.length !== 1) { + throw new Error('Only one event can be sent in a GET request'); + } + + const event = events[0]; + const url = event.getGETRequestURL(getFullCollectorUrl(), useStm); + + return makeRequest(url, { + method: 'GET', + }); + } + + function toRequest(): Request | undefined { + if (events.length === 0) { + return undefined; + } + if (usePost) { + return makePostRequest(); + } else { + return makeGetRequest(); + } + } + + function closeRequest(successful: boolean, reason?: string) { + if (timer !== undefined) { + clearTimeout(timer); + timer = undefined; + } + + if (abortController !== undefined) { + const controller = abortController; + abortController = undefined; + if (!successful) { + controller.abort(reason); + } + } + } + + return { + addEvent, + getEvents, + toRequest, + countBytes, + countEvents, + isFull, + closeRequest, + }; +} diff --git a/libraries/tracker-core/src/emitter/index.ts b/libraries/tracker-core/src/emitter/index.ts new file mode 100644 index 000000000..ca142b7cf --- /dev/null +++ b/libraries/tracker-core/src/emitter/index.ts @@ -0,0 +1,446 @@ +import { EventStore, newInMemoryEventStore } from '../event_store'; +import { Payload } from '../payload'; +import { EmitterRequest, newEmitterRequest } from './emitter_request'; +import { EmitterEvent, newEmitterEvent } from './emitter_event'; +import { newEventStorePayload } from '../event_store_payload'; +import { LOG } from '../logger'; + +/** + * A collection of event payloads which are sent to the collector. + */ +export type EventBatch = Payload[]; + +/** + * The data that will be available to the `onRequestFailure` callback + */ +export type RequestFailure = { + /** The batch of events that failed to send */ + events: EventBatch; + /** The status code of the failed request */ + status?: number; + /** The error message of the failed request */ + message?: string; + /** Whether the tracker will retry the request */ + willRetry: boolean; +}; + +/* The supported methods which events can be sent with */ +export type EventMethod = 'post' | 'get'; + +export interface EmitterConfigurationBase { + /** + * The preferred technique to use to send events + * @defaultValue post + */ + eventMethod?: EventMethod; + /** + * The post path which events will be sent to. + * Ensure your collector is configured to accept events on this post path + * @defaultValue '/com.snowplowanalytics.snowplow/tp2' + */ + postPath?: string; + /** + * The amount of events that should be buffered before sending + * Recommended to leave as 1 to reduce change of losing events + * @defaultValue 1 on Web, 10 on Node + */ + bufferSize?: number; + /** + * The max size a POST request can be before the tracker will force send it + * Also dictates the max size of a POST request before a batch of events is split into multiple requests + * @defaultValue 40000 + */ + maxPostBytes?: number; + /** + * The max size a GET request (its complete URL) can be. Requests over this size will be tried as a POST request. + * @defaultValue unlimited + */ + maxGetBytes?: number; + /** + * Should the Sent Timestamp be attached to events. + * Only applies for GET events. + * @defaultValue true + */ + useStm?: boolean; + /** + * How long to wait before aborting requests to the collector + * @defaultValue 5000 (milliseconds) + */ + connectionTimeout?: number; + /** + * An object of key value pairs which represent headers to + * attach when sending a POST request, only works for POST + * @defaultValue `{}` + */ + customHeaders?: Record; + /** + * Controls whether or not the browser sends credentials (defaults to 'include') + * @defaultValue 'include' + */ + credentials?: 'omit' | 'same-origin' | 'include'; + /** + * Whether to retry failed requests to the collector. + * + * Failed requests are requests that failed due to + * [timeouts](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/timeout_event), + * [network errors](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/error_event), + * and [abort events](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort_event). + * + * Takes precedent over `retryStatusCodes` and `dontRetryStatusCodes`. + * + * @defaultValue true + */ + retryFailedRequests?: boolean; + /** + * List of HTTP response status codes for which events sent to Collector should be retried in future requests. + * Only non-success status codes are considered (greater or equal to 300). + * The retry codes are only considered for GET and POST requests. + * They take priority over the `dontRetryStatusCodes` option. + * By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422. + */ + retryStatusCodes?: number[]; + /** + * List of HTTP response status codes for which events sent to Collector should not be retried in future request. + * Only non-success status codes are considered (greater or equal to 300). + * The don't retry codes are only considered for GET and POST requests. + * By default, the tracker retries on all non-success status codes except for 400, 401, 403, 410, and 422 (these don't retry codes will remain even if you set your own `dontRetryStatusCodes` but can be changed using the `retryStatusCodes`). + */ + dontRetryStatusCodes?: number[]; + /** + * Id service full URL. This URL will be added to the queue and will be called using a GET method. + * This option is there to allow the service URL to be called in order to set any required identifiers e.g. extra cookies. + * + * The request respects the `anonymousTracking` option, including the SP-Anonymous header if needed, and any additional custom headers from the customHeaders option. + */ + idService?: string; + /** + * Indicates that the request should be allowed to outlive the webpage that initiated it. + * Enables collector requests to complete even if the page is closed or navigated away from. + * Note: Browsers put a limit on keepalive requests of 64KB. In case of multiple keepalive requests in parallel (may happen in case of multiple trackers), the limit is shared. + * @defaultValue false + */ + keepalive?: boolean; + /** + * Enables overriding the default fetch function with a custom implementation. + * @param input - Instance of Request + * @param options - Additional options for the request + * @returns A Promise that resolves to the Response. + */ + customFetch?: (input: Request, options?: RequestInit) => Promise; + /** + * A callback function to be executed whenever a request is successfully sent to the collector. + * In practice this means any request which returns a 2xx status code will trigger this callback. + * + * @param data - The event batch that was successfully sent + */ + onRequestSuccess?: (data: EventBatch, response: Response) => void; + /** + * A callback function to be executed whenever a request fails to be sent to the collector. + * This is the inverse of the onRequestSuccess callback, so any non 2xx status code will trigger this callback. + * + * @param data - The data associated with the event(s) that failed to send + */ + onRequestFailure?: (data: RequestFailure, response?: Response) => void; + /** + * Enables providing a custom EventStore implementation to store events before sending them to the collector. + */ + eventStore?: EventStore; +} + +export interface EmitterConfiguration extends EmitterConfigurationBase { + /* The collector URL to which events will be sent */ + endpoint: string; + /* http or https. Defaults to https */ + protocol?: 'http' | 'https'; + /* Collector port number */ + port?: number; + /* If the request should undergo server anonymization. */ + serverAnonymization?: boolean; +} + +/** + * Emitter is responsible for sending events to the collector. + * It manages the event queue and sends events in batches depending on configuration. + */ +export interface Emitter { + /** + * Forces the emitter to send all events in the event store to the collector. + * @returns A Promise that resolves when all events have been sent to the collector. + */ + flush: () => Promise; + /** + * Adds a payload to the event store or sends it to the collector. + * @param payload - A payload to be sent to the collector + * @returns Promise that resolves when the payload has been added to the event store or sent to the collector + */ + input: (payload: Payload) => Promise; + /** + * Updates the collector URL to which events will be sent. + * @param url - New collector URL + */ + setCollectorUrl: (url: string) => void; + /** + * Sets the server anonymization flag. + */ + setAnonymousTracking: (anonymous: boolean) => void; + /** + * Updates the buffer size of the emitter. + */ + setBufferSize: (bufferSize: number) => void; +} + +interface RequestResult { + success: boolean; + retry: boolean; + status?: number; +} + +export function newEmitter({ + endpoint, + eventMethod = 'post', + protocol, + port, + maxPostBytes = 40000, + maxGetBytes, + bufferSize = 1, + customHeaders, + serverAnonymization, + connectionTimeout, + keepalive, + idService, + dontRetryStatusCodes = [], + retryStatusCodes = [], + retryFailedRequests = true, + onRequestFailure, + onRequestSuccess, + customFetch = fetch, + useStm, + eventStore = newInMemoryEventStore({}), + credentials, +}: EmitterConfiguration): Emitter { + let idServiceCalled = false; + let flushInProgress = false; + const usePost = eventMethod.toLowerCase() === 'post'; + dontRetryStatusCodes = dontRetryStatusCodes.concat([400, 401, 403, 410, 422]); + + function shouldRetryForStatusCode(statusCode: number): boolean { + // success, don't retry + if (statusCode >= 200 && statusCode < 300) { + return false; + } + + if (!retryFailedRequests) { + return false; + } + + // retry if status code among custom user-supplied retry codes + if (retryStatusCodes.includes(statusCode)) { + return true; + } + + // retry if status code *not* among the don't retry codes + return !dontRetryStatusCodes.includes(statusCode); + } + + function callOnRequestSuccess(payloads: Payload[], response: Response) { + if (onRequestSuccess !== undefined) { + setTimeout(() => { + try { + onRequestSuccess?.(payloads, response); + } catch (e) { + LOG.error('Error in onRequestSuccess', e); + } + }, 0); + } + } + + function callOnRequestFailure(failure: RequestFailure, response?: Response) { + if (onRequestFailure !== undefined) { + setTimeout(() => { + try { + onRequestFailure?.(failure, response); + } catch (e) { + LOG.error('Error in onRequestFailure', e); + } + }, 0); + } + } + + async function executeRequest(request: EmitterRequest): Promise { + const fetchRequest = request.toRequest(); + if (fetchRequest === undefined) { + throw new Error('Empty batch'); + } + + const payloads = request.getEvents().map((event) => event.getPayload()); + try { + const response = await customFetch(fetchRequest); + await response.text(); // wait for the response to be fully read + + request.closeRequest(true); + + if (response.ok) { + callOnRequestSuccess(payloads, response); + return { success: true, retry: false, status: response.status }; + } else { + const willRetry = shouldRetryForStatusCode(response.status); + callOnRequestFailure( + { + events: payloads, + status: response.status, + message: response.statusText, + willRetry: willRetry, + }, + response + ); + return { success: false, retry: willRetry, status: response.status }; + } + } catch (e) { + request.closeRequest(false); + + const message = typeof e === 'string' ? e : e ? (e as Error).message : 'Unknown error'; + callOnRequestFailure({ + events: payloads, + message: message, + willRetry: true, + }); + return { success: false, retry: true }; + } + } + + function newEmitterRequestWithConfig(): EmitterRequest { + return newEmitterRequest({ + endpoint, + protocol, + port, + eventMethod, + customHeaders, + connectionTimeout, + keepalive, + maxPostBytes, + useStm, + credentials, + }); + } + + function shouldSkipEventStore(emitterEvent: EmitterEvent): boolean { + const eventTooBigWarning = (bytes: number, maxBytes: number) => + LOG.warn('Event (' + bytes + 'B) too big, max is ' + maxBytes); + + if (usePost) { + const bytes = emitterEvent.getPOSTRequestBytesCount() + 88; // 88 bytes for the payload_data envelope + const tooBig = bytes > maxPostBytes; + if (tooBig) { + eventTooBigWarning(bytes, maxPostBytes); + } + return tooBig; + } else { + if (maxGetBytes === undefined) { + return false; + } + const bytes = emitterEvent.getGETRequestBytesCount(); + const tooBig = bytes > maxGetBytes; + if (tooBig) { + eventTooBigWarning(bytes, maxGetBytes); + } + return tooBig; + } + } + + async function callIdService() { + if (idService && !idServiceCalled) { + idServiceCalled = true; + const request = new Request(idService, { method: 'GET' }); + await customFetch(request); + } + } + + async function flush() { + if (!flushInProgress) { + flushInProgress = true; + + try { + await continueFlush(); + } catch (e) { + LOG.error('Error sending events', e); + } finally { + flushInProgress = false; + } + } + } + + async function continueFlush() { + await callIdService(); + + const request = newEmitterRequestWithConfig(); + const eventStoreIterator = eventStore.iterator(); + + while (true) { + if (request.isFull()) { + break; + } + + const { value, done } = await eventStoreIterator.next(); + if (done || value === undefined) { + break; + } + + const event = newEmitterEvent(value); + if (!request.addEvent(event)) { + break; + } + } + + if (request.countEvents() === 0) { + return; + } + + const { success, retry, status } = await executeRequest(request); + + if (success || !retry) { + if (!success) { + LOG.error(`Status ${status}, will not retry.`); + } + await eventStore.removeHead(request.countEvents()); + } + + if (success) { + await continueFlush(); + } + } + + async function input(payload: Payload) { + const eventStorePayload = newEventStorePayload({ payload, svrAnon: serverAnonymization }); + const event = newEmitterEvent(eventStorePayload); + if (shouldSkipEventStore(event)) { + const request = newEmitterRequestWithConfig(); + request.addEvent(event); + await executeRequest(request); + } else { + const count = await eventStore.add(eventStorePayload); + if (count >= bufferSize) { + await flush(); + } + } + } + + function setCollectorUrl(url: string) { + endpoint = url; + } + + function setAnonymousTracking(at: boolean) { + serverAnonymization = at; + } + + function setBufferSize(bs: number) { + bufferSize = bs; + } + + return { + flush, + input, + setCollectorUrl, + setAnonymousTracking, + setBufferSize, + }; +} diff --git a/libraries/tracker-core/src/event_store.ts b/libraries/tracker-core/src/event_store.ts new file mode 100644 index 000000000..287c16641 --- /dev/null +++ b/libraries/tracker-core/src/event_store.ts @@ -0,0 +1,99 @@ +import { EventStorePayload } from './event_store_payload'; +import { Payload } from './payload'; + +export interface EventStoreIterator { + /** + * Retrieve the next event in the store + */ + next: () => Promise<{ value: EventStorePayload | undefined; done: boolean }>; +} + +/** + * EventStore allows storing and retrieving events before they are sent to the collector + */ +export interface EventStore { + /** + * Count all events in the store + */ + count: () => Promise; + /** + * Add an event to the store + * @returns the number of events in the store after adding + */ + add: (payload: EventStorePayload) => Promise; + /** + * Remove the first `count` events from the store + */ + removeHead: (count: number) => Promise; + /** + * Get an iterator over all events in the store + */ + iterator: () => EventStoreIterator; + /** + * Retrieve all payloads including their meta configuration in the store + */ + getAll: () => Promise; + /** + * Retrieve all pure payloads in the store + */ + getAllPayloads: () => Promise; +} + +export interface EventStoreConfiguration { + /** + * The maximum amount of events that will be buffered in the event store + * + * This is useful to ensure the Tracker doesn't fill the 5MB or 10MB available to + * each website should the collector be unavailable due to lost connectivity. + * Will drop old events once the limit is hit + */ + maxSize?: number; +} + +export interface InMemoryEventStoreConfiguration { + /** + * Initial events to add to the store + */ + events?: EventStorePayload[]; +} + +export function newInMemoryEventStore({ + maxSize = 1000, + events = [], +}: EventStoreConfiguration & InMemoryEventStoreConfiguration): EventStore { + let store: EventStorePayload[] = [...events]; + + const count = () => Promise.resolve(store.length); + + return { + count, + add: (payload: EventStorePayload) => { + store.push(payload); + while (store.length > maxSize) { + store.shift(); + } + return count(); + }, + removeHead: (count: number) => { + for (let i = 0; i < count; i++) { + store.shift(); + } + return Promise.resolve(); + }, + iterator: () => { + let index = 0; + // copy the store to prevent mutation + let events = [...store]; + return { + next: () => { + if (index < events.length) { + return Promise.resolve({ value: events[index++], done: false }); + } + return Promise.resolve({ value: undefined, done: true }); + }, + }; + }, + getAll: () => Promise.resolve([...store]), + getAllPayloads: () => Promise.resolve(store.map((e) => e.payload)), + }; +} diff --git a/libraries/tracker-core/src/event_store_payload.ts b/libraries/tracker-core/src/event_store_payload.ts new file mode 100644 index 000000000..1a5c54a2a --- /dev/null +++ b/libraries/tracker-core/src/event_store_payload.ts @@ -0,0 +1,27 @@ +import { Payload } from "./payload"; + +export interface EventStorePayload { + /** + * The event payload to be stored + */ + payload: Payload; + + /** + * If the request should undergo server anonymization. + * @defaultValue false + */ + svrAnon?: boolean; +} + +/** + * Create a new EventStorePayload + */ +export function newEventStorePayload({ + payload, + svrAnon = false, +}: EventStorePayload): EventStorePayload { + return { + payload, + svrAnon, + }; +} diff --git a/libraries/tracker-core/src/index.ts b/libraries/tracker-core/src/index.ts index 94561ca02..5c0d702f3 100644 --- a/libraries/tracker-core/src/index.ts +++ b/libraries/tracker-core/src/index.ts @@ -41,5 +41,8 @@ export const version = v; export * from './contexts'; export * from './plugins'; export * from './payload'; +export * from './event_store_payload'; export * from './core'; export * from './logger'; +export * from './emitter'; +export * from './event_store'; diff --git a/libraries/tracker-core/src/plugins.ts b/libraries/tracker-core/src/plugins.ts index e0f195c57..4d1f6c2d9 100644 --- a/libraries/tracker-core/src/plugins.ts +++ b/libraries/tracker-core/src/plugins.ts @@ -44,7 +44,7 @@ export interface CorePlugin { */ activateCorePlugin?: (core: TrackerCore) => void; /** - * Called just before the trackerCore callback fires + * Called before the `filter` method is called and before the trackerCore callback fires (if the filter passes) * @param payloadBuilder - The payloadBuilder which will be sent to the callback, can be modified */ beforeTrack?: (payloadBuilder: PayloadBuilder) => void; @@ -53,6 +53,12 @@ export interface CorePlugin { * @param payload - The final built payload */ afterTrack?: (payload: Payload) => void; + /** + * Called before the payload is sent to the callback to decide whether to send the payload or skip it + * @param payload - The final event payload, can't be modified. + * @returns True if the payload should be sent, false if it should be skipped + */ + filter?: (payload: Payload) => boolean; /** * Called when constructing the context for each event * Useful for adding additional context to events diff --git a/libraries/tracker-core/src/schemata.ts b/libraries/tracker-core/src/schemata.ts new file mode 100644 index 000000000..26caf6e56 --- /dev/null +++ b/libraries/tracker-core/src/schemata.ts @@ -0,0 +1 @@ +export const PAYLOAD_DATA_SCHEMA = 'iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-4'; diff --git a/libraries/tracker-core/test/contexts.ts b/libraries/tracker-core/test/contexts.ts index b1e511b94..fe2d6a81a 100644 --- a/libraries/tracker-core/test/contexts.ts +++ b/libraries/tracker-core/test/contexts.ts @@ -215,6 +215,75 @@ test('Add global contexts', (t) => { t.is(globalContexts.getConditionalProviders().length, 2, 'Correct number of conditional providers added'); }); +test('Handle named global contexts', (t) => { + const geolocationContext = { + schema: 'iglu:com.snowplowanalytics.snowplow/geolocation_context/jsonschema/1-1-0', + data: { + latitude: 40.0, + longitude: 55.1, + }, + }; + + function eventTypeContextGenerator(args?: contexts.ContextEvent) { + const context: SelfDescribingJson = { + schema: 'iglu:com.snowplowanalytics.snowplow/mobile_context/jsonschema/1-0-1', + data: { + osType: 'ubuntu', + osVersion: '2018.04', + deviceManufacturer: 'ASUS', + deviceModel: args ? String(args['eventType']) : '', + }, + }; + return context; + } + + const bothRuleSet = { + accept: ['iglu:com.snowplowanalytics.snowplow/*/jsonschema/*-*-*'], + reject: ['iglu:com.snowplowanalytics.snowplow/*/jsonschema/*-*-*'], + }; + + const filterFunction = function (args?: contexts.ContextEvent) { + return args?.eventType === 'ue'; + }; + + const filterProvider: contexts.FilterProvider = [filterFunction, [geolocationContext, eventTypeContextGenerator]]; + const ruleSetProvider: contexts.RuleSetProvider = [bothRuleSet, [geolocationContext, eventTypeContextGenerator]]; + + const namedContexts = { + filters: filterProvider, + rules: ruleSetProvider, + static: geolocationContext, + generator: eventTypeContextGenerator, + }; + const globalContexts = contexts.globalContexts(); + + globalContexts.addGlobalContexts(namedContexts); + t.is(globalContexts.getGlobalPrimitives().length, 2, 'Correct number of primitives added'); + t.is(globalContexts.getConditionalProviders().length, 2, 'Correct number of conditional providers added'); + + globalContexts.removeGlobalContexts(['static', 'rules']); + t.is(globalContexts.getGlobalPrimitives().length, 1, 'Correct number of primitives removed'); + t.is(globalContexts.getConditionalProviders().length, 1, 'Correct number of conditional providers removed'); + + globalContexts.clearGlobalContexts(); + t.is(globalContexts.getGlobalPrimitives().length, 0, 'All primitives removed'); + t.is(globalContexts.getConditionalProviders().length, 0, 'All conditional providers removed'); + + globalContexts.addGlobalContexts([geolocationContext]); + globalContexts.addGlobalContexts({ geo: geolocationContext }); + t.is(globalContexts.getGlobalPrimitives().length, 2, 'Treats anonymous and named globals separately'); + + const mutatedGeolocationContext = JSON.parse(JSON.stringify(geolocationContext)); + mutatedGeolocationContext.data.latitude = 30; + + globalContexts.addGlobalContexts({ geo: mutatedGeolocationContext }); + t.deepEqual( + globalContexts.getGlobalPrimitives(), + [geolocationContext, mutatedGeolocationContext], + 'Upserts named globals' + ); +}); + test('Remove one of two global context primitives', (t) => { const geolocationContext = { schema: 'iglu:com.snowplowanalytics.snowplow/geolocation_context/jsonschema/1-1-0', diff --git a/libraries/tracker-core/test/core.ts b/libraries/tracker-core/test/core.ts index b238514d6..aac62a570 100644 --- a/libraries/tracker-core/test/core.ts +++ b/libraries/tracker-core/test/core.ts @@ -80,7 +80,7 @@ test('tracker.track API should return the eid attribute', (t) => { page: pageTitle, refr: referrer, }; - const eventPayload = tracker.track(buildPageView({ pageUrl, pageTitle, referrer })); + const eventPayload = tracker.track(buildPageView({ pageUrl, pageTitle, referrer }))!; t.truthy(eventPayload.eid); t.regex(eventPayload.eid as string, UUID_REGEX); compare(eventPayload, expected, t); @@ -96,7 +96,7 @@ test('should track a page view', (t) => { page: pageTitle, refr: referrer, }; - compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer })), expected, t); + compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer }))!, expected, t); }); test('should track a page ping', (t) => { const pageUrl = 'http://www.example.com'; @@ -115,7 +115,7 @@ test('should track a page ping', (t) => { compare( tracker.track( buildPagePing({ pageUrl, pageTitle, referrer, minXOffset: 1, maxXOffset: 2, minYOffset: 3, maxYOffset: 4 }) - ), + )!, expected, t ); @@ -130,7 +130,7 @@ test('should track a structured event', (t) => { se_va: '1', }; compare( - tracker.track(buildStructEvent({ category: 'cat', action: 'act', label: 'lab', property: 'prop', value: 1 })), + tracker.track(buildStructEvent({ category: 'cat', action: 'act', label: 'lab', property: 'prop', value: 1 }))!, expected, t ); @@ -170,7 +170,7 @@ test('should track an ecommerce transaction event', (t) => { country, currency, }) - ), + )!, expected, t ); @@ -194,7 +194,7 @@ test('should track an ecommerce transaction item event', (t) => { ti_cu: currency, }; compare( - tracker.track(buildEcommerceTransactionItem({ orderId, sku, name, category, price, quantity, currency })), + tracker.track(buildEcommerceTransactionItem({ orderId, sku, name, category, price, quantity, currency }))!, expected, t ); @@ -213,7 +213,7 @@ test('should track a self-describing event', (t) => { data: inputJson, }), }; - compare(tracker.track(buildSelfDescribingEvent({ event: inputJson })), expected, t); + compare(tracker.track(buildSelfDescribingEvent({ event: inputJson }))!, expected, t); }); test('should track a link click', (t) => { const targetUrl = 'http://www.example.com'; @@ -239,7 +239,7 @@ test('should track a link click', (t) => { }), }; compare( - tracker.track(buildLinkClick({ targetUrl, elementId, elementClasses, elementTarget, elementContent })), + tracker.track(buildLinkClick({ targetUrl, elementId, elementClasses, elementTarget, elementContent }))!, expected, t ); @@ -261,7 +261,7 @@ test('should track a screen view', (t) => { data: inputJson, }), }; - compare(tracker.track(buildScreenView({ name, id })), expected, t); + compare(tracker.track(buildScreenView({ name, id }))!, expected, t); }); test('should track an ad impression', (t) => { const impressionId = 'a0e8f8780ab3'; @@ -295,7 +295,7 @@ test('should track an ad impression', (t) => { compare( tracker.track( buildAdImpression({ impressionId, costModel, cost, targetUrl, bannerId, zoneId, advertiserId, campaignId }) - ), + )!, expected, t ); @@ -334,7 +334,7 @@ test('should track an ad click', (t) => { compare( tracker.track( buildAdClick({ targetUrl, clickId, costModel, cost, bannerId, zoneId, impressionId, advertiserId, campaignId }) - ), + )!, expected, t ); @@ -383,7 +383,7 @@ test('should track an ad conversion', (t) => { advertiserId, campaignId, }) - ), + )!, expected, t ); @@ -407,7 +407,7 @@ test('should track a social interaction', (t) => { data: inputJson, }), }; - compare(tracker.track(buildSocialInteraction({ action, network, target })), expected, t); + compare(tracker.track(buildSocialInteraction({ action, network, target }))!, expected, t); }); test('should track an add-to-cart event', (t) => { const sku = '4q345'; @@ -434,7 +434,7 @@ test('should track an add-to-cart event', (t) => { data: inputJson, }), }; - compare(tracker.track(buildAddToCart({ sku, name, category, unitPrice, quantity, currency })), expected, t); + compare(tracker.track(buildAddToCart({ sku, name, category, unitPrice, quantity, currency }))!, expected, t); }); test('should track a remove-from-cart event', (t) => { const sku = '4q345'; @@ -461,7 +461,7 @@ test('should track a remove-from-cart event', (t) => { data: inputJson, }), }; - compare(tracker.track(buildRemoveFromCart({ sku, name, category, unitPrice, quantity, currency })), expected, t); + compare(tracker.track(buildRemoveFromCart({ sku, name, category, unitPrice, quantity, currency }))!, expected, t); }); test('should track a form focus event', (t) => { const formId = 'parent'; @@ -491,7 +491,7 @@ test('should track a form focus event', (t) => { compare( tracker.track( buildFormFocusOrChange({ schema: 'focus_form', formId, elementId, nodeName, type, elementClasses, value }) - ), + )!, expected, t ); @@ -524,7 +524,7 @@ test('should track a form change event', (t) => { compare( tracker.track( buildFormFocusOrChange({ schema: 'change_form', formId, elementId, nodeName, type, elementClasses, value }) - ), + )!, expected, t ); @@ -555,7 +555,7 @@ test('should track a form submission event', (t) => { data: inputJson, }), }; - compare(tracker.track(buildFormSubmission({ formId, formClasses, elements })), expected, t); + compare(tracker.track(buildFormSubmission({ formId, formClasses, elements }))!, expected, t); }); test('should track a site seach event', (t) => { const terms = ['javascript', 'development']; @@ -581,7 +581,7 @@ test('should track a site seach event', (t) => { data: inputJson, }), }; - compare(tracker.track(buildSiteSearch({ terms, filters, totalResults, pageResults })), expected, t); + compare(tracker.track(buildSiteSearch({ terms, filters, totalResults, pageResults }))!, expected, t); }); test('should track a consent withdrawn event', (t) => { const all = false; @@ -619,7 +619,7 @@ test('should track a consent withdrawn event', (t) => { }), }; const consentEvent = buildConsentWithdrawn({ all, id, version, name, description }); - compare(tracker.track(consentEvent.event, consentEvent.context, timestamp), expected, t); + compare(tracker.track(consentEvent.event, consentEvent.context, timestamp)!, expected, t); }); test('should track a consent granted event', (t) => { const id = '1234'; @@ -657,7 +657,7 @@ test('should track a consent granted event', (t) => { }), }; const consentEvent = buildConsentGranted({ id, version, name, description, expiry }); - compare(tracker.track(consentEvent.event, consentEvent.context, timestamp), expected, t); + compare(tracker.track(consentEvent.event, consentEvent.context, timestamp)!, expected, t); }); test('should track a page view with custom context', (t) => { const pageUrl = 'http://www.example.com'; @@ -682,7 +682,7 @@ test('should track a page view with custom context', (t) => { data: inputContext, }), }; - compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer }), inputContext), expected, t); + compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer }), inputContext)!, expected, t); }); test('should track a page view with a timestamp', (t) => { const timestamp = 1000000000000; @@ -691,7 +691,7 @@ test('should track a page view with a timestamp', (t) => { buildPageView({ pageUrl: 'http://www.example.com', pageTitle: 'title', referrer: 'ref' }), [], timestamp - )['dtm'], + )!['dtm'], '1000000000000' ); }); @@ -710,7 +710,7 @@ test('should add individual name-value pairs to the payload', (t) => { }; tracker.addPayloadPair('tna', 'sp'); tracker.addPayloadPair('tv', 'js-2.0.0'); - compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer })), expected, t); + compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer }))!, expected, t); }); test('should add a dictionary of name-value pairs to the payload', (t) => { const tracker = trackerCore({ base64: false }); @@ -731,7 +731,7 @@ test('should add a dictionary of name-value pairs to the payload', (t) => { tna: 'sp', aid: 'sp325', }); - compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer })), expected, t); + compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer }))!, expected, t); }); test('should reset payload name-value pairs', (t) => { const tracker = trackerCore({ base64: false }); @@ -747,7 +747,7 @@ test('should reset payload name-value pairs', (t) => { }; tracker.addPayloadPair('tna', 'mistake'); tracker.resetPayloadPairs({ tna: 'sp' }); - compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer })), expected, t); + compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer }))!, expected, t); }); test('should execute a callback', (t) => { const tracker = trackerCore({ @@ -802,7 +802,7 @@ test('should use setter methods', (t) => { ua: 'SnowplowJavascript/0.0.1', refr: referrer, }; - compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer })), expected, t); + compare(tracker.track(buildPageView({ pageUrl, pageTitle, referrer }))!, expected, t); }); test('should set true timestamp', (t) => { @@ -812,7 +812,7 @@ test('should set true timestamp', (t) => { const result = tracker.track(buildPageView({ pageUrl, pageTitle, referrer }), undefined, { type: 'ttm', value: 1477403862, - }); + })!; t.true('ttm' in result); t.is(result['ttm'], '1477403862'); t.false('dtm' in result); @@ -828,7 +828,7 @@ test('should set device timestamp as ADT', (t) => { const result = tracker.track(buildSelfDescribingEvent({ event: inputJson }), [inputJson], { type: 'dtm', value: 1477403869, - }); + })!; t.true('dtm' in result); t.is(result['dtm'], '1477403869'); t.false('ttm' in result); @@ -932,3 +932,105 @@ test('should run plugin before and after track callbacks on each track event', ( t.is(beforeCount, fs.length); t.is(afterCount, fs.length); }); + +test('should skip events in case the plugin filter function returns false', (t) => { + let countTracked = 0; + const tracker = trackerCore({ + base64: false, + corePlugins: [ + { + filter: (payload) => { + return payload.e !== 'pv'; + }, + }, + { + filter: (payload) => { + return payload.e !== 'pp'; + }, + }, + { + afterTrack: () => { + countTracked += 1; + }, + }, + ], + }); + + t.falsy( + tracker.track( + buildPageView({ + pageUrl: 'http://www.example.com', + pageTitle: 'title page', + referrer: 'https://www.google.com', + }) + ) + ); + + t.falsy( + tracker.track( + buildPagePing({ + pageUrl: 'http://www.example.com', + pageTitle: 'title page', + referrer: 'https://www.google.com', + maxXOffset: 1, + maxYOffset: 1, + minXOffset: 1, + minYOffset: 1, + }) + ) + ); + + t.truthy( + tracker.track( + buildAddToCart({ + category: 'cat', + name: 'name', + quantity: 1, + sku: 'sku', + unitPrice: 1, + }) + ) + ); + + t.assert(countTracked === 1); +}); + +test('filter is passed full payload including dynamic context', (t) => { + let countTracked = 0; + const tracker = trackerCore({ + base64: false, + corePlugins: [ + { + contexts: () => { + return [ + { + schema: 'iglu:com.acme/user/jsonschema/1-0-0', + data: { + userType: 'tester', + userName: 'Jon', + }, + }, + ] + }, + filter: (payload) => { + return (payload.co as string).includes('com.acme'); + }, + afterTrack: () => { + countTracked += 1; + }, + }, + ], + }); + + t.truthy( + tracker.track( + buildPageView({ + pageUrl: 'http://www.example.com', + pageTitle: 'title page', + referrer: 'https://www.google.com', + }) + ) + ); + + t.assert(countTracked === 1); +}); diff --git a/libraries/tracker-core/test/emitter/emitter_event.test.ts b/libraries/tracker-core/test/emitter/emitter_event.test.ts new file mode 100644 index 000000000..5d9520713 --- /dev/null +++ b/libraries/tracker-core/test/emitter/emitter_event.test.ts @@ -0,0 +1,66 @@ +import test from 'ava'; + +import { newEmitterEvent } from '../../src/emitter/emitter_event'; +import { newEventStorePayload } from '../../src/event_store_payload'; + +test('getGETRequestURL returns the correct URL with stm', (t) => { + const collectorUrl = 'https://example.com'; + const payload = { e: 'pv' }; + const event = newEmitterEvent(newEventStorePayload({ payload })); + const url = event.getGETRequestURL(collectorUrl, true); + t.regex(url, new RegExp(`${collectorUrl}\\?stm=\\d+&e=pv`)); +}); + +test('getGETRequestURL returns the correct URL without stm', (t) => { + const collectorUrl = 'https://example.com'; + const payload = { e: 'pv', p: 'web' }; + const event = newEmitterEvent(newEventStorePayload({ payload })); + const url = event.getGETRequestURL(collectorUrl, false); + t.is(url, `${collectorUrl}?e=pv&p=web`); +}); + +test('getGETRequestBytesCount returns the correct byte count', (t) => { + const payload = { e: 'pv', p: 'web' }; + const event = newEmitterEvent(newEventStorePayload({ payload })); + const count = event.getGETRequestBytesCount(); + t.is(count, 11); +}); + +test('getPOSTRequestBody processes integer into string values', (t) => { + const payload = { e: 'pv', x: 12 }; + const event = newEmitterEvent(newEventStorePayload({ payload })); + const body = event.getPOSTRequestBody(); + t.deepEqual(body, { e: 'pv', x: '12' }); +}); + +test('getPOSTRequestBytesCount returns the correct byte count', (t) => { + const payload = { e: 'pv', p: 'web' }; + const event = newEmitterEvent(newEventStorePayload({ payload })); + const count = event.getPOSTRequestBytesCount(); + t.is(count, 20); +}); + +test('getPOSTRequestBytesCount counts multibyte chars properly', (t) => { + const payload = { e: 'pv', p: '🍕' }; + const event = newEmitterEvent(newEventStorePayload({ payload })); + const count = event.getPOSTRequestBytesCount(); + t.is(count, 21); +}); + +test('getPayload returns the payload', (t) => { + const payload = { e: 'pv' }; + const event = newEmitterEvent(newEventStorePayload({ payload })); + t.deepEqual(event.getPayload(), payload); +}); + +test('getServerAnonymization returns the serverAnonymization value', (t) => { + const payload = { e: 'pv' }; + const event = newEmitterEvent(newEventStorePayload({ payload, svrAnon: true })); + t.true(event.getServerAnonymization()); +}); + +test('getServerAnonymization returns false by default', (t) => { + const payload = { e: 'pv' }; + const event = newEmitterEvent(newEventStorePayload({ payload })); + t.false(event.getServerAnonymization()); +}); diff --git a/libraries/tracker-core/test/emitter/emitter_request.test.ts b/libraries/tracker-core/test/emitter/emitter_request.test.ts new file mode 100644 index 000000000..15acb533a --- /dev/null +++ b/libraries/tracker-core/test/emitter/emitter_request.test.ts @@ -0,0 +1,302 @@ +import test from 'ava'; + +import { newEmitterRequest } from '../../src/emitter/emitter_request'; +import { newEmitterEvent } from '../../src/emitter/emitter_event'; +import { newEventStorePayload } from '../../src/event_store_payload'; + +const newEmitterEventFromPayload = (payload: Record) => { + return newEmitterEvent(newEventStorePayload({ payload })); +}; + +// MARK: - addEvent + +test('addEvent adds an event to the request', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + maxPostBytes: 1000, + }); + + const event = newEmitterEventFromPayload({ e: 'pv' }); + + t.true(request.addEvent(event)); + t.is(request.getEvents().length, 1); +}); + +test('addEvent returns false when server anonymization does not match previous events', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + maxPostBytes: 1000, + }); + + t.true( + request.addEvent( + newEmitterEvent( + newEventStorePayload({ + payload: { e: 'pv' }, + svrAnon: true, + }) + ) + ) + ); + t.false( + request.addEvent( + newEmitterEvent( + newEventStorePayload({ + payload: { e: 'pv' }, + svrAnon: false, + }) + ) + ) + ); + t.true( + request.addEvent( + newEmitterEvent( + newEventStorePayload({ + payload: { e: 'pv' }, + svrAnon: true, + }) + ) + ) + ); + t.is(request.getEvents().length, 2); +}); + +// MARK: - countBytes + +test('countBytes returns the correct byte count', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + maxPostBytes: 1000, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'mob' }))); + t.is(request.countBytes(), 40 + 88); // 40 bytes for each event, 88 bytes for the payload_data envelope +}); + +// MARK: - countEvents + +test('countEvents returns the correct event count', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + maxPostBytes: 1000, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'mob' }))); + t.is(request.countEvents(), 2); +}); + +// MARK: - isFull + +test('isFull returns false when not reached max post bytes', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + maxPostBytes: 1000, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + t.false(request.isFull()); +}); + +test('isFull returns false when reached buffer size and not max post bytes', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + maxPostBytes: 1000, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'mob' }))); + t.false(request.isFull()); +}); + +test('isFull returns true when reached max post bytes', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + maxPostBytes: 10, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + t.true(request.isFull()); +}); + +// MARK: - toRequest + +test('toRequest returns a Request object with default settings', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.url, 'https://example.com/com.snowplowanalytics.snowplow/tp2'); + t.is(req.method, 'POST'); + t.is(req.headers.get('Content-Type'), 'application/json; charset=UTF-8'); + t.is(req.headers.get('SP-Anonymous'), null); +}); + +test('toRequest builds a GET request when method is get', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + eventMethod: 'get', + useStm: false, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.url, 'https://example.com/i?e=pv&p=web'); + t.is(req.method, 'GET'); +}); + +test('toRequest includes stm when useStm is true', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + eventMethod: 'get', + useStm: true, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.regex(req.url, /stm=\d+/); +}); + +test('toRequest includes custom headers', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + customHeaders: { 'X-Test': 'test' }, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.headers.get('X-Test'), 'test'); +}); + +test('toRequest includes server anonymization header', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + }); + + t.true( + request.addEvent( + newEmitterEvent( + newEventStorePayload({ + payload: { e: 'pv', p: 'web' }, + svrAnon: true, + }) + ) + ) + ); + const req = request.toRequest()!; + t.is(req.headers.get('SP-Anonymous'), '*'); +}); + +test('toRequest contains keepalive', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + keepalive: true, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.keepalive, true); +}); + +test('toRequest URL contains custom POST path', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + postPath: '/custom', + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.url, 'https://example.com/custom'); +}); + +test('toRequest URL adds default protocol when missing', (t) => { + const request = newEmitterRequest({ + endpoint: 'example.com', + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.url, 'https://example.com/com.snowplowanalytics.snowplow/tp2'); +}); + +test('toRequest URL adds protocol and port', (t) => { + const request = newEmitterRequest({ + endpoint: 'example.com', + protocol: 'http', + port: 9090, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.url, 'http://example.com:9090/com.snowplowanalytics.snowplow/tp2'); +}); + +test('toRequest URL does not add protocol if already contains', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + protocol: 'http', + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.url, 'https://example.com/com.snowplowanalytics.snowplow/tp2'); +}); + +test('toRequest returns undefined when no events', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + }); + + t.is(request.toRequest(), undefined); +}); + +test('toRequest contains default credentials', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + eventMethod: 'get', + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.credentials, 'include'); +}); + +test('toRequest contains credentials', (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + credentials: 'omit', + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + t.is(req.credentials, 'omit'); +}); + +test('toRequest adds stm to POST request', async (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + useStm: true, + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + const req = request.toRequest()!; + const data = await req.json(); + t.truthy(data.data[0].stm); +}); + +test('toRequest body has the correct structure', async (t) => { + const request = newEmitterRequest({ + endpoint: 'https://example.com', + }); + + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + t.true(request.addEvent(newEmitterEventFromPayload({ e: 'pv', p: 'web' }))); + + const req = request.toRequest()!; + const data = await req.json(); + t.is(data.schema, 'iglu:com.snowplowanalytics.snowplow/payload_data/jsonschema/1-0-4'); + t.is(data.data.length, 2); +}); diff --git a/libraries/tracker-core/test/emitter/index.test.ts b/libraries/tracker-core/test/emitter/index.test.ts new file mode 100644 index 000000000..6e01b131a --- /dev/null +++ b/libraries/tracker-core/test/emitter/index.test.ts @@ -0,0 +1,335 @@ +import test from 'ava'; + +import { newEmitter, Emitter } from '../../src/emitter'; +import { newInMemoryEventStore } from "../../src/event_store"; + +function createMockFetch(status: number, requests: Request[]) { + return async (input: Request) => { + requests.push(input); + let response = new Response(null, { status }); + return response; + }; +} + +test.before(() => { + console.error = () => {}; // Silence console.error globally +}); + +test("input adds an event to the event store", async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(200, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 2, + customFetch: mockFetch, + eventStore, + }); + + await emitter.input({ e: "pv" }); + + t.is(await eventStore.count(), 1); + t.is(requests.length, 0); +}); + +test('input sends events to the collector when the buffer is full', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(200, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 2, + customFetch: mockFetch, + eventStore, + }); + + await emitter.input({ e: 'pv' }); + await emitter.input({ e: 'pv' }); + + t.is(requests.length, 1); + t.is(await eventStore.count(), 0); +}); + +test('flush sends events to the collector', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(200, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + eventStore, + }); + + await emitter.input({ e: 'pv' }); + await emitter.input({ e: 'pv' }); + await emitter.flush(); + + t.is(requests.length, 1); + t.is(await eventStore.count(), 0); +}); + +test('flush does not make any request when the event store is empty', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(200, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + eventStore, + }); + + await emitter.flush(); + + t.is(requests.length, 0); + t.is(await eventStore.count(), 0); +}); + +test('flush makes separate requests for server anonymization settings in events', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(200, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + eventStore, + }); + + await emitter.input({ e: 'pv' }); + await emitter.input({ e: 'pv' }); + emitter.setAnonymousTracking(true); + await emitter.input({ e: 'pv' }); + await emitter.input({ e: 'pv' }); + await emitter.flush(); + + t.is(requests.length, 2); + t.is(requests[0].headers.get('SP-Anonymous'), null); + t.is(requests[1].headers.get('SP-Anonymous'), '*'); + t.is(await eventStore.count(), 0); +}); + +test('setCollectorUrl changes the collector URL', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(200, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + eventStore, + }); + + emitter.setCollectorUrl('https://example2.com'); + await emitter.input({ e: 'pv' }); + await emitter.flush(); + + t.is(requests.length, 1); + t.is(await eventStore.count(), 0); + t.is(requests[0].url, 'https://example2.com/com.snowplowanalytics.snowplow/tp2'); +}); + +test('calls onRequestSuccess when the request is successful', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(200, requests); + const eventStore = newInMemoryEventStore({}); + + await new Promise((resolve) => { + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + onRequestSuccess: (data) => { + t.is(data.length, 1); + resolve(null); + }, + eventStore, + }); + + emitter.input({ e: 'pv' }).then(() => emitter.flush()); + }); +}); + +test('handles errors when onRequestSuccess throws', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(200, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + onRequestSuccess: () => { + throw new Error('error'); + }, + eventStore, + }); + + await emitter.input({ e: 'pv' }); + await emitter.flush(); + t.is(await eventStore.count(), 0); +}); + +test('calls onRequestFailure when the request fails', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(500, requests); + const eventStore = newInMemoryEventStore({}); + + await new Promise((resolve) => { + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + onRequestFailure: (requestFailure, response) => { + t.is(requestFailure.events.length, 1); + t.is(requestFailure.status, 500); + t.is(response?.status, 500); + resolve(null); + }, + eventStore, + }); + + emitter.input({ e: 'pv' }).then(() => emitter.flush()); + }); +}); + +test('retries when the request fails', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(500, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + retryFailedRequests: true, + eventStore, + }); + + await emitter.input({ e: 'pv' }); + await emitter.flush(); + t.is(requests.length, 1); + t.is(await eventStore.count(), 1); +}); + +test('does not retry when the request fails and retryFailedRequests is false', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(500, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + retryFailedRequests: false, + eventStore, + }); + + await emitter.input({ e: 'pv' }); + await emitter.flush(); + t.is(requests.length, 1); + t.is(await eventStore.count(), 0); +}); + +test('does not retry certain status codes', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(422, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + eventStore, + }); + + await emitter.input({ e: 'pv' }); + await emitter.flush(); + t.is(requests.length, 1); + t.is(await eventStore.count(), 0); +}); + +test('does not retry if the status code is in the dontRetry list', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(500, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + dontRetryStatusCodes: [500], + eventStore, + }); + + await emitter.input({ e: 'pv' }); + await emitter.flush(); + t.is(requests.length, 1); + t.is(await eventStore.count(), 0); +}); + +test('retries if the status code is in the retry list', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(422, requests); + const eventStore = newInMemoryEventStore({}); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + bufferSize: 5, + customFetch: mockFetch, + retryStatusCodes: [422], + eventStore, + }); + + await emitter.input({ e: 'pv' }); + await emitter.flush(); + t.is(requests.length, 1); + t.is(await eventStore.count(), 1); +}); + +test('makes a request to the id service only once', async (t) => { + const requests: Request[] = []; + const mockFetch = createMockFetch(200, requests); + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + customFetch: mockFetch, + idService: 'https://id-example.com', + }); + + await emitter.input({ e: 'pv' }); + + t.is(requests.length, 2); + t.is(requests[0].url, 'https://id-example.com/'); + t.is(requests[1].url, 'https://example.com/com.snowplowanalytics.snowplow/tp2'); + + await emitter.input({ e: 'pv' }); + t.is(requests.length, 3); + t.is(requests[2].url, 'https://example.com/com.snowplowanalytics.snowplow/tp2'); +}); + +test('adds a timeout to the request', async (t) => { + const requests: Request[] = []; + let eventStore = newInMemoryEventStore({}); + + const mockFetch = (input: Request): Promise => { + requests.push(input); + + return new Promise((resolve, reject) => { + let timer = setTimeout(() => { + t.fail('Request should have timed out'); + resolve(new Response(null, { status: 200 })); + }, 500); + + input.signal?.addEventListener('abort', () => { + clearTimeout(timer); + reject(new Error('Request aborted')); + }); + }); + }; + const emitter: Emitter = newEmitter({ + endpoint: 'https://example.com', + customFetch: mockFetch, + connectionTimeout: 100, + eventStore + }); + + await emitter.input({ e: 'pv' }); + + t.is(requests.length, 1); + t.is(await eventStore.count(), 1); +}); diff --git a/libraries/tracker-core/test/event_store.test.ts b/libraries/tracker-core/test/event_store.test.ts new file mode 100644 index 000000000..468d444d0 --- /dev/null +++ b/libraries/tracker-core/test/event_store.test.ts @@ -0,0 +1,92 @@ +import test from 'ava'; + +import { newInMemoryEventStore } from '../src/event_store'; +import { newEventStorePayload } from '../src/event_store_payload'; + +test('count returns the number of events', async (t) => { + const eventStore = newInMemoryEventStore({}); + + t.is(await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })), 1); + t.is(await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })), 2); + t.is(await eventStore.count(), 2); +}); + +test('iterator returns all events', async (t) => { + const eventStore = newInMemoryEventStore({}); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })); + + const iterator = eventStore.iterator(); + const { value: first } = await iterator.next(); + const { value: second } = await iterator.next(); + const { done } = await iterator.next(); + + t.deepEqual(first?.payload, { e: 'pv' }); + t.deepEqual(second?.payload, { e: 'pv' }); + t.true(done); +}); + +test('removeHead removes the first n events', async (t) => { + const eventStore = newInMemoryEventStore({}); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })); + + await eventStore.removeHead(1); + + t.is(await eventStore.count(), 1); +}); + +test('removeHead does nothing when there are no events', async (t) => { + const eventStore = newInMemoryEventStore({}); + await eventStore.removeHead(1); + + t.is(await eventStore.count(), 0); +}); + +test('does not exceed maxSize', async (t) => { + const eventStore = newInMemoryEventStore({ maxSize: 1 }); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv1' } })); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv2' } })); + + t.is(await eventStore.count(), 1); + t.is((await eventStore.iterator().next()).value?.payload.e, 'pv2'); +}); + +test('iterator does not consider mutations', async (t) => { + const eventStore = newInMemoryEventStore({}); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })); + + const iterator = eventStore.iterator(); + await iterator.next(); + + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })); + + const { value } = await iterator.next(); + + t.is(value, undefined); +}); + +test('stores server anonymization setting', async (t) => { + const eventStore = newInMemoryEventStore({}); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' }, svrAnon: true })); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' }, svrAnon: false })); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })); + + t.is(await eventStore.count(), 3); + const iterator = eventStore.iterator(); + const first = await iterator.next(); + const second = await iterator.next(); + const third = await iterator.next(); + + t.true(first?.value?.svrAnon); + t.false(second?.value?.svrAnon); + t.false(third?.value?.svrAnon); +}); + +test('getAllPayloads returns all payloads', async (t) => { + const eventStore = newInMemoryEventStore({}); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })); + await eventStore.add(newEventStorePayload({ payload: { e: 'pv' } })); + + t.deepEqual(await eventStore.getAllPayloads(), [{ e: 'pv' }, { e: 'pv' }]); +}); diff --git a/plugins/browser-plugin-ad-tracking/jest.config.js b/plugins/browser-plugin-ad-tracking/jest.config.js index bd3ea4e2a..87d15da9b 100644 --- a/plugins/browser-plugin-ad-tracking/jest.config.js +++ b/plugins/browser-plugin-ad-tracking/jest.config.js @@ -1,5 +1,6 @@ module.exports = { preset: 'ts-jest', reporters: ['jest-standard-reporter'], + setupFilesAfterEnv: ['../../setupTestGlobals.ts'], testEnvironment: 'jest-environment-jsdom-global', }; diff --git a/plugins/browser-plugin-ad-tracking/test/events.test.ts b/plugins/browser-plugin-ad-tracking/test/events.test.ts index e3d4ec496..98521c9fc 100644 --- a/plugins/browser-plugin-ad-tracking/test/events.test.ts +++ b/plugins/browser-plugin-ad-tracking/test/events.test.ts @@ -31,9 +31,10 @@ import { addTracker, SharedState } from '@snowplow/browser-tracker-core'; import F from 'lodash/fp'; import { AdTrackingPlugin, trackAdClick, trackAdConversion, trackAdImpression } from '../src'; +import { newInMemoryEventStore } from '@snowplow/tracker-core'; -const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('evt.e')))); -const extractEventProperties = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('evt.ue_pr'))); +const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('e')))); +const extractEventProperties = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('ue_pr'))); const extractUeEvent = (schema: string) => { return { from: F.compose( @@ -48,13 +49,18 @@ const extractUeEvent = (schema: string) => { describe('AdTrackingPlugin', () => { const state = new SharedState(); + let eventStore = newInMemoryEventStore({}); + const customFetch = async () => new Response(null, { status: 500 }); + addTracker('sp1', 'sp1', 'js-3.0.0', '', state, { stateStorageStrategy: 'cookie', encodeBase64: false, plugins: [AdTrackingPlugin()], + eventStore, + customFetch, }); - it('trackAdClick adds the expected ad click event to the queue', () => { + it('trackAdClick adds the expected ad click event to the queue', async () => { trackAdClick({ targetUrl: 'https://www.snowplowanalytics.com', bannerId: 'banner-1', @@ -68,7 +74,9 @@ describe('AdTrackingPlugin', () => { }); expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/ad_click/jsonschema/1-0-0').from(state.outQueues[0]) + extractUeEvent('iglu:com.snowplowanalytics.snowplow/ad_click/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) ).toMatchObject({ schema: 'iglu:com.snowplowanalytics.snowplow/ad_click/jsonschema/1-0-0', data: { @@ -85,7 +93,7 @@ describe('AdTrackingPlugin', () => { }); }); - it('trackAdConversion adds the expected ad conversion event to the queue', () => { + it('trackAdConversion adds the expected ad conversion event to the queue', async () => { trackAdConversion({ action: 'action', advertiserId: 'advertiser-1', @@ -99,7 +107,9 @@ describe('AdTrackingPlugin', () => { }); expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/ad_conversion/jsonschema/1-0-0').from(state.outQueues[0]) + extractUeEvent('iglu:com.snowplowanalytics.snowplow/ad_conversion/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) ).toMatchObject({ schema: 'iglu:com.snowplowanalytics.snowplow/ad_conversion/jsonschema/1-0-0', data: { @@ -116,7 +126,7 @@ describe('AdTrackingPlugin', () => { }); }); - it('trackAdImpression adds the expected ad impression event to the queue', () => { + it('trackAdImpression adds the expected ad impression event to the queue', async () => { trackAdImpression({ advertiserId: 'advrtiser-1', bannerId: 'banner-1', @@ -129,7 +139,9 @@ describe('AdTrackingPlugin', () => { }); expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/ad_impression/jsonschema/1-0-0').from(state.outQueues[0]) + extractUeEvent('iglu:com.snowplowanalytics.snowplow/ad_impression/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) ).toMatchObject({ schema: 'iglu:com.snowplowanalytics.snowplow/ad_impression/jsonschema/1-0-0', data: { diff --git a/plugins/browser-plugin-browser-features/CHANGELOG.json b/plugins/browser-plugin-browser-features/CHANGELOG.json deleted file mode 100644 index 72d38ce63..000000000 --- a/plugins/browser-plugin-browser-features/CHANGELOG.json +++ /dev/null @@ -1,425 +0,0 @@ -{ - "name": "@snowplow/browser-plugin-browser-features", - "entries": [ - { - "version": "3.24.6", - "tag": "@snowplow/browser-plugin-browser-features_v3.24.6", - "date": "Mon, 28 Oct 2024 10:23:28 GMT", - "comments": {} - }, - { - "version": "3.24.5", - "tag": "@snowplow/browser-plugin-browser-features_v3.24.5", - "date": "Fri, 25 Oct 2024 08:53:04 GMT", - "comments": {} - }, - { - "version": "3.24.4", - "tag": "@snowplow/browser-plugin-browser-features_v3.24.4", - "date": "Thu, 26 Sep 2024 06:10:22 GMT", - "comments": {} - }, - { - "version": "3.24.3", - "tag": "@snowplow/browser-plugin-browser-features_v3.24.3", - "date": "Tue, 03 Sep 2024 08:15:14 GMT", - "comments": { - "none": [ - { - "comment": "Upgrade supported Node.JS versions in build to 18 - 20 and upgrade rush" - } - ] - } - }, - { - "version": "3.24.2", - "tag": "@snowplow/browser-plugin-browser-features_v3.24.2", - "date": "Wed, 24 Jul 2024 08:59:00 GMT", - "comments": {} - }, - { - "version": "3.24.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.24.1", - "date": "Tue, 02 Jul 2024 07:08:17 GMT", - "comments": {} - }, - { - "version": "3.24.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.24.0", - "date": "Tue, 25 Jun 2024 08:31:05 GMT", - "comments": {} - }, - { - "version": "3.23.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.23.1", - "date": "Tue, 04 Jun 2024 13:34:45 GMT", - "comments": {} - }, - { - "version": "3.23.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.23.0", - "date": "Thu, 28 Mar 2024 11:28:45 GMT", - "comments": {} - }, - { - "version": "3.22.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.22.1", - "date": "Wed, 13 Mar 2024 08:39:48 GMT", - "comments": {} - }, - { - "version": "3.22.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.22.0", - "date": "Fri, 08 Mar 2024 08:13:04 GMT", - "comments": {} - }, - { - "version": "3.21.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.21.0", - "date": "Mon, 29 Jan 2024 08:34:06 GMT", - "comments": {} - }, - { - "version": "3.20.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.20.0", - "date": "Mon, 15 Jan 2024 14:41:16 GMT", - "comments": {} - }, - { - "version": "3.19.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.19.0", - "date": "Thu, 14 Dec 2023 10:45:22 GMT", - "comments": {} - }, - { - "version": "3.18.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.18.0", - "date": "Mon, 04 Dec 2023 13:44:02 GMT", - "comments": {} - }, - { - "version": "3.17.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.17.0", - "date": "Tue, 14 Nov 2023 17:58:26 GMT", - "comments": {} - }, - { - "version": "3.16.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.16.0", - "date": "Mon, 16 Oct 2023 14:58:08 GMT", - "comments": {} - }, - { - "version": "3.15.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.15.0", - "date": "Mon, 28 Aug 2023 14:25:14 GMT", - "comments": {} - }, - { - "version": "3.14.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.14.0", - "date": "Thu, 10 Aug 2023 13:56:44 GMT", - "comments": {} - }, - { - "version": "3.13.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.13.1", - "date": "Thu, 29 Jun 2023 14:20:06 GMT", - "comments": {} - }, - { - "version": "3.13.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.13.0", - "date": "Tue, 20 Jun 2023 07:44:23 GMT", - "comments": {} - }, - { - "version": "3.12.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.12.1", - "date": "Thu, 15 Jun 2023 10:05:37 GMT", - "comments": {} - }, - { - "version": "3.12.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.12.0", - "date": "Mon, 05 Jun 2023 11:51:22 GMT", - "comments": {} - }, - { - "version": "3.11.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.11.0", - "date": "Wed, 24 May 2023 15:56:17 GMT", - "comments": {} - }, - { - "version": "3.10.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.10.1", - "date": "Fri, 12 May 2023 06:59:31 GMT", - "comments": {} - }, - { - "version": "3.10.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.10.0", - "date": "Thu, 11 May 2023 08:29:15 GMT", - "comments": {} - }, - { - "version": "3.9.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.9.0", - "date": "Thu, 30 Mar 2023 13:46:56 GMT", - "comments": {} - }, - { - "version": "3.8.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.8.0", - "date": "Tue, 03 Jan 2023 15:36:33 GMT", - "comments": {} - }, - { - "version": "3.7.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.7.0", - "date": "Mon, 31 Oct 2022 06:26:28 GMT", - "comments": {} - }, - { - "version": "3.6.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.6.0", - "date": "Thu, 15 Sep 2022 07:55:20 GMT", - "comments": {} - }, - { - "version": "3.5.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.5.0", - "date": "Fri, 10 Jun 2022 18:57:46 GMT", - "comments": {} - }, - { - "version": "3.4.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.4.0", - "date": "Thu, 07 Apr 2022 11:56:26 GMT", - "comments": { - "none": [ - { - "comment": "Bump dependencies (#1067)" - } - ] - } - }, - { - "version": "3.3.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.3.1", - "date": "Wed, 23 Feb 2022 19:27:40 GMT", - "comments": {} - }, - { - "version": "3.3.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.3.0", - "date": "Mon, 31 Jan 2022 15:58:10 GMT", - "comments": {} - }, - { - "version": "3.2.3", - "tag": "@snowplow/browser-plugin-browser-features_v3.2.3", - "date": "Tue, 18 Jan 2022 16:23:52 GMT", - "comments": { - "none": [ - { - "comment": "Bump Copyright to 2022 (#1040)" - } - ] - } - }, - { - "version": "3.2.2", - "tag": "@snowplow/browser-plugin-browser-features_v3.2.2", - "date": "Fri, 14 Jan 2022 10:17:59 GMT", - "comments": {} - }, - { - "version": "3.2.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.2.1", - "date": "Wed, 12 Jan 2022 09:50:29 GMT", - "comments": {} - }, - { - "version": "3.2.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.2.0", - "date": "Tue, 11 Jan 2022 12:53:22 GMT", - "comments": {} - }, - { - "version": "3.1.6", - "tag": "@snowplow/browser-plugin-browser-features_v3.1.6", - "date": "Tue, 19 Oct 2021 09:17:22 GMT", - "comments": { - "none": [ - { - "comment": "Fix failing build on ARM Macs (#1012)" - } - ] - } - }, - { - "version": "3.1.5", - "tag": "@snowplow/browser-plugin-browser-features_v3.1.5", - "date": "Fri, 01 Oct 2021 08:09:20 GMT", - "comments": {} - }, - { - "version": "3.1.4", - "tag": "@snowplow/browser-plugin-browser-features_v3.1.4", - "date": "Tue, 21 Sep 2021 14:59:36 GMT", - "comments": {} - }, - { - "version": "3.1.3", - "tag": "@snowplow/browser-plugin-browser-features_v3.1.3", - "date": "Mon, 23 Aug 2021 10:13:18 GMT", - "comments": {} - }, - { - "version": "3.1.2", - "tag": "@snowplow/browser-plugin-browser-features_v3.1.2", - "date": "Mon, 16 Aug 2021 12:59:59 GMT", - "comments": { - "none": [ - { - "comment": "Update READMEs with correct Node requirements (#994)" - } - ] - } - }, - { - "version": "3.1.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.1.1", - "date": "Wed, 04 Aug 2021 10:12:25 GMT", - "comments": { - "none": [ - { - "comment": "Bump tslib to 2.3.0 (#986)" - }, - { - "comment": "Bump typescript to 4.3.5 (#987)" - }, - { - "comment": "Switch from @wessberg/rollup-plugin-ts to rollup-plugin-ts (#988)" - } - ] - } - }, - { - "version": "3.1.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.1.0", - "date": "Fri, 14 May 2021 10:45:32 GMT", - "comments": {} - }, - { - "version": "3.0.3", - "tag": "@snowplow/browser-plugin-browser-features_v3.0.3", - "date": "Wed, 21 Apr 2021 12:35:06 GMT", - "comments": {} - }, - { - "version": "3.0.2", - "tag": "@snowplow/browser-plugin-browser-features_v3.0.2", - "date": "Thu, 15 Apr 2021 21:07:39 GMT", - "comments": {} - }, - { - "version": "3.0.1", - "tag": "@snowplow/browser-plugin-browser-features_v3.0.1", - "date": "Wed, 14 Apr 2021 16:30:05 GMT", - "comments": { - "none": [ - { - "comment": "Add peerDependencies to plugins (#950)" - }, - { - "comment": "Mark packages as sideEffect: false (#951)" - }, - { - "comment": "Add unit tests to plugin track* functions (#954)" - } - ] - } - }, - { - "version": "3.0.0", - "tag": "@snowplow/browser-plugin-browser-features_v3.0.0", - "date": "Wed, 31 Mar 2021 14:46:47 GMT", - "comments": { - "none": [ - { - "comment": "Allow plugins to be dynamically loaded when using tracker (#918)" - }, - { - "comment": "Bump rollup to 2.41 (#916)" - }, - { - "comment": "Publish UMD versions of plugins to GitHub release (#923)" - }, - { - "comment": "Remove module level references to window and document (close #928)" - }, - { - "comment": "Ensure browser-tracker API methods catch exceptions (#919)" - }, - { - "comment": "Introduce TSDoc comments and extract interfaces where appropriate (#906)" - }, - { - "comment": "Bump major version to v3 and update READMEs (#904)" - }, - { - "comment": "Improve Core API for module bundlers which support treeshaking (#903)" - }, - { - "comment": "Rename @snowplow/browser-core to @snowplow/browser-tracker-core (#901)" - }, - { - "comment": "Publish lite version of sp.js (#900)" - }, - { - "comment": "Ensure correct 3-Clause BSD License notices are being used (#316)" - }, - { - "comment": "Improve API for module bundlers which support treeshaking (#899)" - }, - { - "comment": "Bump rush to 5.39 (#895)" - }, - { - "comment": "Port to TypeScript (#72)" - }, - { - "comment": "Make sp.js build process modular (#450)" - }, - { - "comment": "Create @snowplow/browser-tracker package for npm distribution (#541)" - }, - { - "comment": "Split auto contexts into plugins (#880)" - }, - { - "comment": "Add rush to manage monorepo (#883)" - }, - { - "comment": "Add ES Module builds (#882)" - }, - { - "comment": "Cleanup deprecated methods (#557)" - }, - { - "comment": "Update publishing process for rush (#907)" - }, - { - "comment": "Change white and black lists to allow and deny lists (#908)" - }, - { - "comment": "Create rush change files for major version 3 release (#909)" - } - ] - } - } - ] -} diff --git a/plugins/browser-plugin-browser-features/CHANGELOG.md b/plugins/browser-plugin-browser-features/CHANGELOG.md deleted file mode 100644 index 0b92ac329..000000000 --- a/plugins/browser-plugin-browser-features/CHANGELOG.md +++ /dev/null @@ -1,291 +0,0 @@ -# Change Log - @snowplow/browser-plugin-browser-features - -This log was last generated on Mon, 28 Oct 2024 10:23:28 GMT and should not be manually modified. - -## 3.24.6 -Mon, 28 Oct 2024 10:23:28 GMT - -_Version update only_ - -## 3.24.5 -Fri, 25 Oct 2024 08:53:04 GMT - -_Version update only_ - -## 3.24.4 -Thu, 26 Sep 2024 06:10:22 GMT - -_Version update only_ - -## 3.24.3 -Tue, 03 Sep 2024 08:15:14 GMT - -### Updates - -- Upgrade supported Node.JS versions in build to 18 - 20 and upgrade rush - -## 3.24.2 -Wed, 24 Jul 2024 08:59:00 GMT - -_Version update only_ - -## 3.24.1 -Tue, 02 Jul 2024 07:08:17 GMT - -_Version update only_ - -## 3.24.0 -Tue, 25 Jun 2024 08:31:05 GMT - -_Version update only_ - -## 3.23.1 -Tue, 04 Jun 2024 13:34:45 GMT - -_Version update only_ - -## 3.23.0 -Thu, 28 Mar 2024 11:28:45 GMT - -_Version update only_ - -## 3.22.1 -Wed, 13 Mar 2024 08:39:48 GMT - -_Version update only_ - -## 3.22.0 -Fri, 08 Mar 2024 08:13:04 GMT - -_Version update only_ - -## 3.21.0 -Mon, 29 Jan 2024 08:34:06 GMT - -_Version update only_ - -## 3.20.0 -Mon, 15 Jan 2024 14:41:16 GMT - -_Version update only_ - -## 3.19.0 -Thu, 14 Dec 2023 10:45:22 GMT - -_Version update only_ - -## 3.18.0 -Mon, 04 Dec 2023 13:44:02 GMT - -_Version update only_ - -## 3.17.0 -Tue, 14 Nov 2023 17:58:26 GMT - -_Version update only_ - -## 3.16.0 -Mon, 16 Oct 2023 14:58:08 GMT - -_Version update only_ - -## 3.15.0 -Mon, 28 Aug 2023 14:25:14 GMT - -_Version update only_ - -## 3.14.0 -Thu, 10 Aug 2023 13:56:44 GMT - -_Version update only_ - -## 3.13.1 -Thu, 29 Jun 2023 14:20:06 GMT - -_Version update only_ - -## 3.13.0 -Tue, 20 Jun 2023 07:44:23 GMT - -_Version update only_ - -## 3.12.1 -Thu, 15 Jun 2023 10:05:37 GMT - -_Version update only_ - -## 3.12.0 -Mon, 05 Jun 2023 11:51:22 GMT - -_Version update only_ - -## 3.11.0 -Wed, 24 May 2023 15:56:17 GMT - -_Version update only_ - -## 3.10.1 -Fri, 12 May 2023 06:59:31 GMT - -_Version update only_ - -## 3.10.0 -Thu, 11 May 2023 08:29:15 GMT - -_Version update only_ - -## 3.9.0 -Thu, 30 Mar 2023 13:46:56 GMT - -_Version update only_ - -## 3.8.0 -Tue, 03 Jan 2023 15:36:33 GMT - -_Version update only_ - -## 3.7.0 -Mon, 31 Oct 2022 06:26:28 GMT - -_Version update only_ - -## 3.6.0 -Thu, 15 Sep 2022 07:55:20 GMT - -_Version update only_ - -## 3.5.0 -Fri, 10 Jun 2022 18:57:46 GMT - -_Version update only_ - -## 3.4.0 -Thu, 07 Apr 2022 11:56:26 GMT - -### Updates - -- Bump dependencies (#1067) - -## 3.3.1 -Wed, 23 Feb 2022 19:27:40 GMT - -_Version update only_ - -## 3.3.0 -Mon, 31 Jan 2022 15:58:10 GMT - -_Version update only_ - -## 3.2.3 -Tue, 18 Jan 2022 16:23:52 GMT - -### Updates - -- Bump Copyright to 2022 (#1040) - -## 3.2.2 -Fri, 14 Jan 2022 10:17:59 GMT - -_Version update only_ - -## 3.2.1 -Wed, 12 Jan 2022 09:50:29 GMT - -_Version update only_ - -## 3.2.0 -Tue, 11 Jan 2022 12:53:22 GMT - -_Version update only_ - -## 3.1.6 -Tue, 19 Oct 2021 09:17:22 GMT - -### Updates - -- Fix failing build on ARM Macs (#1012) - -## 3.1.5 -Fri, 01 Oct 2021 08:09:20 GMT - -_Version update only_ - -## 3.1.4 -Tue, 21 Sep 2021 14:59:36 GMT - -_Version update only_ - -## 3.1.3 -Mon, 23 Aug 2021 10:13:18 GMT - -_Version update only_ - -## 3.1.2 -Mon, 16 Aug 2021 12:59:59 GMT - -### Updates - -- Update READMEs with correct Node requirements (#994) - -## 3.1.1 -Wed, 04 Aug 2021 10:12:25 GMT - -### Updates - -- Bump tslib to 2.3.0 (#986) -- Bump typescript to 4.3.5 (#987) -- Switch from @wessberg/rollup-plugin-ts to rollup-plugin-ts (#988) - -## 3.1.0 -Fri, 14 May 2021 10:45:32 GMT - -_Version update only_ - -## 3.0.3 -Wed, 21 Apr 2021 12:35:06 GMT - -_Version update only_ - -## 3.0.2 -Thu, 15 Apr 2021 21:07:39 GMT - -_Version update only_ - -## 3.0.1 -Wed, 14 Apr 2021 16:30:05 GMT - -### Updates - -- Add peerDependencies to plugins (#950) -- Mark packages as sideEffect: false (#951) -- Add unit tests to plugin track* functions (#954) - -## 3.0.0 -Wed, 31 Mar 2021 14:46:47 GMT - -### Updates - -- Allow plugins to be dynamically loaded when using tracker (#918) -- Bump rollup to 2.41 (#916) -- Publish UMD versions of plugins to GitHub release (#923) -- Remove module level references to window and document (close #928) -- Ensure browser-tracker API methods catch exceptions (#919) -- Introduce TSDoc comments and extract interfaces where appropriate (#906) -- Bump major version to v3 and update READMEs (#904) -- Improve Core API for module bundlers which support treeshaking (#903) -- Rename @snowplow/browser-core to @snowplow/browser-tracker-core (#901) -- Publish lite version of sp.js (#900) -- Ensure correct 3-Clause BSD License notices are being used (#316) -- Improve API for module bundlers which support treeshaking (#899) -- Bump rush to 5.39 (#895) -- Port to TypeScript (#72) -- Make sp.js build process modular (#450) -- Create @snowplow/browser-tracker package for npm distribution (#541) -- Split auto contexts into plugins (#880) -- Add rush to manage monorepo (#883) -- Add ES Module builds (#882) -- Cleanup deprecated methods (#557) -- Update publishing process for rush (#907) -- Change white and black lists to allow and deny lists (#908) -- Create rush change files for major version 3 release (#909) - diff --git a/plugins/browser-plugin-browser-features/LICENSE b/plugins/browser-plugin-browser-features/LICENSE deleted file mode 100644 index 76f1946ea..000000000 --- a/plugins/browser-plugin-browser-features/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/browser-plugin-browser-features/README.md b/plugins/browser-plugin-browser-features/README.md deleted file mode 100644 index ecac879e2..000000000 --- a/plugins/browser-plugin-browser-features/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Snowplow Browser Feature Tracking - -[![npm version][npm-image]][npm-url] -[![License][license-image]](LICENSE) - -Browser Plugin to be used with `@snowplow/browser-tracker`. - -Adds Browser Features to your Snowplow tracking. Identifies available MIME Types. - -## Maintainer quick start - -Part of the Snowplow JavaScript Tracker monorepo. -Build with [Node.js](https://nodejs.org/en/) (18 - 20) and [Rush](https://rushjs.io/). - -### Setup repository - -```bash -npm install -g @microsoft/rush -git clone https://github.com/snowplow/snowplow-javascript-tracker.git -rush update -``` - -## Package Installation - -With npm: - -```bash -npm install @snowplow/browser-plugin-browser-features -``` - -## Usage - -Initialize your tracker with the BrowserFeaturesPlugin: - -```js -import { newTracker } from '@snowplow/browser-tracker'; -import { BrowserFeaturesPlugin } from '@snowplow/browser-plugin-browser-features'; - -newTracker('sp1', '{{collector}}', { plugins: [ BrowserFeaturesPlugin() ] }); // Also stores reference at module level -``` - -## Copyright and license - -Licensed and distributed under the [BSD 3-Clause License](LICENSE) ([An OSI Approved License][osi]). - -Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang. - -All rights reserved. - -[npm-url]: https://www.npmjs.com/package/@snowplow/browser-plugin-browser-features -[npm-image]: https://img.shields.io/npm/v/@snowplow/browser-plugin-browser-features -[docs]: https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/javascript-tracker/ -[osi]: https://opensource.org/licenses/BSD-3-Clause -[license-image]: https://img.shields.io/npm/l/@snowplow/browser-plugin-browser-features diff --git a/plugins/browser-plugin-browser-features/package.json b/plugins/browser-plugin-browser-features/package.json deleted file mode 100644 index dcc5f74d4..000000000 --- a/plugins/browser-plugin-browser-features/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "@snowplow/browser-plugin-browser-features", - "version": "3.24.6", - "description": "Attaches browser features to Snowplow events", - "homepage": "http://bit.ly/sp-js", - "bugs": "https://github.com/snowplow/snowplow-javascript-tracker/issues", - "repository": { - "type": "git", - "url": "https://github.com/snowplow/snowplow-javascript-tracker.git" - }, - "license": "BSD-3-Clause", - "author": "Paul Boocock", - "sideEffects": false, - "main": "./dist/index.umd.js", - "module": "./dist/index.module.js", - "types": "./dist/index.module.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "rollup -c --silent --failAfterWarnings", - "test": "jest" - }, - "dependencies": { - "@snowplow/browser-tracker-core": "workspace:*", - "tslib": "^2.3.1" - }, - "devDependencies": { - "@ampproject/rollup-plugin-closure-compiler": "~0.27.0", - "@rollup/plugin-commonjs": "~21.0.2", - "@rollup/plugin-node-resolve": "~13.1.3", - "@snowplow/tracker-core": "workspace:*", - "@types/jest": "~27.4.1", - "@types/jsdom": "~16.2.14", - "@typescript-eslint/eslint-plugin": "~5.15.0", - "@typescript-eslint/parser": "~5.15.0", - "eslint": "~8.11.0", - "jest": "~27.5.1", - "jest-environment-jsdom": "~27.5.1", - "jest-environment-jsdom-global": "~3.0.0", - "jest-standard-reporter": "~2.0.0", - "rollup": "~2.70.1", - "rollup-plugin-cleanup": "~3.2.1", - "rollup-plugin-license": "~2.6.1", - "rollup-plugin-terser": "~7.0.2", - "rollup-plugin-ts": "~2.0.5", - "ts-jest": "~27.1.3", - "typescript": "~4.6.2" - }, - "peerDependencies": { - "@snowplow/browser-tracker": "~3.24.6" - } -} diff --git a/plugins/browser-plugin-browser-features/rollup.config.js b/plugins/browser-plugin-browser-features/rollup.config.js deleted file mode 100644 index 74acf7118..000000000 --- a/plugins/browser-plugin-browser-features/rollup.config.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import ts from 'rollup-plugin-ts'; // Prefered over @rollup/plugin-typescript as it bundles .d.ts files -import { banner } from '../../banner'; -import compiler from '@ampproject/rollup-plugin-closure-compiler'; -import { terser } from 'rollup-plugin-terser'; -import cleanup from 'rollup-plugin-cleanup'; -import pkg from './package.json'; -import { builtinModules } from 'module'; - -const umdPlugins = [nodeResolve({ browser: true }), commonjs(), ts()]; -const umdName = 'snowplowBrowserFeatures'; - -export default [ - // CommonJS (for Node) and ES module (for bundlers) build. - { - input: './src/index.ts', - plugins: [...umdPlugins, banner()], - treeshake: { moduleSideEffects: ['sha1'] }, - output: [{ file: pkg.main, format: 'umd', sourcemap: true, name: umdName }], - }, - { - input: './src/index.ts', - plugins: [...umdPlugins, compiler(), terser(), cleanup({ comments: 'none' }), banner()], - treeshake: { moduleSideEffects: ['sha1'] }, - output: [{ file: pkg.main.replace('.js', '.min.js'), format: 'umd', sourcemap: true, name: umdName }], - }, - { - input: './src/index.ts', - external: [...builtinModules, ...Object.keys(pkg.dependencies), ...Object.keys(pkg.devDependencies)], - plugins: [ - ts(), // so Rollup can convert TypeScript to JavaScript - banner(), - ], - output: [{ file: pkg.module, format: 'es', sourcemap: true }], - }, -]; diff --git a/plugins/browser-plugin-browser-features/src/index.ts b/plugins/browser-plugin-browser-features/src/index.ts deleted file mode 100644 index 2467dad8e..000000000 --- a/plugins/browser-plugin-browser-features/src/index.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import { isFunction, BrowserPlugin, BrowserTracker } from '@snowplow/browser-tracker-core'; - -declare global { - interface MimeTypeArray { - [index: string]: MimeType; - } -} - -const pluginMap: Record = { - // document types - pdf: 'application/pdf', - - // media players - qt: 'video/quicktime', - realp: 'audio/x-pn-realaudio-plugin', - wma: 'application/x-mplayer2', - - // interactive multimedia - dir: 'application/x-director', - fla: 'application/x-shockwave-flash', - - // RIA - java: 'application/x-java-vm', - gears: 'application/x-googlegears', - ag: 'application/x-silverlight', -}; - -/** - * Adds the available MIME Types to each event - */ -export function BrowserFeaturesPlugin(): BrowserPlugin { - return { - activateBrowserPlugin: (tracker: BrowserTracker) => { - const navigatorAlias = navigator; - // General plugin detection - if (navigatorAlias.mimeTypes && navigatorAlias.mimeTypes.length) { - for (const i in pluginMap) { - if (Object.prototype.hasOwnProperty.call(pluginMap, i)) { - let mimeType = navigatorAlias.mimeTypes[pluginMap[i]]; - if (mimeType) { - tracker.core.addPayloadPair('f_' + i, mimeType.enabledPlugin ? '1' : '0'); - } - } - } - } - - // Firefox - if (isFunction((window as any).GearsFactory)) { - tracker.core.addPayloadPair('f_gears', '1'); - } - }, - }; -} diff --git a/plugins/browser-plugin-browser-features/test/browser_features.test.ts b/plugins/browser-plugin-browser-features/test/browser_features.test.ts deleted file mode 100644 index 88b76dfc3..000000000 --- a/plugins/browser-plugin-browser-features/test/browser_features.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import { BrowserTracker } from '@snowplow/browser-tracker-core'; -import { buildLinkClick, trackerCore } from '@snowplow/tracker-core'; -import { JSDOM } from 'jsdom'; -import { BrowserFeaturesPlugin } from '../src/index'; - -declare var jsdom: JSDOM; - -describe('Browser Features plugin', () => { - it('Returns undefined or false for unavailable mimeTypes', (done) => { - Object.defineProperty(jsdom.window.navigator, 'mimeTypes', { - value: { - 'application/pdf': { enabledPlugin: false }, - length: 1, - }, - configurable: true, - }); - - const core = trackerCore({ - base64: false, - callback: (payloadBuilder) => { - const payload = payloadBuilder.build(); - expect(payload['f_pdf']).toBe('0'); - expect(payload['f_qt']).toBeUndefined(); - done(); - }, - }); - - BrowserFeaturesPlugin().activateBrowserPlugin?.({ core } as BrowserTracker); - core.track(buildLinkClick({ targetUrl: 'https://example.com' })); - }); - - it('Returns values for available mimeTypes', (done) => { - Object.defineProperty(jsdom.window.navigator, 'mimeTypes', { - value: { - 'application/pdf': { enabledPlugin: true }, - 'video/quicktime': { enabledPlugin: true }, - 'audio/x-pn-realaudio-plugin': { enabledPlugin: true }, - 'application/x-mplayer2': { enabledPlugin: true }, - 'application/x-director': { enabledPlugin: true }, - 'application/x-shockwave-flash': { enabledPlugin: true }, - 'application/x-java-vm': { enabledPlugin: true }, - 'application/x-googlegears': { enabledPlugin: true }, - 'application/x-silverlight': { enabledPlugin: true }, - length: 9, - }, - configurable: true, - }); - - const core = trackerCore({ - base64: false, - callback: (payloadBuilder) => { - const payload = payloadBuilder.build(); - expect(payload['f_pdf']).toBe('1'); - expect(payload['f_qt']).toBe('1'); - expect(payload['f_realp']).toBe('1'); - expect(payload['f_wma']).toBe('1'); - expect(payload['f_dir']).toBe('1'); - expect(payload['f_fla']).toBe('1'); - expect(payload['f_java']).toBe('1'); - expect(payload['f_gears']).toBe('1'); - expect(payload['f_ag']).toBe('1'); - done(); - }, - }); - - BrowserFeaturesPlugin().activateBrowserPlugin?.({ core } as BrowserTracker); - core.track(buildLinkClick({ targetUrl: 'https://example.com' })); - }); -}); diff --git a/plugins/browser-plugin-browser-features/tsconfig.json b/plugins/browser-plugin-browser-features/tsconfig.json deleted file mode 100644 index 4082f16a5..000000000 --- a/plugins/browser-plugin-browser-features/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tsconfig.json" -} diff --git a/plugins/browser-plugin-button-click-tracking/src/api.ts b/plugins/browser-plugin-button-click-tracking/src/api.ts index d3979ae9c..51fb16d37 100644 --- a/plugins/browser-plugin-button-click-tracking/src/api.ts +++ b/plugins/browser-plugin-button-click-tracking/src/api.ts @@ -62,7 +62,7 @@ export function enableButtonClickTracking( }; const addClickListener = () => { - document.addEventListener('click', _listeners[trackerId]); + document.addEventListener('click', _listeners[trackerId], true); }; if (_trackers[trackerId]?.sharedState.hasLoaded) { @@ -83,7 +83,7 @@ export function enableButtonClickTracking( export function disableButtonClickTracking() { for (const trackerId in _trackers) { if (_listeners[trackerId]) { - document.removeEventListener('click', _listeners[trackerId]); + document.removeEventListener('click', _listeners[trackerId], true); } } } @@ -97,7 +97,7 @@ export function disableButtonClickTracking() { * @param context - The dynamic context which will be evaluated for each button click event */ function eventHandler(event: MouseEvent, trackerId: string, filter: FilterFunction, context?: DynamicContext) { - let elem = event.target as HTMLElement | null; + let elem = (event.composed ? event.composedPath()[0] : event.target) as HTMLElement | null; while (elem) { if (elem instanceof HTMLButtonElement || (elem instanceof HTMLInputElement && elem.type === 'button')) { if (filter(elem)) { diff --git a/plugins/browser-plugin-consent/CHANGELOG.json b/plugins/browser-plugin-consent/CHANGELOG.json deleted file mode 100644 index b9cbec269..000000000 --- a/plugins/browser-plugin-consent/CHANGELOG.json +++ /dev/null @@ -1,422 +0,0 @@ -{ - "name": "@snowplow/browser-plugin-consent", - "entries": [ - { - "version": "3.24.6", - "tag": "@snowplow/browser-plugin-consent_v3.24.6", - "date": "Mon, 28 Oct 2024 10:23:28 GMT", - "comments": {} - }, - { - "version": "3.24.5", - "tag": "@snowplow/browser-plugin-consent_v3.24.5", - "date": "Fri, 25 Oct 2024 08:53:04 GMT", - "comments": {} - }, - { - "version": "3.24.4", - "tag": "@snowplow/browser-plugin-consent_v3.24.4", - "date": "Thu, 26 Sep 2024 06:10:22 GMT", - "comments": {} - }, - { - "version": "3.24.3", - "tag": "@snowplow/browser-plugin-consent_v3.24.3", - "date": "Tue, 03 Sep 2024 08:15:14 GMT", - "comments": { - "none": [ - { - "comment": "Upgrade supported Node.JS versions in build to 18 - 20 and upgrade rush" - } - ] - } - }, - { - "version": "3.24.2", - "tag": "@snowplow/browser-plugin-consent_v3.24.2", - "date": "Wed, 24 Jul 2024 08:59:00 GMT", - "comments": {} - }, - { - "version": "3.24.1", - "tag": "@snowplow/browser-plugin-consent_v3.24.1", - "date": "Tue, 02 Jul 2024 07:08:17 GMT", - "comments": {} - }, - { - "version": "3.24.0", - "tag": "@snowplow/browser-plugin-consent_v3.24.0", - "date": "Tue, 25 Jun 2024 08:31:05 GMT", - "comments": {} - }, - { - "version": "3.23.1", - "tag": "@snowplow/browser-plugin-consent_v3.23.1", - "date": "Tue, 04 Jun 2024 13:34:45 GMT", - "comments": {} - }, - { - "version": "3.23.0", - "tag": "@snowplow/browser-plugin-consent_v3.23.0", - "date": "Thu, 28 Mar 2024 11:28:45 GMT", - "comments": {} - }, - { - "version": "3.22.1", - "tag": "@snowplow/browser-plugin-consent_v3.22.1", - "date": "Wed, 13 Mar 2024 08:39:48 GMT", - "comments": {} - }, - { - "version": "3.22.0", - "tag": "@snowplow/browser-plugin-consent_v3.22.0", - "date": "Fri, 08 Mar 2024 08:13:04 GMT", - "comments": {} - }, - { - "version": "3.21.0", - "tag": "@snowplow/browser-plugin-consent_v3.21.0", - "date": "Mon, 29 Jan 2024 08:34:06 GMT", - "comments": {} - }, - { - "version": "3.20.0", - "tag": "@snowplow/browser-plugin-consent_v3.20.0", - "date": "Mon, 15 Jan 2024 14:41:16 GMT", - "comments": {} - }, - { - "version": "3.19.0", - "tag": "@snowplow/browser-plugin-consent_v3.19.0", - "date": "Thu, 14 Dec 2023 10:45:22 GMT", - "comments": {} - }, - { - "version": "3.18.0", - "tag": "@snowplow/browser-plugin-consent_v3.18.0", - "date": "Mon, 04 Dec 2023 13:44:02 GMT", - "comments": {} - }, - { - "version": "3.17.0", - "tag": "@snowplow/browser-plugin-consent_v3.17.0", - "date": "Tue, 14 Nov 2023 17:58:26 GMT", - "comments": {} - }, - { - "version": "3.16.0", - "tag": "@snowplow/browser-plugin-consent_v3.16.0", - "date": "Mon, 16 Oct 2023 14:58:08 GMT", - "comments": {} - }, - { - "version": "3.15.0", - "tag": "@snowplow/browser-plugin-consent_v3.15.0", - "date": "Mon, 28 Aug 2023 14:25:14 GMT", - "comments": {} - }, - { - "version": "3.14.0", - "tag": "@snowplow/browser-plugin-consent_v3.14.0", - "date": "Thu, 10 Aug 2023 13:56:44 GMT", - "comments": {} - }, - { - "version": "3.13.1", - "tag": "@snowplow/browser-plugin-consent_v3.13.1", - "date": "Thu, 29 Jun 2023 14:20:06 GMT", - "comments": {} - }, - { - "version": "3.13.0", - "tag": "@snowplow/browser-plugin-consent_v3.13.0", - "date": "Tue, 20 Jun 2023 07:44:23 GMT", - "comments": {} - }, - { - "version": "3.12.1", - "tag": "@snowplow/browser-plugin-consent_v3.12.1", - "date": "Thu, 15 Jun 2023 10:05:37 GMT", - "comments": {} - }, - { - "version": "3.12.0", - "tag": "@snowplow/browser-plugin-consent_v3.12.0", - "date": "Mon, 05 Jun 2023 11:51:22 GMT", - "comments": {} - }, - { - "version": "3.11.0", - "tag": "@snowplow/browser-plugin-consent_v3.11.0", - "date": "Wed, 24 May 2023 15:56:17 GMT", - "comments": {} - }, - { - "version": "3.10.1", - "tag": "@snowplow/browser-plugin-consent_v3.10.1", - "date": "Fri, 12 May 2023 06:59:31 GMT", - "comments": {} - }, - { - "version": "3.10.0", - "tag": "@snowplow/browser-plugin-consent_v3.10.0", - "date": "Thu, 11 May 2023 08:29:15 GMT", - "comments": {} - }, - { - "version": "3.9.0", - "tag": "@snowplow/browser-plugin-consent_v3.9.0", - "date": "Thu, 30 Mar 2023 13:46:56 GMT", - "comments": {} - }, - { - "version": "3.8.0", - "tag": "@snowplow/browser-plugin-consent_v3.8.0", - "date": "Tue, 03 Jan 2023 15:36:33 GMT", - "comments": {} - }, - { - "version": "3.7.0", - "tag": "@snowplow/browser-plugin-consent_v3.7.0", - "date": "Mon, 31 Oct 2022 06:26:28 GMT", - "comments": {} - }, - { - "version": "3.6.0", - "tag": "@snowplow/browser-plugin-consent_v3.6.0", - "date": "Thu, 15 Sep 2022 07:55:20 GMT", - "comments": {} - }, - { - "version": "3.5.0", - "tag": "@snowplow/browser-plugin-consent_v3.5.0", - "date": "Fri, 10 Jun 2022 18:57:46 GMT", - "comments": {} - }, - { - "version": "3.4.0", - "tag": "@snowplow/browser-plugin-consent_v3.4.0", - "date": "Thu, 07 Apr 2022 11:56:26 GMT", - "comments": { - "none": [ - { - "comment": "Bump dependencies (#1067)" - } - ] - } - }, - { - "version": "3.3.1", - "tag": "@snowplow/browser-plugin-consent_v3.3.1", - "date": "Wed, 23 Feb 2022 19:27:40 GMT", - "comments": {} - }, - { - "version": "3.3.0", - "tag": "@snowplow/browser-plugin-consent_v3.3.0", - "date": "Mon, 31 Jan 2022 15:58:10 GMT", - "comments": {} - }, - { - "version": "3.2.3", - "tag": "@snowplow/browser-plugin-consent_v3.2.3", - "date": "Tue, 18 Jan 2022 16:23:52 GMT", - "comments": { - "none": [ - { - "comment": "Bump Copyright to 2022 (#1040)" - } - ] - } - }, - { - "version": "3.2.2", - "tag": "@snowplow/browser-plugin-consent_v3.2.2", - "date": "Fri, 14 Jan 2022 10:17:59 GMT", - "comments": {} - }, - { - "version": "3.2.1", - "tag": "@snowplow/browser-plugin-consent_v3.2.1", - "date": "Wed, 12 Jan 2022 09:50:29 GMT", - "comments": {} - }, - { - "version": "3.2.0", - "tag": "@snowplow/browser-plugin-consent_v3.2.0", - "date": "Tue, 11 Jan 2022 12:53:22 GMT", - "comments": {} - }, - { - "version": "3.1.6", - "tag": "@snowplow/browser-plugin-consent_v3.1.6", - "date": "Tue, 19 Oct 2021 09:17:22 GMT", - "comments": { - "none": [ - { - "comment": "Fix failing build on ARM Macs (#1012)" - } - ] - } - }, - { - "version": "3.1.5", - "tag": "@snowplow/browser-plugin-consent_v3.1.5", - "date": "Fri, 01 Oct 2021 08:09:20 GMT", - "comments": {} - }, - { - "version": "3.1.4", - "tag": "@snowplow/browser-plugin-consent_v3.1.4", - "date": "Tue, 21 Sep 2021 14:59:36 GMT", - "comments": {} - }, - { - "version": "3.1.3", - "tag": "@snowplow/browser-plugin-consent_v3.1.3", - "date": "Mon, 23 Aug 2021 10:13:18 GMT", - "comments": {} - }, - { - "version": "3.1.2", - "tag": "@snowplow/browser-plugin-consent_v3.1.2", - "date": "Mon, 16 Aug 2021 12:59:59 GMT", - "comments": { - "none": [ - { - "comment": "Update READMEs with correct Node requirements (#994)" - } - ] - } - }, - { - "version": "3.1.1", - "tag": "@snowplow/browser-plugin-consent_v3.1.1", - "date": "Wed, 04 Aug 2021 10:12:25 GMT", - "comments": { - "none": [ - { - "comment": "Bump tslib to 2.3.0 (#986)" - }, - { - "comment": "Bump typescript to 4.3.5 (#987)" - }, - { - "comment": "Switch from @wessberg/rollup-plugin-ts to rollup-plugin-ts (#988)" - } - ] - } - }, - { - "version": "3.1.0", - "tag": "@snowplow/browser-plugin-consent_v3.1.0", - "date": "Fri, 14 May 2021 10:45:32 GMT", - "comments": {} - }, - { - "version": "3.0.3", - "tag": "@snowplow/browser-plugin-consent_v3.0.3", - "date": "Wed, 21 Apr 2021 12:35:06 GMT", - "comments": {} - }, - { - "version": "3.0.2", - "tag": "@snowplow/browser-plugin-consent_v3.0.2", - "date": "Thu, 15 Apr 2021 21:07:39 GMT", - "comments": {} - }, - { - "version": "3.0.1", - "tag": "@snowplow/browser-plugin-consent_v3.0.1", - "date": "Wed, 14 Apr 2021 16:30:05 GMT", - "comments": { - "none": [ - { - "comment": "Add peerDependencies to plugins (#950)" - }, - { - "comment": "Mark packages as sideEffect: false (#951)" - }, - { - "comment": "Add unit tests to plugin track* functions (#954)" - } - ] - } - }, - { - "version": "3.0.0", - "tag": "@snowplow/browser-plugin-consent_v3.0.0", - "date": "Wed, 31 Mar 2021 14:46:47 GMT", - "comments": { - "none": [ - { - "comment": "Add debug mode (#381)" - }, - { - "comment": "Bump rollup to 2.41 (#916)" - }, - { - "comment": "Publish UMD versions of plugins to GitHub release (#923)" - }, - { - "comment": "Ensure browser-tracker API methods catch exceptions (#919)" - }, - { - "comment": "Introduce TSDoc comments and extract interfaces where appropriate (#906)" - }, - { - "comment": "Bump major version to v3 and update READMEs (#904)" - }, - { - "comment": "Improve Core API for module bundlers which support treeshaking (#903)" - }, - { - "comment": "Rename @snowplow/browser-core to @snowplow/browser-tracker-core (#901)" - }, - { - "comment": "Publish lite version of sp.js (#900)" - }, - { - "comment": "Ensure correct 3-Clause BSD License notices are being used (#316)" - }, - { - "comment": "Improve API for module bundlers which support treeshaking (#899)" - }, - { - "comment": "Bump rush to 5.39 (#895)" - }, - { - "comment": "Port to TypeScript (#72)" - }, - { - "comment": "Make sp.js build process modular (#450)" - }, - { - "comment": "Create @snowplow/browser-tracker package for npm distribution (#541)" - }, - { - "comment": "Split auto contexts into plugins (#880)" - }, - { - "comment": "Add rush to manage monorepo (#883)" - }, - { - "comment": "Add ES Module builds (#882)" - }, - { - "comment": "Cleanup deprecated methods (#557)" - }, - { - "comment": "Update publishing process for rush (#907)" - }, - { - "comment": "Change white and black lists to allow and deny lists (#908)" - }, - { - "comment": "Create rush change files for major version 3 release (#909)" - } - ] - } - } - ] -} diff --git a/plugins/browser-plugin-consent/CHANGELOG.md b/plugins/browser-plugin-consent/CHANGELOG.md deleted file mode 100644 index 6fb2387e0..000000000 --- a/plugins/browser-plugin-consent/CHANGELOG.md +++ /dev/null @@ -1,290 +0,0 @@ -# Change Log - @snowplow/browser-plugin-consent - -This log was last generated on Mon, 28 Oct 2024 10:23:28 GMT and should not be manually modified. - -## 3.24.6 -Mon, 28 Oct 2024 10:23:28 GMT - -_Version update only_ - -## 3.24.5 -Fri, 25 Oct 2024 08:53:04 GMT - -_Version update only_ - -## 3.24.4 -Thu, 26 Sep 2024 06:10:22 GMT - -_Version update only_ - -## 3.24.3 -Tue, 03 Sep 2024 08:15:14 GMT - -### Updates - -- Upgrade supported Node.JS versions in build to 18 - 20 and upgrade rush - -## 3.24.2 -Wed, 24 Jul 2024 08:59:00 GMT - -_Version update only_ - -## 3.24.1 -Tue, 02 Jul 2024 07:08:17 GMT - -_Version update only_ - -## 3.24.0 -Tue, 25 Jun 2024 08:31:05 GMT - -_Version update only_ - -## 3.23.1 -Tue, 04 Jun 2024 13:34:45 GMT - -_Version update only_ - -## 3.23.0 -Thu, 28 Mar 2024 11:28:45 GMT - -_Version update only_ - -## 3.22.1 -Wed, 13 Mar 2024 08:39:48 GMT - -_Version update only_ - -## 3.22.0 -Fri, 08 Mar 2024 08:13:04 GMT - -_Version update only_ - -## 3.21.0 -Mon, 29 Jan 2024 08:34:06 GMT - -_Version update only_ - -## 3.20.0 -Mon, 15 Jan 2024 14:41:16 GMT - -_Version update only_ - -## 3.19.0 -Thu, 14 Dec 2023 10:45:22 GMT - -_Version update only_ - -## 3.18.0 -Mon, 04 Dec 2023 13:44:02 GMT - -_Version update only_ - -## 3.17.0 -Tue, 14 Nov 2023 17:58:26 GMT - -_Version update only_ - -## 3.16.0 -Mon, 16 Oct 2023 14:58:08 GMT - -_Version update only_ - -## 3.15.0 -Mon, 28 Aug 2023 14:25:14 GMT - -_Version update only_ - -## 3.14.0 -Thu, 10 Aug 2023 13:56:44 GMT - -_Version update only_ - -## 3.13.1 -Thu, 29 Jun 2023 14:20:06 GMT - -_Version update only_ - -## 3.13.0 -Tue, 20 Jun 2023 07:44:23 GMT - -_Version update only_ - -## 3.12.1 -Thu, 15 Jun 2023 10:05:37 GMT - -_Version update only_ - -## 3.12.0 -Mon, 05 Jun 2023 11:51:22 GMT - -_Version update only_ - -## 3.11.0 -Wed, 24 May 2023 15:56:17 GMT - -_Version update only_ - -## 3.10.1 -Fri, 12 May 2023 06:59:31 GMT - -_Version update only_ - -## 3.10.0 -Thu, 11 May 2023 08:29:15 GMT - -_Version update only_ - -## 3.9.0 -Thu, 30 Mar 2023 13:46:56 GMT - -_Version update only_ - -## 3.8.0 -Tue, 03 Jan 2023 15:36:33 GMT - -_Version update only_ - -## 3.7.0 -Mon, 31 Oct 2022 06:26:28 GMT - -_Version update only_ - -## 3.6.0 -Thu, 15 Sep 2022 07:55:20 GMT - -_Version update only_ - -## 3.5.0 -Fri, 10 Jun 2022 18:57:46 GMT - -_Version update only_ - -## 3.4.0 -Thu, 07 Apr 2022 11:56:26 GMT - -### Updates - -- Bump dependencies (#1067) - -## 3.3.1 -Wed, 23 Feb 2022 19:27:40 GMT - -_Version update only_ - -## 3.3.0 -Mon, 31 Jan 2022 15:58:10 GMT - -_Version update only_ - -## 3.2.3 -Tue, 18 Jan 2022 16:23:52 GMT - -### Updates - -- Bump Copyright to 2022 (#1040) - -## 3.2.2 -Fri, 14 Jan 2022 10:17:59 GMT - -_Version update only_ - -## 3.2.1 -Wed, 12 Jan 2022 09:50:29 GMT - -_Version update only_ - -## 3.2.0 -Tue, 11 Jan 2022 12:53:22 GMT - -_Version update only_ - -## 3.1.6 -Tue, 19 Oct 2021 09:17:22 GMT - -### Updates - -- Fix failing build on ARM Macs (#1012) - -## 3.1.5 -Fri, 01 Oct 2021 08:09:20 GMT - -_Version update only_ - -## 3.1.4 -Tue, 21 Sep 2021 14:59:36 GMT - -_Version update only_ - -## 3.1.3 -Mon, 23 Aug 2021 10:13:18 GMT - -_Version update only_ - -## 3.1.2 -Mon, 16 Aug 2021 12:59:59 GMT - -### Updates - -- Update READMEs with correct Node requirements (#994) - -## 3.1.1 -Wed, 04 Aug 2021 10:12:25 GMT - -### Updates - -- Bump tslib to 2.3.0 (#986) -- Bump typescript to 4.3.5 (#987) -- Switch from @wessberg/rollup-plugin-ts to rollup-plugin-ts (#988) - -## 3.1.0 -Fri, 14 May 2021 10:45:32 GMT - -_Version update only_ - -## 3.0.3 -Wed, 21 Apr 2021 12:35:06 GMT - -_Version update only_ - -## 3.0.2 -Thu, 15 Apr 2021 21:07:39 GMT - -_Version update only_ - -## 3.0.1 -Wed, 14 Apr 2021 16:30:05 GMT - -### Updates - -- Add peerDependencies to plugins (#950) -- Mark packages as sideEffect: false (#951) -- Add unit tests to plugin track* functions (#954) - -## 3.0.0 -Wed, 31 Mar 2021 14:46:47 GMT - -### Updates - -- Add debug mode (#381) -- Bump rollup to 2.41 (#916) -- Publish UMD versions of plugins to GitHub release (#923) -- Ensure browser-tracker API methods catch exceptions (#919) -- Introduce TSDoc comments and extract interfaces where appropriate (#906) -- Bump major version to v3 and update READMEs (#904) -- Improve Core API for module bundlers which support treeshaking (#903) -- Rename @snowplow/browser-core to @snowplow/browser-tracker-core (#901) -- Publish lite version of sp.js (#900) -- Ensure correct 3-Clause BSD License notices are being used (#316) -- Improve API for module bundlers which support treeshaking (#899) -- Bump rush to 5.39 (#895) -- Port to TypeScript (#72) -- Make sp.js build process modular (#450) -- Create @snowplow/browser-tracker package for npm distribution (#541) -- Split auto contexts into plugins (#880) -- Add rush to manage monorepo (#883) -- Add ES Module builds (#882) -- Cleanup deprecated methods (#557) -- Update publishing process for rush (#907) -- Change white and black lists to allow and deny lists (#908) -- Create rush change files for major version 3 release (#909) - diff --git a/plugins/browser-plugin-consent/LICENSE b/plugins/browser-plugin-consent/LICENSE deleted file mode 100644 index 76f1946ea..000000000 --- a/plugins/browser-plugin-consent/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/browser-plugin-consent/README.md b/plugins/browser-plugin-consent/README.md deleted file mode 100644 index 7afde7448..000000000 --- a/plugins/browser-plugin-consent/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Snowplow Consent Tracking - -[![npm version][npm-image]][npm-url] -[![License][license-image]](LICENSE) - -Browser Plugin to be used with `@snowplow/browser-tracker`. - -Adds consent information to your Snowplow tracking. - -## Maintainer quick start - -Part of the Snowplow JavaScript Tracker monorepo. -Build with [Node.js](https://nodejs.org/en/) (18 - 20) and [Rush](https://rushjs.io/). - -### Setup repository - -```bash -npm install -g @microsoft/rush -git clone https://github.com/snowplow/snowplow-javascript-tracker.git -rush update -``` - -## Package Installation - -With npm: - -```bash -npm install @snowplow/browser-plugin-consent -``` - -## Usage - -Initialize your tracker with the ConsentPlugin: - -```js -import { newTracker } from '@snowplow/browser-tracker'; -import { ConsentPlugin } from '@snowplow/browser-plugin-consent'; - -newTracker('sp1', '{{collector}}', { plugins: [ ConsentPlugin() ] }); // Also stores reference at module level -``` - -Then use the available functions from this package to track to all trackers which have been initialized with this plugin: - -```js -import { enableGdprContext, trackConsentGranted } from '@snowplow/browser-plugin-consent'; - -enableGdprContext({ basisForProcessing: 'consent' }); -trackConsentGranted({ id: '123-456', version: '1' }); -``` - -## Copyright and license - -Licensed and distributed under the [BSD 3-Clause License](LICENSE) ([An OSI Approved License][osi]). - -Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang. - -All rights reserved. - -[npm-url]: https://www.npmjs.com/package/@snowplow/browser-plugin-consent -[npm-image]: https://img.shields.io/npm/v/@snowplow/browser-plugin-consent -[docs]: https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/javascript-tracker/ -[osi]: https://opensource.org/licenses/BSD-3-Clause -[license-image]: https://img.shields.io/npm/l/@snowplow/browser-plugin-consent diff --git a/plugins/browser-plugin-consent/jest.config.js b/plugins/browser-plugin-consent/jest.config.js deleted file mode 100644 index bd3ea4e2a..000000000 --- a/plugins/browser-plugin-consent/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - preset: 'ts-jest', - reporters: ['jest-standard-reporter'], - testEnvironment: 'jest-environment-jsdom-global', -}; diff --git a/plugins/browser-plugin-consent/package.json b/plugins/browser-plugin-consent/package.json deleted file mode 100644 index b2f388044..000000000 --- a/plugins/browser-plugin-consent/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "@snowplow/browser-plugin-consent", - "version": "3.24.6", - "description": "Consent and GDPR data for Snowplow events", - "homepage": "http://bit.ly/sp-js", - "bugs": "https://github.com/snowplow/snowplow-javascript-tracker/issues", - "repository": { - "type": "git", - "url": "https://github.com/snowplow/snowplow-javascript-tracker.git" - }, - "license": "BSD-3-Clause", - "author": "Paul Boocock", - "sideEffects": false, - "main": "./dist/index.umd.js", - "module": "./dist/index.module.js", - "types": "./dist/index.module.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "rollup -c --silent --failAfterWarnings", - "test": "jest" - }, - "dependencies": { - "@snowplow/browser-tracker-core": "workspace:*", - "@snowplow/tracker-core": "workspace:*", - "tslib": "^2.3.1" - }, - "devDependencies": { - "@ampproject/rollup-plugin-closure-compiler": "~0.27.0", - "@rollup/plugin-commonjs": "~21.0.2", - "@rollup/plugin-node-resolve": "~13.1.3", - "@types/jest": "~27.4.1", - "@types/jsdom": "~16.2.14", - "@types/lodash": "~4.14.180", - "@typescript-eslint/eslint-plugin": "~5.15.0", - "@typescript-eslint/parser": "~5.15.0", - "eslint": "~8.11.0", - "jest": "~27.5.1", - "jest-environment-jsdom": "~27.5.1", - "jest-environment-jsdom-global": "~3.0.0", - "jest-standard-reporter": "~2.0.0", - "lodash": "~4.17.21", - "rollup": "~2.70.1", - "rollup-plugin-cleanup": "~3.2.1", - "rollup-plugin-license": "~2.6.1", - "rollup-plugin-terser": "~7.0.2", - "rollup-plugin-ts": "~2.0.5", - "ts-jest": "~27.1.3", - "typescript": "~4.6.2" - }, - "peerDependencies": { - "@snowplow/browser-tracker": "~3.24.6" - } -} diff --git a/plugins/browser-plugin-consent/rollup.config.js b/plugins/browser-plugin-consent/rollup.config.js deleted file mode 100644 index 827996cc8..000000000 --- a/plugins/browser-plugin-consent/rollup.config.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import ts from 'rollup-plugin-ts'; // Prefered over @rollup/plugin-typescript as it bundles .d.ts files -import { banner } from '../../banner'; -import compiler from '@ampproject/rollup-plugin-closure-compiler'; -import { terser } from 'rollup-plugin-terser'; -import cleanup from 'rollup-plugin-cleanup'; -import pkg from './package.json'; -import { builtinModules } from 'module'; - -const umdPlugins = [nodeResolve({ browser: true }), commonjs(), ts()]; -const umdName = 'snowplowConsent'; - -export default [ - // CommonJS (for Node) and ES module (for bundlers) build. - { - input: './src/index.ts', - plugins: [...umdPlugins, banner()], - treeshake: { moduleSideEffects: ['sha1'] }, - output: [{ file: pkg.main, format: 'umd', sourcemap: true, name: umdName }], - }, - { - input: './src/index.ts', - plugins: [...umdPlugins, compiler(), terser(), cleanup({ comments: 'none' }), banner()], - treeshake: { moduleSideEffects: ['sha1'] }, - output: [{ file: pkg.main.replace('.js', '.min.js'), format: 'umd', sourcemap: true, name: umdName }], - }, - { - input: './src/index.ts', - external: [...builtinModules, ...Object.keys(pkg.dependencies), ...Object.keys(pkg.devDependencies)], - plugins: [ - ts(), // so Rollup can convert TypeScript to JavaScript - banner(), - ], - output: [{ file: pkg.module, format: 'es', sourcemap: true }], - }, -]; diff --git a/plugins/browser-plugin-consent/src/contexts.ts b/plugins/browser-plugin-consent/src/contexts.ts deleted file mode 100644 index 46c82c3fb..000000000 --- a/plugins/browser-plugin-consent/src/contexts.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * Schema for a web page context - */ -export interface Gdpr { - /** - * GDPR basis for data collection & processing - */ - basisForProcessing: - | 'consent' - | 'contract' - | 'legal_obligation' - | 'vital_interests' - | 'public_task' - | 'legitimate_interests'; - /** - * ID for document detailing basis for processing - */ - documentId?: string | null; - /** - * Version of document detailing basis for processing - */ - documentVersion?: string | null; - /** - * Description of document detailing basis for processing - */ - documentDescription?: string | null; - [key: string]: unknown; -} diff --git a/plugins/browser-plugin-consent/src/index.ts b/plugins/browser-plugin-consent/src/index.ts deleted file mode 100644 index e568f653d..000000000 --- a/plugins/browser-plugin-consent/src/index.ts +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import { BrowserPlugin, BrowserTracker, dispatchToTrackersInCollection } from '@snowplow/browser-tracker-core'; -import { - buildConsentGranted, - buildConsentWithdrawn, - CommonEventProperties, - ConsentGrantedEvent, - ConsentWithdrawnEvent, - Logger, -} from '@snowplow/tracker-core'; -import { Gdpr } from './contexts'; - -export { ConsentGrantedEvent, ConsentWithdrawnEvent }; - -export enum gdprBasis { - consent = 'consent', - contract = 'contract', - legalObligation = 'legal_obligation', - vitalInterests = 'vital_interests', - publicTask = 'public_task', - legitimateInterests = 'legitimate_interests', -} - -export type GdprBasis = keyof typeof gdprBasis; - -/** The Configuration for the GDPR Context */ -export interface GdprContextConfiguration { - /** The basis for why the document will be processed */ - basisForProcessing: GdprBasis; - /** An identifier for the document */ - documentId?: string; - /** The version of the document */ - documentVersion?: string; - /** A descrtiption of the document */ - documentDescription?: string; -} - -const _trackers: Record = {}; -const _context: Record = {}; -let LOG: Logger; - -/** - * The Consent Plugin - * - * Adds Consent Granted and Withdrawn events - * and the ability to add the GDPR context to events - */ -export function ConsentPlugin(): BrowserPlugin { - let trackerId: string; - - return { - activateBrowserPlugin: (tracker) => { - trackerId = tracker.id; - _trackers[tracker.id] = tracker; - }, - contexts: () => { - if (_context[trackerId]) { - return [ - { - schema: 'iglu:com.snowplowanalytics.snowplow/gdpr/jsonschema/1-0-0', - data: _context[trackerId], - }, - ]; - } - - return []; - }, - logger: (logger) => { - LOG = logger; - }, - }; -} - -/** - * Enable the GDPR context for each event - * @param configuration - the configuration for the GDPR context - * @param trackers - The tracker identifiers which should have the GDPR context enabled - */ -export function enableGdprContext( - configuration: GdprContextConfiguration, - trackers: Array = Object.keys(_trackers) -) { - const { basisForProcessing, documentId, documentVersion, documentDescription } = configuration; - let basis = gdprBasis[basisForProcessing]; - - if (!basis) { - LOG.warn( - 'enableGdprContext: basisForProcessing must be one of: consent, contract, legalObligation, vitalInterests, publicTask, legitimateInterests' - ); - return; - } else { - trackers.forEach((t) => { - if (_trackers[t]) { - _context[t] = { - basisForProcessing: basis, - documentId: documentId ?? null, - documentVersion: documentVersion ?? null, - documentDescription: documentDescription ?? null, - }; - } - }); - } -} - -/** - * Track a consent granted action - * - * @param event - The event information - * @param trackers - The tracker identifiers which the event will be sent to - */ -export function trackConsentGranted( - event: ConsentGrantedEvent & CommonEventProperties, - trackers: Array = Object.keys(_trackers) -) { - dispatchToTrackersInCollection(trackers, _trackers, (t) => { - const builtEvent = buildConsentGranted(event); - t.core.track( - builtEvent.event, - event.context ? event.context.concat(builtEvent.context) : builtEvent.context, - event.timestamp - ); - }); -} - -/** - * Track a consent withdrawn action - * - * @param event - The event information - * @param trackers - The tracker identifiers which the event will be sent to - */ -export function trackConsentWithdrawn( - event: ConsentWithdrawnEvent & CommonEventProperties, - trackers: Array = Object.keys(_trackers) -) { - dispatchToTrackersInCollection(trackers, _trackers, (t) => { - const builtEvent = buildConsentWithdrawn(event); - t.core.track( - builtEvent.event, - event.context ? event.context.concat(builtEvent.context) : builtEvent.context, - event.timestamp - ); - }); -} diff --git a/plugins/browser-plugin-consent/test/events.test.ts b/plugins/browser-plugin-consent/test/events.test.ts deleted file mode 100644 index 5353064cf..000000000 --- a/plugins/browser-plugin-consent/test/events.test.ts +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import { addTracker, SharedState } from '@snowplow/browser-tracker-core'; -import F from 'lodash/fp'; -import { ConsentPlugin, trackConsentWithdrawn, trackConsentGranted, enableGdprContext } from '../src'; - -const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('evt.e')))); -const extractEventProperties = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('evt.ue_pr'))); -const extractEventContext = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('evt.co'))); -const extractUeEvent = (schema: string) => { - return { - from: F.compose( - F.first, - F.filter(F.compose(F.eq(schema), F.get('schema'))), - F.flatten, - extractEventProperties, - getUEEvents - ), - }; -}; -const extractContext = (schema: string) => { - return { - from: F.compose( - F.first, - F.filter(F.compose(F.eq(schema), F.get('schema'))), - F.flatten, - extractEventContext, - getUEEvents - ), - }; -}; - -describe('AdTrackingPlugin', () => { - const state = new SharedState(); - addTracker('sp1', 'sp1', 'js-3.0.0', '', state, { - stateStorageStrategy: 'cookie', - encodeBase64: false, - plugins: [ConsentPlugin()], - }); - addTracker('sp2', 'sp2', 'js-3.0.0', '', state, { - stateStorageStrategy: 'cookie', - encodeBase64: false, - plugins: [ConsentPlugin()], - }); - - enableGdprContext({ - basisForProcessing: 'legalObligation', - documentDescription: 'doc-desc-1', - documentId: 'doc-id-1', - documentVersion: 'doc-ver-1', - }); - - trackConsentWithdrawn( - { - all: true, - description: 'desc-1', - id: 'id-1', - name: 'name-1', - version: '1.1.0', - }, - ['sp1'] - ); - - trackConsentGranted( - { - description: 'desc-2', - id: 'id-2', - name: 'name-2', - version: '1.2.0', - expiry: '2020-01-01T00:00:00Z', - }, - ['sp2'] - ); - - it('trackConsentWithdrawn adds the expected consent withdrawn event to the queue', () => { - expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/consent_withdrawn/jsonschema/1-0-0').from(state.outQueues[0]) - ).toMatchObject({ - schema: 'iglu:com.snowplowanalytics.snowplow/consent_withdrawn/jsonschema/1-0-0', - data: { - all: true, - }, - }); - - expect( - extractContext('iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0').from(state.outQueues[0]) - ).toMatchObject({ - schema: 'iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0', - data: { - description: 'desc-1', - id: 'id-1', - name: 'name-1', - version: '1.1.0', - }, - }); - }); - - it('trackConsentGranted adds the expected consent granted event to the queue', () => { - expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/consent_granted/jsonschema/1-0-0').from(state.outQueues[1]) - ).toMatchObject({ - schema: 'iglu:com.snowplowanalytics.snowplow/consent_granted/jsonschema/1-0-0', - data: { - expiry: '2020-01-01T00:00:00Z', - }, - }); - - expect( - extractContext('iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0').from(state.outQueues[1]) - ).toMatchObject({ - schema: 'iglu:com.snowplowanalytics.snowplow/consent_document/jsonschema/1-0-0', - data: { - description: 'desc-2', - id: 'id-2', - name: 'name-2', - version: '1.2.0', - }, - }); - }); - - it('events contain the GDPR context', () => { - expect( - extractContext('iglu:com.snowplowanalytics.snowplow/gdpr/jsonschema/1-0-0').from(state.outQueues[0]) - ).toMatchObject({ - schema: 'iglu:com.snowplowanalytics.snowplow/gdpr/jsonschema/1-0-0', - data: { - basisForProcessing: 'legal_obligation', - documentDescription: 'doc-desc-1', - documentId: 'doc-id-1', - documentVersion: 'doc-ver-1', - }, - }); - - expect( - extractContext('iglu:com.snowplowanalytics.snowplow/gdpr/jsonschema/1-0-0').from(state.outQueues[1]) - ).toMatchObject({ - schema: 'iglu:com.snowplowanalytics.snowplow/gdpr/jsonschema/1-0-0', - data: { - basisForProcessing: 'legal_obligation', - documentDescription: 'doc-desc-1', - documentId: 'doc-id-1', - documentVersion: 'doc-ver-1', - }, - }); - }); -}); diff --git a/plugins/browser-plugin-consent/tsconfig.json b/plugins/browser-plugin-consent/tsconfig.json deleted file mode 100644 index 4082f16a5..000000000 --- a/plugins/browser-plugin-consent/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tsconfig.json" -} diff --git a/plugins/browser-plugin-ecommerce/CHANGELOG.json b/plugins/browser-plugin-ecommerce/CHANGELOG.json deleted file mode 100644 index 37f9d41e5..000000000 --- a/plugins/browser-plugin-ecommerce/CHANGELOG.json +++ /dev/null @@ -1,422 +0,0 @@ -{ - "name": "@snowplow/browser-plugin-ecommerce", - "entries": [ - { - "version": "3.24.6", - "tag": "@snowplow/browser-plugin-ecommerce_v3.24.6", - "date": "Mon, 28 Oct 2024 10:23:28 GMT", - "comments": {} - }, - { - "version": "3.24.5", - "tag": "@snowplow/browser-plugin-ecommerce_v3.24.5", - "date": "Fri, 25 Oct 2024 08:53:04 GMT", - "comments": {} - }, - { - "version": "3.24.4", - "tag": "@snowplow/browser-plugin-ecommerce_v3.24.4", - "date": "Thu, 26 Sep 2024 06:10:22 GMT", - "comments": {} - }, - { - "version": "3.24.3", - "tag": "@snowplow/browser-plugin-ecommerce_v3.24.3", - "date": "Tue, 03 Sep 2024 08:15:14 GMT", - "comments": { - "none": [ - { - "comment": "Upgrade supported Node.JS versions in build to 18 - 20 and upgrade rush" - } - ] - } - }, - { - "version": "3.24.2", - "tag": "@snowplow/browser-plugin-ecommerce_v3.24.2", - "date": "Wed, 24 Jul 2024 08:59:00 GMT", - "comments": {} - }, - { - "version": "3.24.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.24.1", - "date": "Tue, 02 Jul 2024 07:08:17 GMT", - "comments": {} - }, - { - "version": "3.24.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.24.0", - "date": "Tue, 25 Jun 2024 08:31:05 GMT", - "comments": {} - }, - { - "version": "3.23.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.23.1", - "date": "Tue, 04 Jun 2024 13:34:45 GMT", - "comments": {} - }, - { - "version": "3.23.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.23.0", - "date": "Thu, 28 Mar 2024 11:28:45 GMT", - "comments": {} - }, - { - "version": "3.22.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.22.1", - "date": "Wed, 13 Mar 2024 08:39:48 GMT", - "comments": {} - }, - { - "version": "3.22.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.22.0", - "date": "Fri, 08 Mar 2024 08:13:04 GMT", - "comments": {} - }, - { - "version": "3.21.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.21.0", - "date": "Mon, 29 Jan 2024 08:34:06 GMT", - "comments": {} - }, - { - "version": "3.20.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.20.0", - "date": "Mon, 15 Jan 2024 14:41:16 GMT", - "comments": {} - }, - { - "version": "3.19.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.19.0", - "date": "Thu, 14 Dec 2023 10:45:22 GMT", - "comments": {} - }, - { - "version": "3.18.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.18.0", - "date": "Mon, 04 Dec 2023 13:44:02 GMT", - "comments": {} - }, - { - "version": "3.17.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.17.0", - "date": "Tue, 14 Nov 2023 17:58:26 GMT", - "comments": {} - }, - { - "version": "3.16.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.16.0", - "date": "Mon, 16 Oct 2023 14:58:08 GMT", - "comments": {} - }, - { - "version": "3.15.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.15.0", - "date": "Mon, 28 Aug 2023 14:25:14 GMT", - "comments": {} - }, - { - "version": "3.14.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.14.0", - "date": "Thu, 10 Aug 2023 13:56:44 GMT", - "comments": {} - }, - { - "version": "3.13.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.13.1", - "date": "Thu, 29 Jun 2023 14:20:06 GMT", - "comments": {} - }, - { - "version": "3.13.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.13.0", - "date": "Tue, 20 Jun 2023 07:44:23 GMT", - "comments": {} - }, - { - "version": "3.12.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.12.1", - "date": "Thu, 15 Jun 2023 10:05:37 GMT", - "comments": {} - }, - { - "version": "3.12.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.12.0", - "date": "Mon, 05 Jun 2023 11:51:22 GMT", - "comments": {} - }, - { - "version": "3.11.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.11.0", - "date": "Wed, 24 May 2023 15:56:17 GMT", - "comments": {} - }, - { - "version": "3.10.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.10.1", - "date": "Fri, 12 May 2023 06:59:31 GMT", - "comments": {} - }, - { - "version": "3.10.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.10.0", - "date": "Thu, 11 May 2023 08:29:15 GMT", - "comments": {} - }, - { - "version": "3.9.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.9.0", - "date": "Thu, 30 Mar 2023 13:46:56 GMT", - "comments": {} - }, - { - "version": "3.8.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.8.0", - "date": "Tue, 03 Jan 2023 15:36:33 GMT", - "comments": {} - }, - { - "version": "3.7.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.7.0", - "date": "Mon, 31 Oct 2022 06:26:28 GMT", - "comments": {} - }, - { - "version": "3.6.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.6.0", - "date": "Thu, 15 Sep 2022 07:55:20 GMT", - "comments": {} - }, - { - "version": "3.5.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.5.0", - "date": "Fri, 10 Jun 2022 18:57:46 GMT", - "comments": {} - }, - { - "version": "3.4.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.4.0", - "date": "Thu, 07 Apr 2022 11:56:26 GMT", - "comments": { - "none": [ - { - "comment": "Bump dependencies (#1067)" - } - ] - } - }, - { - "version": "3.3.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.3.1", - "date": "Wed, 23 Feb 2022 19:27:40 GMT", - "comments": {} - }, - { - "version": "3.3.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.3.0", - "date": "Mon, 31 Jan 2022 15:58:10 GMT", - "comments": {} - }, - { - "version": "3.2.3", - "tag": "@snowplow/browser-plugin-ecommerce_v3.2.3", - "date": "Tue, 18 Jan 2022 16:23:52 GMT", - "comments": { - "none": [ - { - "comment": "Bump Copyright to 2022 (#1040)" - } - ] - } - }, - { - "version": "3.2.2", - "tag": "@snowplow/browser-plugin-ecommerce_v3.2.2", - "date": "Fri, 14 Jan 2022 10:17:59 GMT", - "comments": {} - }, - { - "version": "3.2.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.2.1", - "date": "Wed, 12 Jan 2022 09:50:29 GMT", - "comments": {} - }, - { - "version": "3.2.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.2.0", - "date": "Tue, 11 Jan 2022 12:53:22 GMT", - "comments": {} - }, - { - "version": "3.1.6", - "tag": "@snowplow/browser-plugin-ecommerce_v3.1.6", - "date": "Tue, 19 Oct 2021 09:17:22 GMT", - "comments": { - "none": [ - { - "comment": "Fix failing build on ARM Macs (#1012)" - } - ] - } - }, - { - "version": "3.1.5", - "tag": "@snowplow/browser-plugin-ecommerce_v3.1.5", - "date": "Fri, 01 Oct 2021 08:09:21 GMT", - "comments": {} - }, - { - "version": "3.1.4", - "tag": "@snowplow/browser-plugin-ecommerce_v3.1.4", - "date": "Tue, 21 Sep 2021 14:59:36 GMT", - "comments": {} - }, - { - "version": "3.1.3", - "tag": "@snowplow/browser-plugin-ecommerce_v3.1.3", - "date": "Mon, 23 Aug 2021 10:13:18 GMT", - "comments": {} - }, - { - "version": "3.1.2", - "tag": "@snowplow/browser-plugin-ecommerce_v3.1.2", - "date": "Mon, 16 Aug 2021 12:59:59 GMT", - "comments": { - "none": [ - { - "comment": "Update READMEs with correct Node requirements (#994)" - } - ] - } - }, - { - "version": "3.1.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.1.1", - "date": "Wed, 04 Aug 2021 10:12:25 GMT", - "comments": { - "none": [ - { - "comment": "Automate api-extractor on release (#972)" - }, - { - "comment": "Bump tslib to 2.3.0 (#986)" - }, - { - "comment": "Bump typescript to 4.3.5 (#987)" - }, - { - "comment": "Switch from @wessberg/rollup-plugin-ts to rollup-plugin-ts (#988)" - } - ] - } - }, - { - "version": "3.1.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.1.0", - "date": "Fri, 14 May 2021 10:45:32 GMT", - "comments": {} - }, - { - "version": "3.0.3", - "tag": "@snowplow/browser-plugin-ecommerce_v3.0.3", - "date": "Wed, 21 Apr 2021 12:35:06 GMT", - "comments": {} - }, - { - "version": "3.0.2", - "tag": "@snowplow/browser-plugin-ecommerce_v3.0.2", - "date": "Thu, 15 Apr 2021 21:07:39 GMT", - "comments": {} - }, - { - "version": "3.0.1", - "tag": "@snowplow/browser-plugin-ecommerce_v3.0.1", - "date": "Wed, 14 Apr 2021 16:30:05 GMT", - "comments": { - "none": [ - { - "comment": "Add peerDependencies to plugins (#950)" - }, - { - "comment": "Mark packages as sideEffect: false (#951)" - }, - { - "comment": "Add unit tests to plugin track* functions (#954)" - } - ] - } - }, - { - "version": "3.0.0", - "tag": "@snowplow/browser-plugin-ecommerce_v3.0.0", - "date": "Wed, 31 Mar 2021 14:46:47 GMT", - "comments": { - "none": [ - { - "comment": "Bump rollup to 2.41 (#916)" - }, - { - "comment": "Publish UMD versions of plugins to GitHub release (#923)" - }, - { - "comment": "Ensure browser-tracker API methods catch exceptions (#919)" - }, - { - "comment": "Introduce TSDoc comments and extract interfaces where appropriate (#906)" - }, - { - "comment": "Bump major version to v3 and update READMEs (#904)" - }, - { - "comment": "Improve Core API for module bundlers which support treeshaking (#903)" - }, - { - "comment": "Rename @snowplow/browser-core to @snowplow/browser-tracker-core (#901)" - }, - { - "comment": "Publish lite version of sp.js (#900)" - }, - { - "comment": "Ensure correct 3-Clause BSD License notices are being used (#316)" - }, - { - "comment": "Improve API for module bundlers which support treeshaking (#899)" - }, - { - "comment": "Bump rush to 5.39 (#895)" - }, - { - "comment": "Port to TypeScript (#72)" - }, - { - "comment": "Make sp.js build process modular (#450)" - }, - { - "comment": "Create @snowplow/browser-tracker package for npm distribution (#541)" - }, - { - "comment": "Split auto contexts into plugins (#880)" - }, - { - "comment": "Add rush to manage monorepo (#883)" - }, - { - "comment": "Add ES Module builds (#882)" - }, - { - "comment": "Cleanup deprecated methods (#557)" - }, - { - "comment": "Update publishing process for rush (#907)" - }, - { - "comment": "Change white and black lists to allow and deny lists (#908)" - }, - { - "comment": "Create rush change files for major version 3 release (#909)" - } - ] - } - } - ] -} diff --git a/plugins/browser-plugin-ecommerce/CHANGELOG.md b/plugins/browser-plugin-ecommerce/CHANGELOG.md deleted file mode 100644 index 21eab09da..000000000 --- a/plugins/browser-plugin-ecommerce/CHANGELOG.md +++ /dev/null @@ -1,290 +0,0 @@ -# Change Log - @snowplow/browser-plugin-ecommerce - -This log was last generated on Mon, 28 Oct 2024 10:23:28 GMT and should not be manually modified. - -## 3.24.6 -Mon, 28 Oct 2024 10:23:28 GMT - -_Version update only_ - -## 3.24.5 -Fri, 25 Oct 2024 08:53:04 GMT - -_Version update only_ - -## 3.24.4 -Thu, 26 Sep 2024 06:10:22 GMT - -_Version update only_ - -## 3.24.3 -Tue, 03 Sep 2024 08:15:14 GMT - -### Updates - -- Upgrade supported Node.JS versions in build to 18 - 20 and upgrade rush - -## 3.24.2 -Wed, 24 Jul 2024 08:59:00 GMT - -_Version update only_ - -## 3.24.1 -Tue, 02 Jul 2024 07:08:17 GMT - -_Version update only_ - -## 3.24.0 -Tue, 25 Jun 2024 08:31:05 GMT - -_Version update only_ - -## 3.23.1 -Tue, 04 Jun 2024 13:34:45 GMT - -_Version update only_ - -## 3.23.0 -Thu, 28 Mar 2024 11:28:45 GMT - -_Version update only_ - -## 3.22.1 -Wed, 13 Mar 2024 08:39:48 GMT - -_Version update only_ - -## 3.22.0 -Fri, 08 Mar 2024 08:13:04 GMT - -_Version update only_ - -## 3.21.0 -Mon, 29 Jan 2024 08:34:06 GMT - -_Version update only_ - -## 3.20.0 -Mon, 15 Jan 2024 14:41:16 GMT - -_Version update only_ - -## 3.19.0 -Thu, 14 Dec 2023 10:45:22 GMT - -_Version update only_ - -## 3.18.0 -Mon, 04 Dec 2023 13:44:02 GMT - -_Version update only_ - -## 3.17.0 -Tue, 14 Nov 2023 17:58:26 GMT - -_Version update only_ - -## 3.16.0 -Mon, 16 Oct 2023 14:58:08 GMT - -_Version update only_ - -## 3.15.0 -Mon, 28 Aug 2023 14:25:14 GMT - -_Version update only_ - -## 3.14.0 -Thu, 10 Aug 2023 13:56:44 GMT - -_Version update only_ - -## 3.13.1 -Thu, 29 Jun 2023 14:20:06 GMT - -_Version update only_ - -## 3.13.0 -Tue, 20 Jun 2023 07:44:23 GMT - -_Version update only_ - -## 3.12.1 -Thu, 15 Jun 2023 10:05:37 GMT - -_Version update only_ - -## 3.12.0 -Mon, 05 Jun 2023 11:51:22 GMT - -_Version update only_ - -## 3.11.0 -Wed, 24 May 2023 15:56:17 GMT - -_Version update only_ - -## 3.10.1 -Fri, 12 May 2023 06:59:31 GMT - -_Version update only_ - -## 3.10.0 -Thu, 11 May 2023 08:29:15 GMT - -_Version update only_ - -## 3.9.0 -Thu, 30 Mar 2023 13:46:56 GMT - -_Version update only_ - -## 3.8.0 -Tue, 03 Jan 2023 15:36:33 GMT - -_Version update only_ - -## 3.7.0 -Mon, 31 Oct 2022 06:26:28 GMT - -_Version update only_ - -## 3.6.0 -Thu, 15 Sep 2022 07:55:20 GMT - -_Version update only_ - -## 3.5.0 -Fri, 10 Jun 2022 18:57:46 GMT - -_Version update only_ - -## 3.4.0 -Thu, 07 Apr 2022 11:56:26 GMT - -### Updates - -- Bump dependencies (#1067) - -## 3.3.1 -Wed, 23 Feb 2022 19:27:40 GMT - -_Version update only_ - -## 3.3.0 -Mon, 31 Jan 2022 15:58:10 GMT - -_Version update only_ - -## 3.2.3 -Tue, 18 Jan 2022 16:23:52 GMT - -### Updates - -- Bump Copyright to 2022 (#1040) - -## 3.2.2 -Fri, 14 Jan 2022 10:17:59 GMT - -_Version update only_ - -## 3.2.1 -Wed, 12 Jan 2022 09:50:29 GMT - -_Version update only_ - -## 3.2.0 -Tue, 11 Jan 2022 12:53:22 GMT - -_Version update only_ - -## 3.1.6 -Tue, 19 Oct 2021 09:17:22 GMT - -### Updates - -- Fix failing build on ARM Macs (#1012) - -## 3.1.5 -Fri, 01 Oct 2021 08:09:21 GMT - -_Version update only_ - -## 3.1.4 -Tue, 21 Sep 2021 14:59:36 GMT - -_Version update only_ - -## 3.1.3 -Mon, 23 Aug 2021 10:13:18 GMT - -_Version update only_ - -## 3.1.2 -Mon, 16 Aug 2021 12:59:59 GMT - -### Updates - -- Update READMEs with correct Node requirements (#994) - -## 3.1.1 -Wed, 04 Aug 2021 10:12:25 GMT - -### Updates - -- Automate api-extractor on release (#972) -- Bump tslib to 2.3.0 (#986) -- Bump typescript to 4.3.5 (#987) -- Switch from @wessberg/rollup-plugin-ts to rollup-plugin-ts (#988) - -## 3.1.0 -Fri, 14 May 2021 10:45:32 GMT - -_Version update only_ - -## 3.0.3 -Wed, 21 Apr 2021 12:35:06 GMT - -_Version update only_ - -## 3.0.2 -Thu, 15 Apr 2021 21:07:39 GMT - -_Version update only_ - -## 3.0.1 -Wed, 14 Apr 2021 16:30:05 GMT - -### Updates - -- Add peerDependencies to plugins (#950) -- Mark packages as sideEffect: false (#951) -- Add unit tests to plugin track* functions (#954) - -## 3.0.0 -Wed, 31 Mar 2021 14:46:47 GMT - -### Updates - -- Bump rollup to 2.41 (#916) -- Publish UMD versions of plugins to GitHub release (#923) -- Ensure browser-tracker API methods catch exceptions (#919) -- Introduce TSDoc comments and extract interfaces where appropriate (#906) -- Bump major version to v3 and update READMEs (#904) -- Improve Core API for module bundlers which support treeshaking (#903) -- Rename @snowplow/browser-core to @snowplow/browser-tracker-core (#901) -- Publish lite version of sp.js (#900) -- Ensure correct 3-Clause BSD License notices are being used (#316) -- Improve API for module bundlers which support treeshaking (#899) -- Bump rush to 5.39 (#895) -- Port to TypeScript (#72) -- Make sp.js build process modular (#450) -- Create @snowplow/browser-tracker package for npm distribution (#541) -- Split auto contexts into plugins (#880) -- Add rush to manage monorepo (#883) -- Add ES Module builds (#882) -- Cleanup deprecated methods (#557) -- Update publishing process for rush (#907) -- Change white and black lists to allow and deny lists (#908) -- Create rush change files for major version 3 release (#909) - diff --git a/plugins/browser-plugin-ecommerce/LICENSE b/plugins/browser-plugin-ecommerce/LICENSE deleted file mode 100644 index 76f1946ea..000000000 --- a/plugins/browser-plugin-ecommerce/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -BSD 3-Clause License - -Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/browser-plugin-ecommerce/README.md b/plugins/browser-plugin-ecommerce/README.md deleted file mode 100644 index f69c4da84..000000000 --- a/plugins/browser-plugin-ecommerce/README.md +++ /dev/null @@ -1,92 +0,0 @@ -# Snowplow Ecommerce Tracking - -[![npm version][npm-image]][npm-url] -[![License][license-image]](LICENSE) - -Browser Plugin to be used with `@snowplow/browser-tracker`. - -Adds ecommerce events to your Snowplow tracking. - -## Maintainer quick start - -Part of the Snowplow JavaScript Tracker monorepo. -Build with [Node.js](https://nodejs.org/en/) (18 - 20) and [Rush](https://rushjs.io/). - -### Setup repository - -```bash -npm install -g @microsoft/rush -git clone https://github.com/snowplow/snowplow-javascript-tracker.git -rush update -``` - -## Package Installation - -With npm: - -```bash -npm install @snowplow/browser-plugin-ecommerce -``` - -## Usage - -Initialize your tracker with the EcommercePlugin: - -```js -import { newTracker } from '@snowplow/browser-tracker'; -import { EcommercePlugin } from '@snowplow/browser-plugin-ecommerce'; - -newTracker('sp1', '{{collector}}', { plugins: [ EcommercePlugin() ] }); // Also stores reference at module level -``` - -Then use the available functions from this package to track to all trackers which have been initialized with this plugin: - -```js -import { addTrans, addItem, trackTrans, trackAddToCart } from '@snowplow/browser-plugin-ecommerce'; - -trackAddToCart({ - sku: '000345', - name: 'blue tie', - category: 'clothing', - unitPrice: 3.49, - quantity: 2, - currency: 'GBP', -}); - -// Grouped, events are sent on `trackTrans()` call -addTrans({ - orderId: 'order-123', - total: 8000, - affiliation: 'acme', - tax: 100, - shipping: 50, - city: 'pheonix', - state: 'arizona', - country: 'USA', - currency: 'JPY', -}); -addItem({ - orderId: 'order-123', - sku: '1001', - name: 'Blue t-shirt', - category: 'clothing', - price: '2000', - quantity: '2', - currency: 'JPY', -}); -trackTrans(); -``` - -## Copyright and license - -Licensed and distributed under the [BSD 3-Clause License](LICENSE) ([An OSI Approved License][osi]). - -Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang. - -All rights reserved. - -[npm-url]: https://www.npmjs.com/package/@snowplow/browser-plugin-ecommerce -[npm-image]: https://img.shields.io/npm/v/@snowplow/browser-plugin-ecommerce -[docs]: https://docs.snowplowanalytics.com/docs/collecting-data/collecting-from-own-applications/javascript-tracker/ -[osi]: https://opensource.org/licenses/BSD-3-Clause -[license-image]: https://img.shields.io/npm/l/@snowplow/browser-plugin-ecommerce diff --git a/plugins/browser-plugin-ecommerce/jest.config.js b/plugins/browser-plugin-ecommerce/jest.config.js deleted file mode 100644 index bd3ea4e2a..000000000 --- a/plugins/browser-plugin-ecommerce/jest.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - preset: 'ts-jest', - reporters: ['jest-standard-reporter'], - testEnvironment: 'jest-environment-jsdom-global', -}; diff --git a/plugins/browser-plugin-ecommerce/package.json b/plugins/browser-plugin-ecommerce/package.json deleted file mode 100644 index 629ea393e..000000000 --- a/plugins/browser-plugin-ecommerce/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "@snowplow/browser-plugin-ecommerce", - "version": "3.24.6", - "description": "Ecommerce tracking for Snowplow", - "homepage": "http://bit.ly/sp-js", - "bugs": "https://github.com/snowplow/snowplow-javascript-tracker/issues", - "repository": { - "type": "git", - "url": "https://github.com/snowplow/snowplow-javascript-tracker.git" - }, - "license": "BSD-3-Clause", - "author": "Paul Boocock", - "sideEffects": false, - "main": "./dist/index.umd.js", - "module": "./dist/index.module.js", - "types": "./dist/index.module.d.ts", - "files": [ - "dist" - ], - "scripts": { - "build": "rollup -c --silent --failAfterWarnings", - "test": "jest" - }, - "dependencies": { - "@snowplow/browser-tracker-core": "workspace:*", - "@snowplow/tracker-core": "workspace:*", - "tslib": "^2.3.1" - }, - "devDependencies": { - "@ampproject/rollup-plugin-closure-compiler": "~0.27.0", - "@rollup/plugin-commonjs": "~21.0.2", - "@rollup/plugin-node-resolve": "~13.1.3", - "@types/jest": "~27.4.1", - "@types/jsdom": "~16.2.14", - "@types/lodash": "~4.14.180", - "@typescript-eslint/eslint-plugin": "~5.15.0", - "@typescript-eslint/parser": "~5.15.0", - "eslint": "~8.11.0", - "jest": "~27.5.1", - "jest-environment-jsdom": "~27.5.1", - "jest-environment-jsdom-global": "~3.0.0", - "jest-standard-reporter": "~2.0.0", - "lodash": "~4.17.21", - "rollup": "~2.70.1", - "rollup-plugin-cleanup": "~3.2.1", - "rollup-plugin-license": "~2.6.1", - "rollup-plugin-terser": "~7.0.2", - "rollup-plugin-ts": "~2.0.5", - "ts-jest": "~27.1.3", - "typescript": "~4.6.2" - }, - "peerDependencies": { - "@snowplow/browser-tracker": "~3.24.6" - } -} diff --git a/plugins/browser-plugin-ecommerce/rollup.config.js b/plugins/browser-plugin-ecommerce/rollup.config.js deleted file mode 100644 index 889a84e64..000000000 --- a/plugins/browser-plugin-ecommerce/rollup.config.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import ts from 'rollup-plugin-ts'; // Prefered over @rollup/plugin-typescript as it bundles .d.ts files -import { banner } from '../../banner'; -import compiler from '@ampproject/rollup-plugin-closure-compiler'; -import { terser } from 'rollup-plugin-terser'; -import cleanup from 'rollup-plugin-cleanup'; -import pkg from './package.json'; -import { builtinModules } from 'module'; - -const umdPlugins = [nodeResolve({ browser: true }), commonjs(), ts()]; -const umdName = 'snowplowEcommerce'; - -export default [ - // CommonJS (for Node) and ES module (for bundlers) build. - { - input: './src/index.ts', - plugins: [...umdPlugins, banner()], - treeshake: { moduleSideEffects: ['sha1'] }, - output: [{ file: pkg.main, format: 'umd', sourcemap: true, name: umdName }], - }, - { - input: './src/index.ts', - plugins: [...umdPlugins, compiler(), terser(), cleanup({ comments: 'none' }), banner()], - treeshake: { moduleSideEffects: ['sha1'] }, - output: [{ file: pkg.main.replace('.js', '.min.js'), format: 'umd', sourcemap: true, name: umdName }], - }, - { - input: './src/index.ts', - external: [...builtinModules, ...Object.keys(pkg.dependencies), ...Object.keys(pkg.devDependencies)], - plugins: [ - ts(), // so Rollup can convert TypeScript to JavaScript - banner(), - ], - output: [{ file: pkg.module, format: 'es', sourcemap: true }], - }, -]; diff --git a/plugins/browser-plugin-ecommerce/src/index.ts b/plugins/browser-plugin-ecommerce/src/index.ts deleted file mode 100644 index 37cb40b56..000000000 --- a/plugins/browser-plugin-ecommerce/src/index.ts +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import { BrowserPlugin, BrowserTracker, dispatchToTrackersInCollection } from '@snowplow/browser-tracker-core'; -import { - AddToCartEvent, - RemoveFromCartEvent, - buildAddToCart, - buildEcommerceTransaction, - buildEcommerceTransactionItem, - buildRemoveFromCart, - CommonEventProperties, - EcommerceTransactionEvent, - EcommerceTransactionItemEvent, -} from '@snowplow/tracker-core'; - -export { AddToCartEvent, RemoveFromCartEvent, EcommerceTransactionEvent, EcommerceTransactionItemEvent }; - -interface Transaction { - transaction?: EcommerceTransactionEvent & CommonEventProperties; - items: Array; -} - -function ecommerceTransactionTemplate(): Transaction { - return { - items: [], - }; -} - -const _trackers: Record = {}; -const _transactions: Record = {}; - -/** - * Adds ecommerce and cart tracking - */ -export function EcommercePlugin(): BrowserPlugin { - return { - activateBrowserPlugin: (tracker) => { - _trackers[tracker.id] = tracker; - _transactions[tracker.id] = ecommerceTransactionTemplate(); - }, - }; -} - -/** - * Track an ecommerce transaction - * - * @param event - The event information - * @param trackers - The tracker identifiers which the event will be sent to - */ -export function addTrans( - event: EcommerceTransactionEvent & CommonEventProperties, - trackers: Array = Object.keys(_trackers) -) { - trackers.forEach((t) => { - if (_transactions[t]) { - _transactions[t].transaction = event; - } - }); -} - -/** - * Track an ecommerce transaction item - * - * @param event - The event information - * @param trackers - The tracker identifiers which the event will be sent to - */ -export function addItem( - event: EcommerceTransactionItemEvent & CommonEventProperties, - trackers: Array = Object.keys(_trackers) -) { - trackers.forEach((t) => { - if (_transactions[t]) { - _transactions[t].items.push(event); - } - }); -} - -/** - * Commit the ecommerce transaction - * - * @remarks - * This call will send the data specified with addTrans, ddItem methods to the tracking server. - */ -export function trackTrans(trackers: Array = Object.keys(_trackers)) { - dispatchToTrackersInCollection(trackers, _trackers, (t) => { - const transaction = _transactions[t.id].transaction; - if (transaction) { - t.core.track(buildEcommerceTransaction(transaction), transaction.context, transaction.timestamp); - } - for (var i = 0; i < _transactions[t.id].items.length; i++) { - const item = _transactions[t.id].items[i]; - t.core.track(buildEcommerceTransactionItem(item), item.context, item.timestamp); - } - - _transactions[t.id] = ecommerceTransactionTemplate(); - }); -} - -/** - * Track an add-to-cart event - * - * @param event - The event information - * @param trackers - The tracker identifiers which the event will be sent to - */ -export function trackAddToCart( - event: AddToCartEvent & CommonEventProperties, - trackers: Array = Object.keys(_trackers) -) { - dispatchToTrackersInCollection(trackers, _trackers, (t) => { - t.core.track(buildAddToCart(event), event.context, event.timestamp); - }); -} - -/** - * Track a remove-from-cart event - * - * @param event - The event information - * @param trackers - The tracker identifiers which the event will be sent to - */ -export function trackRemoveFromCart( - event: RemoveFromCartEvent & CommonEventProperties, - trackers: Array = Object.keys(_trackers) -) { - dispatchToTrackersInCollection(trackers, _trackers, (t) => { - t.core.track(buildRemoveFromCart(event), event.context, event.timestamp); - }); -} diff --git a/plugins/browser-plugin-ecommerce/test/events.test.ts b/plugins/browser-plugin-ecommerce/test/events.test.ts deleted file mode 100644 index 961794e6b..000000000 --- a/plugins/browser-plugin-ecommerce/test/events.test.ts +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import { addTracker, BrowserTracker, SharedState } from '@snowplow/browser-tracker-core'; -import { trackerCore } from '@snowplow/tracker-core'; -import F from 'lodash/fp'; -import { EcommercePlugin, trackAddToCart, trackRemoveFromCart, addItem, addTrans, trackTrans } from '../src'; - -const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('evt.e')))); -const extractEventProperties = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('evt.ue_pr'))); -const extractUeEvent = (schema: string) => { - return { - from: F.compose( - F.first, - F.filter(F.compose(F.eq(schema), F.get('schema'))), - F.flatten, - extractEventProperties, - getUEEvents - ), - }; -}; - -describe('EcommercePlugin', () => { - const state = new SharedState(); - addTracker('sp1', 'sp1', 'js-3.0.0', '', state, { - stateStorageStrategy: 'cookie', - encodeBase64: false, - plugins: [EcommercePlugin()], - }); - - it('trackAddToCart adds the expected add to cart event to the queue', () => { - trackAddToCart( - { - quantity: 1, - sku: '12345-1234', - category: 'category-1', - currency: 'currency-1', - name: 'name-1', - unitPrice: 10.99, - }, - ['sp1'] - ); - - expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/add_to_cart/jsonschema/1-0-0').from(state.outQueues[0]) - ).toMatchObject({ - schema: 'iglu:com.snowplowanalytics.snowplow/add_to_cart/jsonschema/1-0-0', - data: { - quantity: 1, - sku: '12345-1234', - category: 'category-1', - currency: 'currency-1', - name: 'name-1', - unitPrice: 10.99, - }, - }); - }); - - it('trackRemoveFromCart adds the expected remove from cart event to the queue', () => { - trackRemoveFromCart( - { - quantity: 1, - sku: '12345-1234', - category: 'category-1', - currency: 'currency-1', - name: 'name-1', - unitPrice: 10.99, - }, - ['sp1'] - ); - - expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/remove_from_cart/jsonschema/1-0-0').from(state.outQueues[0]) - ).toMatchObject({ - schema: 'iglu:com.snowplowanalytics.snowplow/remove_from_cart/jsonschema/1-0-0', - data: { - quantity: 1, - sku: '12345-1234', - category: 'category-1', - currency: 'currency-1', - name: 'name-1', - unitPrice: 10.99, - }, - }); - }); - - it('trackTrans adds the expected transaction event to the queue', (done) => { - let eventCount = 0; - const plugin = EcommercePlugin(); - const core = trackerCore({ - corePlugins: [plugin], - base64: false, - callback: (payloadBuilder) => { - eventCount++; - const payload = payloadBuilder.build(); - if (payload['e'] === 'tr') { - expect(payload['tr_id']).toBe('1234'); - expect(payload['tr_af']).toBe('aff'); - expect(payload['tr_tt']).toBe(420); - expect(payload['tr_tx']).toBe(4.2); - expect(payload['tr_sh']).toBe(10.69); - expect(payload['tr_ci']).toBe('city'); - expect(payload['tr_st']).toBe('texas'); - expect(payload['tr_co']).toBe('country'); - expect(payload['tr_cu']).toBe('usd'); - } - - if (payload['e'] === 'ti') { - expect(payload['ti_id']).toBe('1234'); - expect(payload['ti_sk']).toBe('12345-1111'); - expect(payload['ti_nm']).toBe('name-1'); - expect(payload['ti_ca']).toBe('category-1'); - expect(payload['ti_pr']).toBe(10.99); - expect(payload['ti_qu']).toBe(2); - expect(payload['ti_cu']).toBe('usd'); - } - - if (eventCount === 2) { - done(); - } - }, - }); - - plugin.activateBrowserPlugin?.({ id: 'sp2', core } as BrowserTracker); - - addTrans( - { - orderId: '1234', - total: 420, - affiliation: 'aff', - city: 'city', - country: 'country', - currency: 'usd', - shipping: 10.69, - state: 'texas', - tax: 4.2, - }, - ['sp2'] - ); - - addItem( - { - orderId: '1234', - price: 10.99, - sku: '12345-1111', - category: 'category-1', - currency: 'usd', - name: 'name-1', - quantity: 2, - }, - ['sp2'] - ); - - trackTrans(['sp2']); - }); -}); diff --git a/plugins/browser-plugin-ecommerce/tsconfig.json b/plugins/browser-plugin-ecommerce/tsconfig.json deleted file mode 100644 index 4082f16a5..000000000 --- a/plugins/browser-plugin-ecommerce/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "../../tsconfig.json" -} diff --git a/plugins/browser-plugin-enhanced-consent/jest.config.js b/plugins/browser-plugin-enhanced-consent/jest.config.js index bd3ea4e2a..87d15da9b 100644 --- a/plugins/browser-plugin-enhanced-consent/jest.config.js +++ b/plugins/browser-plugin-enhanced-consent/jest.config.js @@ -1,5 +1,6 @@ module.exports = { preset: 'ts-jest', reporters: ['jest-standard-reporter'], + setupFilesAfterEnv: ['../../setupTestGlobals.ts'], testEnvironment: 'jest-environment-jsdom-global', }; diff --git a/plugins/browser-plugin-enhanced-consent/test/events.test.ts b/plugins/browser-plugin-enhanced-consent/test/events.test.ts index 7c3e81074..d7431ab5f 100644 --- a/plugins/browser-plugin-enhanced-consent/test/events.test.ts +++ b/plugins/browser-plugin-enhanced-consent/test/events.test.ts @@ -1,4 +1,4 @@ -import { addTracker, SharedState } from '@snowplow/browser-tracker-core'; +import { addTracker, SharedState, EventStore } from '@snowplow/browser-tracker-core'; import { EnhancedConsentPlugin, trackCmpVisible, @@ -11,36 +11,32 @@ import { trackConsentWithdrawn, } from '../src'; import { CMP_VISIBLE_SCHEMA } from '../src/schemata'; +import { newInMemoryEventStore } from '@snowplow/tracker-core'; -const extractStateProperties = ({ - outQueues: [ - [ - { - evt: { ue_pr }, - }, - ], - ], -}: any) => ({ unstructuredEvent: JSON.parse(ue_pr).data }); +const extractEventProperties = ([{ ue_pr }]: any) => ({ unstructuredEvent: JSON.parse(ue_pr).data }); describe('EnhancedConsentPlugin events', () => { - let state: SharedState; let idx = 1; + let eventStore: EventStore; + beforeEach(() => { - state = new SharedState(); - addTracker(`sp${idx++}`, `sp${idx++}`, 'js-3.0.0', '', state, { + eventStore = newInMemoryEventStore({}); + addTracker(`sp${idx++}`, `sp${idx++}`, 'js-3.0.0', '', new SharedState(), { stateStorageStrategy: 'cookie', encodeBase64: false, plugins: [EnhancedConsentPlugin()], contexts: { webPage: false }, + eventStore, + customFetch: async () => new Response(null, { status: 500 }), }); }); - it('trackCmpVisible adds the "CMP Visible" event to the queue', () => { + it('trackCmpVisible adds the "CMP Visible" event to the queue', async () => { trackCmpVisible({ elapsedTime: 1500, }); - const { unstructuredEvent } = extractStateProperties(state); + const { unstructuredEvent } = extractEventProperties(await eventStore.getAllPayloads()); expect(unstructuredEvent).toMatchObject({ schema: CMP_VISIBLE_SCHEMA, @@ -48,7 +44,7 @@ describe('EnhancedConsentPlugin events', () => { }); }); - it('trackConsentAllow adds the "allow consent" event to the queue', () => { + it('trackConsentAllow adds the "allow consent" event to the queue', async () => { trackConsentAllow({ basisForProcessing: 'consent', consentUrl: 'http://consent.url', @@ -57,7 +53,7 @@ describe('EnhancedConsentPlugin events', () => { domainsApplied: ['www.example.com'], }); - const { unstructuredEvent } = extractStateProperties(state); + const { unstructuredEvent } = extractEventProperties(await eventStore.getAllPayloads()); expect(unstructuredEvent).toMatchObject({ data: { @@ -71,7 +67,7 @@ describe('EnhancedConsentPlugin events', () => { }); }); - it('trackConsentSelected adds the "allow selected consent" event to the queue', () => { + it('trackConsentSelected adds the "allow selected consent" event to the queue', async () => { trackConsentSelected({ basisForProcessing: 'consent', consentUrl: 'http://consent.url', @@ -80,7 +76,7 @@ describe('EnhancedConsentPlugin events', () => { domainsApplied: ['www.example.com', 'blog.example.com'], }); - const { unstructuredEvent } = extractStateProperties(state); + const { unstructuredEvent } = extractEventProperties(await eventStore.getAllPayloads()); expect(unstructuredEvent).toMatchObject({ data: { @@ -94,7 +90,7 @@ describe('EnhancedConsentPlugin events', () => { }); }); - it('trackConsentPending adds the "pending consent" event to the queue', () => { + it('trackConsentPending adds the "pending consent" event to the queue', async () => { trackConsentPending({ basisForProcessing: 'consent', consentUrl: 'http://consent.url', @@ -103,7 +99,7 @@ describe('EnhancedConsentPlugin events', () => { domainsApplied: ['www.example.com'], }); - const { unstructuredEvent } = extractStateProperties(state); + const { unstructuredEvent } = extractEventProperties(await eventStore.getAllPayloads()); expect(unstructuredEvent).toMatchObject({ data: { @@ -117,7 +113,7 @@ describe('EnhancedConsentPlugin events', () => { }); }); - it('trackConsentImplicit adds the "implicit consent" event to the queue', () => { + it('trackConsentImplicit adds the "implicit consent" event to the queue', async () => { trackConsentImplicit({ basisForProcessing: 'consent', consentUrl: 'http://consent.url', @@ -126,7 +122,7 @@ describe('EnhancedConsentPlugin events', () => { domainsApplied: ['www.example.com'], }); - const { unstructuredEvent } = extractStateProperties(state); + const { unstructuredEvent } = extractEventProperties(await eventStore.getAllPayloads()); expect(unstructuredEvent).toMatchObject({ data: { @@ -140,7 +136,7 @@ describe('EnhancedConsentPlugin events', () => { }); }); - it('trackConsentExpired adds the "expired consent" event to the queue', () => { + it('trackConsentExpired adds the "expired consent" event to the queue', async () => { trackConsentExpired({ basisForProcessing: 'consent', consentUrl: 'http://consent.url', @@ -149,7 +145,7 @@ describe('EnhancedConsentPlugin events', () => { domainsApplied: ['www.example.com'], }); - const { unstructuredEvent } = extractStateProperties(state); + const { unstructuredEvent } = extractEventProperties(await eventStore.getAllPayloads()); expect(unstructuredEvent).toMatchObject({ data: { @@ -163,7 +159,7 @@ describe('EnhancedConsentPlugin events', () => { }); }); - it('trackConsentDeny adds the "consent denied" event to the queue', () => { + it('trackConsentDeny adds the "consent denied" event to the queue', async () => { trackConsentDeny({ basisForProcessing: 'consent', consentUrl: 'http://consent.url', @@ -172,7 +168,7 @@ describe('EnhancedConsentPlugin events', () => { domainsApplied: ['www.example.com'], }); - const { unstructuredEvent } = extractStateProperties(state); + const { unstructuredEvent } = extractEventProperties(await eventStore.getAllPayloads()); expect(unstructuredEvent).toMatchObject({ data: { @@ -186,7 +182,7 @@ describe('EnhancedConsentPlugin events', () => { }); }); - it('trackConsentWithdrawn adds the "consent withdrawn" event to the queue', () => { + it('trackConsentWithdrawn adds the "consent withdrawn" event to the queue', async () => { trackConsentWithdrawn({ basisForProcessing: 'consent', consentUrl: 'http://consent.url', @@ -195,7 +191,7 @@ describe('EnhancedConsentPlugin events', () => { domainsApplied: ['www.example.com'], }); - const { unstructuredEvent } = extractStateProperties(state); + const { unstructuredEvent } = extractEventProperties(await eventStore.getAllPayloads()); expect(unstructuredEvent).toMatchObject({ data: { diff --git a/plugins/browser-plugin-enhanced-ecommerce/README.md b/plugins/browser-plugin-enhanced-ecommerce/README.md index a46477128..f51375227 100644 --- a/plugins/browser-plugin-enhanced-ecommerce/README.md +++ b/plugins/browser-plugin-enhanced-ecommerce/README.md @@ -3,6 +3,10 @@ [![npm version][npm-image]][npm-url] [![License][license-image]](LICENSE) +

⚠️ This package is deprecated, please use @snowplow/browser-plugin-snowplow-ecommerce instead. ⚠️

+ +--- + Browser Plugin to be used with `@snowplow/browser-tracker`. Adds enhanced ecommerce events to your Snowplow tracking. diff --git a/plugins/browser-plugin-enhanced-ecommerce/jest.config.js b/plugins/browser-plugin-enhanced-ecommerce/jest.config.js index bd3ea4e2a..87d15da9b 100644 --- a/plugins/browser-plugin-enhanced-ecommerce/jest.config.js +++ b/plugins/browser-plugin-enhanced-ecommerce/jest.config.js @@ -1,5 +1,6 @@ module.exports = { preset: 'ts-jest', reporters: ['jest-standard-reporter'], + setupFilesAfterEnv: ['../../setupTestGlobals.ts'], testEnvironment: 'jest-environment-jsdom-global', }; diff --git a/plugins/browser-plugin-enhanced-ecommerce/src/index.ts b/plugins/browser-plugin-enhanced-ecommerce/src/index.ts index 3fc6aa44c..43cc52cbf 100644 --- a/plugins/browser-plugin-enhanced-ecommerce/src/index.ts +++ b/plugins/browser-plugin-enhanced-ecommerce/src/index.ts @@ -43,6 +43,8 @@ const _context: Record> = {}; /** * For tracking GA Enhanced Ecommerce events and contexts * {@link https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce} + * + * @deprecated Use @snowplow/browser-plugin-snowplow-ecommerce instead */ export function EnhancedEcommercePlugin(): BrowserPlugin { return { diff --git a/plugins/browser-plugin-enhanced-ecommerce/test/contexts.test.ts b/plugins/browser-plugin-enhanced-ecommerce/test/contexts.test.ts index 601872755..c8ce5ed35 100644 --- a/plugins/browser-plugin-enhanced-ecommerce/test/contexts.test.ts +++ b/plugins/browser-plugin-enhanced-ecommerce/test/contexts.test.ts @@ -38,16 +38,19 @@ import { addEnhancedEcommercePromoContext, trackEnhancedEcommerceAction, } from '../src'; +import { newInMemoryEventStore } from '@snowplow/tracker-core'; -const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('evt.e'))), F.first); -const extractSchemas = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('evt.co'))); +const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('e')))); +const extractSchemas = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('co'))); -it('attaches enhanced ecommerce contexts to enhanced ecommerce events', () => { - const state = new SharedState(); - addTracker('sp1', 'sp1', 'js-3.0.0', '', state, { +it('attaches enhanced ecommerce contexts to enhanced ecommerce events', async () => { + const eventStore = newInMemoryEventStore({}); + addTracker('sp1', 'sp1', 'js-3.0.0', '', new SharedState(), { stateStorageStrategy: 'cookie', encodeBase64: false, plugins: [EnhancedEcommercePlugin()], + eventStore, + customFetch: async () => new Response(null, { status: 500 }), }); addEnhancedEcommerceProductContext({ id: '1234-5678', name: 'T-Shirt' }); @@ -63,5 +66,5 @@ it('attaches enhanced ecommerce contexts to enhanced ecommerce events', () => { F.compose(F.size, F.filter(F.compose(F.eq(value), F.get('data.id'))), extractContextsWithStaticValue); // we expect there to be four contexts added to the event - expect(countWithStaticValueEq('1234-5678')(state.outQueues)).toBe(4); + expect(countWithStaticValueEq('1234-5678')(await eventStore.getAllPayloads())).toBe(4); }); diff --git a/plugins/browser-plugin-error-tracking/jest.config.js b/plugins/browser-plugin-error-tracking/jest.config.js index bd3ea4e2a..76a179aab 100644 --- a/plugins/browser-plugin-error-tracking/jest.config.js +++ b/plugins/browser-plugin-error-tracking/jest.config.js @@ -1,5 +1,7 @@ module.exports = { preset: 'ts-jest', reporters: ['jest-standard-reporter'], + setupFilesAfterEnv: ['../../setupTestGlobals.ts'], testEnvironment: 'jest-environment-jsdom-global', + testEnvironmentOptions: { resources: 'usable', runScripts: 'dangerously' }, }; diff --git a/plugins/browser-plugin-error-tracking/src/index.ts b/plugins/browser-plugin-error-tracking/src/index.ts index bf7fe0617..e0d239412 100644 --- a/plugins/browser-plugin-error-tracking/src/index.ts +++ b/plugins/browser-plugin-error-tracking/src/index.ts @@ -85,7 +85,7 @@ export function trackError( schema: 'iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1', data: { programmingLanguage: 'JAVASCRIPT', - message: truncatedMessage ?? "JS Exception. Browser doesn't support ErrorEvent API", + message: truncatedMessage ?? 'trackError called without required message', stackTrace: stack, lineNumber: lineno, lineColumn: colno, @@ -103,10 +103,10 @@ export function trackError( * The configuration for automatic error tracking */ export interface ErrorTrackingConfiguration { - /** A callback which allows on certain errors to be tracked */ - filter?: (error: ErrorEvent) => boolean; + /** A callback which allows only certain errors to be tracked */ + filter?: (error: ErrorEvent | Event) => boolean; /** A callback to dynamically add extra context based on the error */ - contextAdder?: (error: ErrorEvent) => Array; + contextAdder?: (error: ErrorEvent | Event) => Array; /** Context to be added to every error */ context?: Array; } @@ -121,9 +121,9 @@ export function enableErrorTracking( trackers: Array = Object.keys(_trackers) ) { const { filter, contextAdder, context } = configuration, - captureError = (errorEvent: Event) => { - if ((filter && isFunction(filter) && filter(errorEvent as ErrorEvent)) || filter == null) { - sendError({ errorEvent: errorEvent as ErrorEvent, commonContext: context, contextAdder }, trackers); + captureError = (errorEvent: ErrorEvent | Event) => { + if ((filter && isFunction(filter) && filter(errorEvent)) || filter == null) { + sendError({ errorEvent: errorEvent, commonContext: context, contextAdder }, trackers); } }; @@ -136,9 +136,9 @@ function sendError( commonContext, contextAdder, }: { - errorEvent: ErrorEvent; + errorEvent: ErrorEvent | Event; commonContext?: Array; - contextAdder?: (error: ErrorEvent) => Array; + contextAdder?: (error: ErrorEvent | Event) => Array; }, trackers: Array ) { @@ -147,15 +147,35 @@ function sendError( context = context.concat(contextAdder(errorEvent)); } - trackError( - { - message: errorEvent.message, - filename: errorEvent.filename, - lineno: errorEvent.lineno, - colno: errorEvent.colno, - error: errorEvent.error, - context, - }, - trackers - ); + if ('message' in errorEvent) { + trackError( + { + message: errorEvent.message, + filename: errorEvent.filename, + lineno: errorEvent.lineno, + colno: errorEvent.colno, + error: errorEvent.error, + context, + }, + trackers + ); + } else if (errorEvent.target && 'tagName' in errorEvent.target) { + const element: any = errorEvent.target; + trackError( + { + message: `Non-script error on ${element.tagName} element`, + filename: element.src || undefined, + context, + }, + trackers + ); + } else { + trackError( + { + message: "JS Exception. Browser doesn't support ErrorEvent API", + context, + }, + trackers + ); + } } diff --git a/plugins/browser-plugin-error-tracking/test/events.test.ts b/plugins/browser-plugin-error-tracking/test/events.test.ts index 0378b76f9..857e761df 100644 --- a/plugins/browser-plugin-error-tracking/test/events.test.ts +++ b/plugins/browser-plugin-error-tracking/test/events.test.ts @@ -30,10 +30,11 @@ import { addTracker, SharedState } from '@snowplow/browser-tracker-core'; import F from 'lodash/fp'; -import { ErrorTrackingPlugin, trackError } from '../src'; +import { ErrorTrackingPlugin, enableErrorTracking, trackError } from '../src'; +import { newInMemoryEventStore } from '@snowplow/tracker-core'; -const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('evt.e')))); -const extractEventProperties = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('evt.ue_pr'))); +const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('e')))); +const extractEventProperties = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('ue_pr'))); const extractUeEvent = (schema: string) => { return { from: F.compose( @@ -46,23 +47,43 @@ const extractUeEvent = (schema: string) => { }; }; -describe('AdTrackingPlugin', () => { +describe('ErrorTrackingPlugin', () => { + const eventStore1 = newInMemoryEventStore({}); + const eventStore2 = newInMemoryEventStore({}); + const eventStore3 = newInMemoryEventStore({}); + const eventStore4 = newInMemoryEventStore({}); + const eventStore5 = newInMemoryEventStore({}); + const state = new SharedState(); addTracker('sp1', 'sp1', 'js-3.0.0', '', state, { encodeBase64: false, plugins: [ErrorTrackingPlugin()], + eventStore: eventStore1, + customFetch: async () => new Response(null, { status: 500 }), }); addTracker('sp2', 'sp2', 'js-3.0.0', '', state, { encodeBase64: false, plugins: [ErrorTrackingPlugin()], + eventStore: eventStore2, + customFetch: async () => new Response(null, { status: 500 }), }); addTracker('sp3', 'sp3', 'js-3.0.0', '', state, { encodeBase64: false, plugins: [ErrorTrackingPlugin()], + eventStore: eventStore3, + customFetch: async () => new Response(null, { status: 500 }), }); addTracker('sp4', 'sp4', 'js-3.0.0', '', state, { encodeBase64: false, plugins: [ErrorTrackingPlugin()], + eventStore: eventStore4, + customFetch: async () => new Response(null, { status: 500 }), + }); + addTracker('sp5', 'sp5', 'js-3.0.0', '', state, { + encodeBase64: false, + plugins: [ErrorTrackingPlugin()], + eventStore: eventStore5, + customFetch: async () => new Response(null, { status: 500 }), }); const error = new Error('this is an error'); @@ -105,9 +126,13 @@ describe('AdTrackingPlugin', () => { ['sp4'] ); - it('trackError adds the expected application error event to the queue', () => { + enableErrorTracking({}, ['sp5']); + + it('trackError adds the expected application error event to the queue', async () => { expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1').from(state.outQueues[0]) + extractUeEvent('iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1').from( + await eventStore1.getAllPayloads() + ) ).toMatchObject({ schema: 'iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1', data: { @@ -121,9 +146,11 @@ describe('AdTrackingPlugin', () => { }); }); - it('trackError accepts empty error messages', () => { + it('trackError accepts empty error messages', async () => { expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1').from(state.outQueues[1]) + extractUeEvent('iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1').from( + await eventStore2.getAllPayloads() + ) ).toMatchObject({ schema: 'iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1', data: { @@ -132,20 +159,24 @@ describe('AdTrackingPlugin', () => { }); }); - it('trackError replaces undefined messages with placeholder', () => { + it('trackError replaces undefined messages with placeholder', async () => { expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1').from(state.outQueues[2]) + extractUeEvent('iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1').from( + await eventStore3.getAllPayloads() + ) ).toMatchObject({ schema: 'iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1', data: { - message: "JS Exception. Browser doesn't support ErrorEvent API", + message: 'trackError called without required message', }, }); }); - it('trackError replaces undefined messages with placeholder', () => { + it('trackError truncates long message and stack traces', async () => { expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1').from(state.outQueues[3]) + extractUeEvent('iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1').from( + await eventStore4.getAllPayloads() + ) ).toMatchObject({ schema: 'iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1', data: { @@ -154,4 +185,27 @@ describe('AdTrackingPlugin', () => { }, }); }); + + it('trackError should be called by listener for resource errors', async () => { + const resourceUrl = '/fake-should-404.js'; + + await new Promise((resolve) => { + const resource = document.createElement('script'); + resource.onerror = resolve; + resource.src = resourceUrl; + document.head.appendChild(resource); + }); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1').from( + await eventStore5.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/application_error/jsonschema/1-0-1', + data: { + message: 'Non-script error on SCRIPT element', + fileName: 'http://localhost' + resourceUrl, + }, + }); + }); }); diff --git a/plugins/browser-plugin-focalmeter/jest.config.js b/plugins/browser-plugin-focalmeter/jest.config.js index bd3ea4e2a..87d15da9b 100644 --- a/plugins/browser-plugin-focalmeter/jest.config.js +++ b/plugins/browser-plugin-focalmeter/jest.config.js @@ -1,5 +1,6 @@ module.exports = { preset: 'ts-jest', reporters: ['jest-standard-reporter'], + setupFilesAfterEnv: ['../../setupTestGlobals.ts'], testEnvironment: 'jest-environment-jsdom-global', }; diff --git a/plugins/browser-plugin-focalmeter/test/request.test.ts b/plugins/browser-plugin-focalmeter/test/request.test.ts index 40ec88ecd..7058c31e5 100644 --- a/plugins/browser-plugin-focalmeter/test/request.test.ts +++ b/plugins/browser-plugin-focalmeter/test/request.test.ts @@ -31,7 +31,7 @@ import { addTracker, SharedState } from '@snowplow/browser-tracker-core'; import { enableFocalMeterIntegration, FocalMeterPlugin } from '../src'; -describe('AdTrackingPlugin', () => { +describe('FocalmeterPlugin', () => { // Mock XHR network requests let xhrMock: Partial; let xhrOpenMock: jest.Mock; @@ -67,7 +67,7 @@ describe('AdTrackingPlugin', () => { const userId = tracker?.getDomainUserId(); await checkMock(() => { - expect(xhrOpenMock).toHaveBeenCalledTimes(2); + expect(xhrOpenMock).toHaveBeenCalledTimes(1); expect(xhrOpenMock).toHaveBeenLastCalledWith('GET', `${domain}?vendor=snowplow&cs_fpid=${userId}&c12=not_set`); }); }); @@ -80,7 +80,7 @@ describe('AdTrackingPlugin', () => { const userId = tracker?.getDomainUserId(); await checkMock(() => { - expect(xhrOpenMock).toHaveBeenCalledTimes(2); + expect(xhrOpenMock).toHaveBeenCalledTimes(1); expect(xhrOpenMock).toHaveBeenLastCalledWith('GET', `${domain}?vendor=snowplow&cs_fpid=${userId}-processed&c12=not_set`); }); }); @@ -93,7 +93,7 @@ describe('AdTrackingPlugin', () => { tracker?.enableAnonymousTracking(); tracker?.trackPageView(); await checkMock(() => { - expect(xhrOpenMock).toHaveBeenCalledTimes(1); + expect(xhrOpenMock).toHaveBeenCalledTimes(0); }); // Makes a request when disabling anonymous tracking @@ -101,14 +101,14 @@ describe('AdTrackingPlugin', () => { tracker?.trackPageView(); const userId = tracker?.getDomainUserId(); await checkMock(() => { - expect(xhrOpenMock).toHaveBeenCalledTimes(2); + expect(xhrOpenMock).toHaveBeenCalledTimes(1); expect(xhrOpenMock).toHaveBeenLastCalledWith('GET', `${domain}?vendor=snowplow&cs_fpid=${userId}&c12=not_set`); }); // Doesn't make another request since user ID didn't change tracker?.trackPageView(); await checkMock(() => { - expect(xhrOpenMock).toHaveBeenCalledTimes(1); + expect(xhrOpenMock).toHaveBeenCalledTimes(0); }); }); @@ -125,15 +125,15 @@ describe('AdTrackingPlugin', () => { // Makes requests for both trackers tracker1?.trackPageView(); - await checkMock(() => expect(xhrOpenMock).toHaveBeenCalledTimes(2)); + await checkMock(() => expect(xhrOpenMock).toHaveBeenCalledTimes(1)); tracker2?.trackPageView(); - await checkMock(() => expect(xhrOpenMock).toHaveBeenCalledTimes(2)); + await checkMock(() => expect(xhrOpenMock).toHaveBeenCalledTimes(1)); // Doesn't make any more requests for the trackers tracker1?.trackPageView(); - await checkMock(() => expect(xhrOpenMock).toHaveBeenCalledTimes(1)); + await checkMock(() => expect(xhrOpenMock).toHaveBeenCalledTimes(0)); tracker2?.trackPageView(); - await checkMock(() => expect(xhrOpenMock).toHaveBeenCalledTimes(1)); + await checkMock(() => expect(xhrOpenMock).toHaveBeenCalledTimes(0)); }); function createTrackerWithPlugin(id: string | undefined = undefined) { @@ -144,6 +144,7 @@ describe('AdTrackingPlugin', () => { stateStorageStrategy: 'cookie', encodeBase64: false, plugins: [FocalMeterPlugin()], + customFetch: async () => new Response(null, { status: 200 }), }); } diff --git a/plugins/browser-plugin-browser-features/jest.config.js b/plugins/browser-plugin-form-tracking/jest.config.js similarity index 71% rename from plugins/browser-plugin-browser-features/jest.config.js rename to plugins/browser-plugin-form-tracking/jest.config.js index bd3ea4e2a..87d15da9b 100644 --- a/plugins/browser-plugin-browser-features/jest.config.js +++ b/plugins/browser-plugin-form-tracking/jest.config.js @@ -1,5 +1,6 @@ module.exports = { preset: 'ts-jest', reporters: ['jest-standard-reporter'], + setupFilesAfterEnv: ['../../setupTestGlobals.ts'], testEnvironment: 'jest-environment-jsdom-global', }; diff --git a/plugins/browser-plugin-form-tracking/package.json b/plugins/browser-plugin-form-tracking/package.json index fcce4fea1..248c105e9 100644 --- a/plugins/browser-plugin-form-tracking/package.json +++ b/plugins/browser-plugin-form-tracking/package.json @@ -19,7 +19,7 @@ ], "scripts": { "build": "rollup -c --silent --failAfterWarnings", - "test": "" + "test": "jest" }, "dependencies": { "@snowplow/browser-tracker-core": "workspace:*", diff --git a/plugins/browser-plugin-form-tracking/src/helpers.ts b/plugins/browser-plugin-form-tracking/src/helpers.ts index 4b424dd04..d7d0e7309 100644 --- a/plugins/browser-plugin-form-tracking/src/helpers.ts +++ b/plugins/browser-plugin-form-tracking/src/helpers.ts @@ -1,16 +1,17 @@ import { - getCssClasses, addEventListener, - BrowserTracker, - FilterCriterion, + flushPendingCookies, + getCssClasses, getFilterByClass, getFilterByName, + type BrowserTracker, + type FilterCriterion, } from '@snowplow/browser-tracker-core'; import { - resolveDynamicContext, - DynamicContext, buildFormFocusOrChange, buildFormSubmission, + resolveDynamicContext, + type DynamicContext, } from '@snowplow/tracker-core'; /** The form tracking configuration */ @@ -22,7 +23,7 @@ export interface FormTrackingConfiguration { } /** Events to capture in form tracking */ -export enum FormTrackingEvent { +enum FormTrackingEvent { /** Form field changed event */ CHANGE_FORM = 'change_form', /** Form field focused event */ @@ -31,45 +32,45 @@ export enum FormTrackingEvent { SUBMIT_FORM = 'submit_form', } -/** List of form tracking events to capture */ -export type FormTrackingEvents = Array; const defaultFormTrackingEvents = [ FormTrackingEvent.CHANGE_FORM, FormTrackingEvent.FOCUS_FORM, FormTrackingEvent.SUBMIT_FORM, ]; -export interface FormTrackingOptions { - forms?: FilterCriterion | HTMLCollectionOf | NodeListOf; - fields?: FilterCriterion & { transform: transformFn }; - events?: FormTrackingEvents; +/** Form tracking plugin options to determine which events to fire and the elements to listen for */ +interface FormTrackingOptions { + /** List of `form` elements that are allowed to generate events, or criteria for deciding that when the event listener handles the event */ + forms?: + | FilterCriterion + | HTMLCollectionOf + | NodeListOf + | HTMLFormElement[]; + /** Criteria for fields within forms that should generate focus or change events; you may also include a transformation function for fields that may include personal data */ + fields?: FilterCriterion & { transform?: transformFn }; + /** Allow list of events to enable tracking for; can be any combination of focus_form, change_form, or submit_form */ + events?: `${FormTrackingEvent}`[]; + /** A list of targets to add event listeners to. If not provided, defaults to the current `document` */ + targets?: EventTarget[]; } -export interface TrackedHTMLElementTagNameMap { - textarea: HTMLTextAreaElement; - input: HTMLInputElement; - select: HTMLSelectElement; -} +type TrackedHTMLElementTagNameMap = Pick; -export type TrackedHTMLElement = TrackedHTMLElementTagNameMap[keyof TrackedHTMLElementTagNameMap]; +type TrackedHTMLElement = TrackedHTMLElementTagNameMap[keyof TrackedHTMLElementTagNameMap]; -export interface ElementData extends Record { +interface ElementData extends Record { name: string; value: string | null; nodeName: string; type?: string; } -export type transformFn = ( +type transformFn = ( elementValue: string | null, elementInfo: ElementData | TrackedHTMLElement, elt: TrackedHTMLElement ) => string | null; -export const innerElementTags: Array = ['textarea', 'input', 'select']; - -type TrackedHTMLElementWithMarker = TrackedHTMLElement & Record; - type ElementDataWrapper = { elementData: ElementData; originalElement: TrackedHTMLElement }; const defaultTransformFn: transformFn = (x) => x; @@ -78,57 +79,76 @@ interface FormConfiguration { formFilter: (_: HTMLFormElement) => boolean; fieldFilter: (_: TrackedHTMLElement) => boolean; fieldTransform: transformFn; + forms: HTMLCollectionOf | NodeListOf | HTMLFormElement[] | null; } -/* - * Add submission event listeners to all form elements - * Add value change event listeners to all mutable inner form elements +const _focusListeners: Record = {}; +const _changeListeners: Record = {}; +const _submitListeners: Record = {}; +const _targets: Record = {}; + +/** + * Add submission/focus/change event listeners to page for forms and elements according to `configuration` + * + * @param tracker The tracker instance the listener belongs to that will be used to track events + * @param configuration Plugin configuration controlling the events to track and forms/fields to target or transform */ export function addFormListeners(tracker: BrowserTracker, configuration: FormTrackingConfiguration) { const { options, context } = configuration, - trackingMarker = tracker.id + 'form', config = getConfigurationForOptions(options); - let forms = config.forms ?? document.getElementsByTagName('form'); - Array.prototype.slice.call(forms).forEach(function (form: HTMLFormElement) { - if (config.formFilter(form)) { - Array.prototype.slice.call(innerElementTags).forEach(function (tagname: keyof TrackedHTMLElementTagNameMap) { - Array.prototype.slice - .call(form.getElementsByTagName(tagname)) - .forEach(function (innerElement: TrackedHTMLElementWithMarker) { - if ( - config.fieldFilter(innerElement) && - !innerElement[trackingMarker] && - innerElement.type.toLowerCase() !== 'password' - ) { - if (config.eventFilter(FormTrackingEvent.FOCUS_FORM)) { - addEventListener( - innerElement, - 'focus', - getFormChangeListener(tracker, config, 'focus_form', context), - false - ); - } - if (config.eventFilter(FormTrackingEvent.CHANGE_FORM)) { - addEventListener( - innerElement, - 'change', - getFormChangeListener(tracker, config, 'change_form', context), - false - ); - } - innerElement[trackingMarker] = true; - } - }); - }); + const events = options?.events ?? defaultFormTrackingEvents; - if (!form[trackingMarker]) { - if (config.eventFilter(FormTrackingEvent.SUBMIT_FORM)) { - addEventListener(form, 'submit', getFormSubmissionListener(tracker, config, trackingMarker, context)); - } - form[trackingMarker] = true; - } - } + const targets = (_targets[tracker.id] = getTargetList(options?.targets, config.forms)); + + if (events.indexOf(FormTrackingEvent.FOCUS_FORM) !== -1) { + _focusListeners[tracker.id] = getFormChangeListener(tracker, config, FormTrackingEvent.FOCUS_FORM, context); + targets.forEach((target) => addEventListener(target, 'focus', _focusListeners[tracker.id], true)); + } + if (events.indexOf(FormTrackingEvent.CHANGE_FORM) !== -1) { + _changeListeners[tracker.id] = getFormChangeListener(tracker, config, FormTrackingEvent.CHANGE_FORM, context); + targets.forEach((target) => addEventListener(target, 'change', _changeListeners[tracker.id], true)); + } + if (events.indexOf(FormTrackingEvent.SUBMIT_FORM) !== -1) { + _submitListeners[tracker.id] = getFormSubmissionListener(tracker, config, context); + targets.forEach((target) => addEventListener(target, 'submit', _submitListeners[tracker.id], true)); + } +} + +/** + * Builds a list of targets for the plugin event listeners + * + * The list can include any specifically provided targets, and will be extended to include the root nodes of any explicit HTMLFormElements provided + * With neither provided, defaults to the current page's `document` element + * + * @param configTargets Explicitly configured list of event target listeners, if any + * @param forms Explicitly configured list of form elements to track, if any + * @returns List of EventTargets to add the listener to + */ +function getTargetList(configTargets: EventTarget[] | undefined, forms: FormConfiguration['forms']) { + // we attach to document rather than window because the window focus event occurs more often than we require + const targets = configTargets ?? [document]; + + if (forms) { + Array.prototype.forEach.call(forms, (form: HTMLFormElement) => { + targets.push(form.ownerDocument.documentElement); + }); + } + + return targets; +} + +/** + * Remove all submission/focus/change event listeners from page that have been added via a call to `addFormListeners` + * + * @param tracker The tracker instance the listener belongs to that will be used to track events + */ +export function removeFormListeners(tracker: BrowserTracker) { + const targets = _targets[tracker.id] ?? [document]; + targets.forEach((target) => { + if (_focusListeners[tracker.id]) target.removeEventListener('focus', _focusListeners[tracker.id], true); + if (_changeListeners[tracker.id]) target.removeEventListener('change', _changeListeners[tracker.id], true); + if (_submitListeners[tracker.id]) target.removeEventListener('submit', _submitListeners[tracker.id], true); }); } @@ -136,31 +156,69 @@ export function addFormListeners(tracker: BrowserTracker, configuration: FormTra * Check if forms array is a collection of HTML form elements or a filter or undefined */ function isCollectionOfHTMLFormElements( - forms?: FilterCriterion | HTMLCollectionOf | NodeListOf -): forms is HTMLCollectionOf | NodeListOf { + forms?: + | FilterCriterion + | HTMLCollectionOf + | NodeListOf + | HTMLFormElement[] +): forms is HTMLCollectionOf | NodeListOf | HTMLFormElement[] { return forms != null && Array.prototype.slice.call(forms).length > 0; } -/* +/** + * Typeguard for `element` to see if it appears to be the HTMLElement with tagName `type` + * + * instanceof checks don't work for cross-document nodes, which this plugin supports + * + * @param elem Object to check element type + * @param type Element type we're checking for + * @returns If `element` is an element with tagName `type` + */ +function isElement>( + elem: unknown, + type: E +): elem is HTMLElementTagNameMap[Lowercase] { + if (typeof elem === 'object' && elem) { + if ('tagName' in elem && typeof (elem as Element)['tagName'] === 'string') { + return (elem as Element).tagName.toUpperCase() === type; + } + } + + return false; +} + +/** + * Determine if given object is a `TrackedHTMLElement` or not + * + * @param element Value to determine + * @returns If `element` is `TrackedHTMLElement` + */ +function isTrackableElement(element: EventTarget | null): element is TrackedHTMLElement { + return isElement(element, 'INPUT') || isElement(element, 'SELECT') || isElement(element, 'TEXTAREA'); +} + +/** * Configures form tracking: which forms and fields will be tracked, and the context to attach + * + * @param options User-supplied configuration + * @returns Final configuration incorporating defaults */ -function getConfigurationForOptions(options?: FormTrackingOptions) { +function getConfigurationForOptions(options?: FormTrackingOptions): FormConfiguration { if (options) { let formFilter = (_: HTMLElement) => true; - let forms: HTMLCollectionOf | NodeListOf | null = null; + let forms: HTMLCollectionOf | NodeListOf | HTMLFormElement[] | null = null; if (isCollectionOfHTMLFormElements(options.forms)) { - // options.forms is a collection of HTML form elements + // options.forms is an explicity allowlist of HTML form elements forms = options.forms; } else { // options.forms is null or a filter formFilter = getFilterByClass(options.forms); } return { - forms: forms, - formFilter: formFilter, + forms, + formFilter, fieldFilter: getFilterByName(options.fields), fieldTransform: getTransform(options.fields), - eventFilter: (event: FormTrackingEvent) => (options.events ?? defaultFormTrackingEvents).indexOf(event) > -1, }; } else { return { @@ -168,31 +226,51 @@ function getConfigurationForOptions(options?: FormTrackingOptions) { formFilter: () => true, fieldFilter: () => true, fieldTransform: defaultTransformFn, - eventFilter: () => true, }; } } +/** + * Check if the found target element is included in the explicit form allowlist, if provided. + * + * @param target A `form` element to check if we're allowed to track. + * @param allowed An optional list of form elements we want to track against. + * @returns True if there is no allowlist or the `target` is in the allowlist, false otherwise. + */ +function explicitlyAllowedForm(target: HTMLFormElement, allowed: FormConfiguration['forms']) { + if (!allowed) return true; + + for (let i = 0; i < allowed.length; i++) { + if (allowed[i].isSameNode(target)) return true; + } + + return false; +} + /** * Convert a criterion object to a transform function * - * @param object - criterion {transform: function (elt) {return the result of transform function applied to element} + * @param criterion + * @returns Transformation function if provided in `criterion`, or a default identity function */ -function getTransform(criterion?: { transform: transformFn }): transformFn { - if (criterion && Object.prototype.hasOwnProperty.call(criterion, 'transform')) { +function getTransform(criterion?: { transform?: transformFn }): transformFn { + if (criterion && typeof criterion.transform === 'function') { return criterion.transform; } return defaultTransformFn; } -/* - * Get an identifier for a form, input, textarea, or select element +/** + * Get an identifier for a form or `TrackedHTMLElement` + * + * @param elt Element to identify + * @returns Identifier for `elt` */ function getElementIdentifier(elt: Record) { - const properties: Array<'name' | 'id' | 'type' | 'nodeName'> = ['name', 'id', 'type', 'nodeName']; + const properties = ['name', 'id', 'type', 'nodeName'] as const; for (const propName of properties) { - if (elt[propName] != false && typeof elt[propName] === 'string') { + if (elt[propName] && typeof elt[propName] === 'string') { return elt[propName]; } } @@ -200,115 +278,166 @@ function getElementIdentifier(elt: Record) { return null; } -/* - * Identifies the parent form in which an element is contained +/** + * Discovers the parent form in which an element is contained + * + * @param elt Child control to identify the owning form for + * @returns The form element this control belongs to or null if not found */ -function getParentFormIdentifier(elt: Node | null) { - while (elt && elt.nodeName && elt.nodeName.toUpperCase() !== 'HTML' && elt.nodeName.toUpperCase() !== 'FORM') { - elt = elt.parentNode; - } - if (elt && elt.nodeName && elt.nodeName.toUpperCase() === 'FORM') { - return getElementIdentifier(elt); +function getParentForm(elt: TrackedHTMLElement | null) { + if (elt && elt.form) return elt.form; + + let parent: ParentNode | null = elt; + + while (parent) { + if (isElement(parent, 'FORM')) { + return parent; + } + parent = parent.parentNode; } - return null; + return parent; } -/* - * Returns a list of the input, textarea, and select elements inside a form along with their values +/** + * Returns a list of the `TrackedHTMLElement`s inside a form along with their values + * + * @param elt Form element to get the control elements for + * @returns Array of wrapped control elements belonging to the form */ -function getInnerFormElements(trackingMarker: string, elt: HTMLFormElement) { - var innerElements: Array = []; - Array.prototype.slice.call(innerElementTags).forEach((tagname: 'textarea' | 'input' | 'select') => { - let trackedChildren = Array.prototype.slice.call(elt.getElementsByTagName(tagname)).filter(function (child) { - return child.hasOwnProperty(trackingMarker); - }); +function getInnerFormElements(elt: HTMLFormElement) { + const innerElements: Array = []; - Array.prototype.slice.call(trackedChildren).forEach(function (child) { - if (child.type === 'submit') { - return; - } - var elementJson: ElementDataWrapper = { - elementData: { - name: getElementIdentifier(child), - value: child.value, - nodeName: child.nodeName, - }, - originalElement: child, - }; - if (child.type && child.nodeName.toUpperCase() === 'INPUT') { - elementJson.elementData.type = child.type; - } + Array.prototype.forEach.call(elt.elements, function (child: Element) { + if (!isTrackableElement(child)) return; + + const inputType = (child.type || 'text').toLowerCase(); - if ((child.type === 'checkbox' || child.type === 'radio') && !(child as HTMLInputElement).checked) { + // submit and image are roughly equivalent + if (inputType === 'submit' || inputType === 'image') { + return; + } + + const elementJson: ElementDataWrapper = { + elementData: { + name: getElementIdentifier(child)!, + value: child.value, + nodeName: child.nodeName, + }, + originalElement: child, + }; + + if (isElement(child, 'INPUT')) { + elementJson.elementData.type = inputType; + + if (inputType === 'password' || ((inputType === 'checkbox' || inputType === 'radio') && !child.checked)) { elementJson.elementData.value = null; } - innerElements.push(elementJson); - }); + } + + innerElements.push(elementJson); }); return innerElements; } -/* - * Return function to handle form field change event +/** + * Create closure function to handle form field change/focus event + * + * @param tracker The tracker instance to generate the event with + * @param config Plugin configuration + * @param event_type Type of event to generate + * @param context List of entities or context generators to evaluate with the event + * @returns A form change/focus handler */ function getFormChangeListener( tracker: BrowserTracker, config: FormConfiguration, - event_type: 'change_form' | 'focus_form', + event_type: Exclude, context?: DynamicContext | null ) { return function (e: Event) { - var elt = e.target as TrackedHTMLElement; - if (elt) { - var type = elt.nodeName && elt.nodeName.toUpperCase() === 'INPUT' ? elt.type : null; - var value = - elt.type === 'checkbox' && !(elt as HTMLInputElement).checked - ? null - : config.fieldTransform(elt.value, elt, elt); + const target = e.composed ? e.composedPath()[0] : e.target; + + // `change` and `submit` are not composed and are thus invisible to us + // bind late to the forms/field directly on field focus in this case + if (target !== e.target && e.composed && isTrackableElement(target)) { + if (target.form) { + if (_changeListeners[tracker.id]) addEventListener(target.form, 'change', _changeListeners[tracker.id], true); + if (_submitListeners[tracker.id]) addEventListener(target.form, 'submit', _submitListeners[tracker.id], true); + } else { + if (_changeListeners[tracker.id]) addEventListener(target, 'change', _changeListeners[tracker.id], true); + } + } + + if (isTrackableElement(target) && config.fieldFilter(target)) { + let value: string | null = null; + let type: string | null = null; + + if (isElement(target, 'INPUT')) { + type = (target.type || 'text').toLowerCase(); + value = + (type === 'checkbox' && !target.checked) || type === 'password' + ? null + : config.fieldTransform(target.value, target, target); + } else { + value = config.fieldTransform(target.value, target, target); + } + + const form = getParentForm(target); + if (!(form && config.formFilter(form) && explicitlyAllowedForm(form, config.forms))) return; + if (event_type === 'change_form' || (type !== 'checkbox' && type !== 'radio')) { tracker.core.track( buildFormFocusOrChange({ schema: event_type, - formId: getParentFormIdentifier(elt) ?? '', - elementId: getElementIdentifier(elt) ?? '', - nodeName: elt.nodeName, + formId: getElementIdentifier(form ?? {}) ?? '', + elementId: getElementIdentifier(target) ?? '', + nodeName: target.nodeName, type, - elementClasses: getCssClasses(elt), + elementClasses: getCssClasses(target), value: value ?? null, }), - resolveDynamicContext(context, elt, type, value) + resolveDynamicContext(context, target, type, value) ); } } }; } -/* - * Return function to handle form submission event +/** + * Create closure function to handle form submission event + * + * @param tracker The tracker instance to generate the event with + * @param config Plugin configuration + * @param context List of entities or context generators to evaluate with the event + * @returns A form submit handler */ function getFormSubmissionListener( tracker: BrowserTracker, config: FormConfiguration, - trackingMarker: string, context?: DynamicContext | null ) { - return function (e: Event) { - var elt = e.target as HTMLFormElement; - var innerElements = getInnerFormElements(trackingMarker, elt); - innerElements.forEach(function (innerElement) { - var eltData = innerElement.elementData; - eltData.value = config.fieldTransform(eltData.value, eltData, innerElement.originalElement) ?? eltData.value; - }); - var elementsData = innerElements.map((elt) => elt.elementData); - tracker.core.track( - buildFormSubmission({ - formId: getElementIdentifier(elt) ?? '', - formClasses: getCssClasses(elt), - elements: elementsData, - }), - resolveDynamicContext(context, elt, elementsData) - ); + return function ({ target }: Event) { + if (isElement(target, 'FORM') && config.formFilter(target) && explicitlyAllowedForm(target, config.forms)) { + const elementsData: ElementData[] = []; + + getInnerFormElements(target).forEach(function ({ elementData, originalElement }) { + if (config.fieldFilter(originalElement) && originalElement.type.toLowerCase() !== 'password') { + elementData.value = config.fieldTransform(elementData.value, elementData, originalElement); + elementsData.push(elementData); + } + }); + + tracker.core.track( + buildFormSubmission({ + formId: getElementIdentifier(target) ?? '', + formClasses: getCssClasses(target), + elements: elementsData, + }), + resolveDynamicContext(context, target, elementsData) + ); + flushPendingCookies(); + } }; } diff --git a/plugins/browser-plugin-form-tracking/src/index.ts b/plugins/browser-plugin-form-tracking/src/index.ts index 3f3f79a39..8649dc16a 100644 --- a/plugins/browser-plugin-form-tracking/src/index.ts +++ b/plugins/browser-plugin-form-tracking/src/index.ts @@ -1,12 +1,12 @@ -import { BrowserPlugin, BrowserTracker } from '@snowplow/browser-tracker-core'; -import { addFormListeners, FormTrackingConfiguration } from './helpers'; +import type { BrowserPlugin, BrowserTracker } from '@snowplow/browser-tracker-core'; +import { addFormListeners, removeFormListeners, type FormTrackingConfiguration } from './helpers'; -export { FormTrackingConfiguration } from './helpers'; +export { type FormTrackingConfiguration } from './helpers'; const _trackers: Record = {}; /** - * A plugin which enabled automatic form tracking + * A plugin which enables automatic form focus, change, and submit tracking */ export function FormTrackingPlugin(): BrowserPlugin { return { @@ -17,9 +17,10 @@ export function FormTrackingPlugin(): BrowserPlugin { } /** - * Enables automatic form tracking + * Enables automatic form tracking. + * * An event will be fired when a form field is changed or a form submitted. - * This can be called multiple times: only forms not already tracked will be tracked. + * This can be called multiple times: previous listeners will be removed and replaced with any updated configuration. * * @param configuration - The form tracking configuration * @param trackers - The tracker identifiers which the events will be sent to @@ -30,13 +31,23 @@ export function enableFormTracking( ) { trackers.forEach((t) => { if (_trackers[t]) { - if (_trackers[t].sharedState.hasLoaded) { - addFormListeners(_trackers[t], configuration); - } else { - _trackers[t].sharedState.registeredOnLoadHandlers.push(function () { - addFormListeners(_trackers[t], configuration); - }); - } + removeFormListeners(_trackers[t]); + addFormListeners(_trackers[t], configuration); + } + }); +} + +/** + * Disables automatic form tracking. + * + * All page-level listeners for the given trackers will be removed. + * + * @param trackers - The tracker identifiers which the events will be sent to + */ +export function disableFormTracking(trackers: Array = Object.keys(_trackers)) { + trackers.forEach((t) => { + if (_trackers[t]) { + removeFormListeners(_trackers[t]); } }); } diff --git a/plugins/browser-plugin-form-tracking/test/events.test.ts b/plugins/browser-plugin-form-tracking/test/events.test.ts new file mode 100644 index 000000000..fb1f7acf4 --- /dev/null +++ b/plugins/browser-plugin-form-tracking/test/events.test.ts @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2022 Snowplow Analytics Ltd, 2010 Anthon Pang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { addTracker, SharedState } from '@snowplow/browser-tracker-core'; +import { enableFormTracking, disableFormTracking, FormTrackingPlugin } from '../src'; +import { newInMemoryEventStore } from '@snowplow/tracker-core'; + +const getUEEvents = (arr: any) => arr.filter(({ e }: any) => e === 'ue'); +const extractEventProperties = (arr: any) => arr.map(({ ue_pr }: any) => JSON.parse(ue_pr).data); +const extractUeEvent = (schema: string) => ({ + from: (arr: any, n: number = 0) => + extractEventProperties(getUEEvents(arr)) + .reduce((acc: any[], curr: any[]) => acc.concat([curr]), []) + .filter((evt: any) => evt.schema === schema)[n], +}); + +describe('FormTrackingPlugin', () => { + const eventStore = newInMemoryEventStore({}); + addTracker('sp1', 'sp1', 'js-3.0.0', '', new SharedState(), { + stateStorageStrategy: 'cookie', + encodeBase64: false, + plugins: [FormTrackingPlugin()], + eventStore, + customFetch: async () => new Response(null, { status: 500 }), + }); + + const $addEventListener = jest.spyOn(document, 'addEventListener'); + const $removeEventListener = jest.spyOn(document, 'removeEventListener'); + + document.body.appendChild(Object.assign(document.createElement('form'), { id: 'test-form' })); + + afterEach(async () => { + // clear the event store after each test + await eventStore.removeHead(await eventStore.count()); + jest.clearAllMocks(); + document.forms[0].replaceChildren(); + }); + + describe('enableFormTracking', () => { + it('does nothing for no trackers', () => { + enableFormTracking({}, []); + expect($addEventListener).not.toBeCalled(); + }); + + it('adds form listeners by default', () => { + enableFormTracking(); + + expect($addEventListener).toBeCalledWith('focus', expect.anything(), true); + expect($addEventListener).toBeCalledWith('change', expect.anything(), true); + expect($addEventListener).toBeCalledWith('submit', expect.anything(), true); + }); + + it('tracks focus on fields that already exist', async () => { + const target = document.createElement('input'); + document.forms[0].appendChild(target); + + enableFormTracking(); + + target.focus(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0', + data: { + formId: 'test-form', + }, + }); + }); + + it('tracks focus on fields added after enabling', async () => { + enableFormTracking(); + + const target = document.createElement('input'); + document.forms[0].appendChild(target); + + target.focus(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0', + data: { + formId: 'test-form', + }, + }); + }); + + it('tracks changes on field values', async () => { + enableFormTracking(); + + const target = document.createElement('input'); + document.forms[0].appendChild(target); + + target.value = 'changed'; + target.dispatchEvent(new Event('change')); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0', + data: { + formId: 'test-form', + value: 'changed', + }, + }); + }); + + it('tracks from forms in shadowdom', async () => { + window.customElements.define( + 'shadow-form', + class extends HTMLElement { + connectedCallback() { + const form = Object.assign(document.createElement('form'), { id: 'shadow-form' }); + + form.addEventListener( + 'submit', + function (e) { + e.preventDefault(); + }, + false + ); + + const input = document.createElement('input'); + form.appendChild(input); + + const shadowRoot = this.attachShadow({ mode: 'open' }); + shadowRoot.appendChild(form); + } + } + ); + + const shadow = document.createElement('shadow-form'); + document.body.appendChild(shadow); + + enableFormTracking(); + + const target = shadow.shadowRoot!.querySelector('input')!; + + target.focus(); + + target.value = 'changed'; + target.dispatchEvent(new Event('change')); + + target.form!.submit(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0', + data: { + formId: 'shadow-form', + }, + }); + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0', + data: { + formId: 'shadow-form', + value: 'changed', + }, + }); + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/submit_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/submit_form/jsonschema/1-0-0', + data: { + formId: 'shadow-form', + }, + }); + }); + + it('associates non-nested forms correctly', async () => { + enableFormTracking(); + + const target = document.createElement('input'); + target.setAttribute('form', 'test-form'); + document.body.appendChild(target); + + target.focus(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0', + data: { + formId: 'test-form', + }, + }); + + document.body.removeChild(target); + }); + + it('ignores password values', async () => { + enableFormTracking(); + + const target = document.createElement('input'); + target.type = 'password'; + target.value = 'initial'; + document.forms[0].appendChild(target); + + target.focus(); + target.value = 'zomg_private!1'; + target.dispatchEvent(new Event('change')); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0', + data: { + formId: 'test-form', + nodeName: 'INPUT', + elementType: 'password', + value: null, + }, + }); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0', + data: { + formId: 'test-form', + nodeName: 'INPUT', + type: 'password', + value: null, + }, + }); + }); + + it('does not listen for ignored event types', () => { + enableFormTracking({ options: { events: ['focus_form'] } }); + expect($addEventListener).toBeCalledTimes(1); + expect($addEventListener).toBeCalledWith('focus', expect.anything(), true); + }); + + it('ignores form that are not explicitly specified', async () => { + const extra = document.createElement('form'); + extra.id = 'skipme'; + document.body.appendChild(extra); + + enableFormTracking({ options: { forms: [document.forms[0]] } }); + + let target = document.createElement('input'); + extra.appendChild(target); + target.focus(); + + expect(await eventStore.getAllPayloads()).toHaveLength(0); + + target = document.createElement('input'); + document.forms[0].appendChild(target); + target.focus(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0', + data: { + formId: 'test-form', + }, + }); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0').from( + await eventStore.getAllPayloads() + ) + ).not.toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0', + data: { + formId: 'skipme', + }, + }); + + document.body.removeChild(extra); + }); + }); + + describe('disableFormTracking', () => { + it('removes any listeners added', () => { + enableFormTracking(); + disableFormTracking(); + + const addCalls = $addEventListener.mock.calls; + + expect(addCalls).toHaveLength(3); + + addCalls.forEach((call) => expect($removeEventListener.mock.calls).toContainEqual(call)); + }); + }); +}); diff --git a/plugins/browser-plugin-ga-cookies/README.md b/plugins/browser-plugin-ga-cookies/README.md index 423b14fbf..05bd088f2 100644 --- a/plugins/browser-plugin-ga-cookies/README.md +++ b/plugins/browser-plugin-ga-cookies/README.md @@ -5,17 +5,17 @@ Browser Plugin to be used with `@snowplow/browser-tracker`. -Adds Universal Analytics and Google Analytics 4 cookies to your Snowplow tracking. +Adds Google Analytics 4 and optionally Universal Analytics cookies to your Snowplow tracking. ## Maintainer quick start -Part of the Snowplow JavaScript Tracker monorepo. -Build with [Node.js](https://nodejs.org/en/) (18 - 20) and [Rush](https://rushjs.io/). +Part of the Snowplow JavaScript Tracker monorepo. +Build with [Node.js](https://nodejs.org/en/) (18+) and [Rush](https://rushjs.io/). ### Setup repository ```bash -npm install -g @microsoft/rush +npm install -g @microsoft/rush git clone https://github.com/snowplow/snowplow-javascript-tracker.git rush update ``` @@ -40,14 +40,14 @@ newTracker('sp1', '{{collector}}', { plugins: [ GaCookiesPlugin( /* pluginOptions */ ) ] }); -/* +/* * Available plugin options `GACookiesPluginOptions`: * { - * ua: Send Universal Analytics specific cookie values. Defaults to true. - * ga4: Send Google Analytics 4 specific cookie values. Defaults to false. + * ua: Send Universal Analytics specific cookie values. Defaults to false. + * ga4: Send Google Analytics 4 specific cookie values. Defaults to true. * ga4MeasurementId: Measurement id/ids to search the Google Analytics 4 session cookie. Can be a single measurement id as a string or an array of measurement id strings. The cookie has the form of _ga_ where is the data stream container id and is the optional cookie_prefix option of the gtag.js tracker. * cookiePrefix: Cookie prefix set on the Google Analytics 4 cookies using the cookie_prefix option of the gtag.js tracker. - * } + * } */ ``` diff --git a/plugins/browser-plugin-ga-cookies/src/index.ts b/plugins/browser-plugin-ga-cookies/src/index.ts index bd7a78ed3..c44597148 100644 --- a/plugins/browser-plugin-ga-cookies/src/index.ts +++ b/plugins/browser-plugin-ga-cookies/src/index.ts @@ -11,8 +11,8 @@ interface GACookiesPluginOptions { } const defaultPluginOptions: GACookiesPluginOptions = { - ua: true, - ga4: false, + ua: false, + ga4: true, ga4MeasurementId: '', cookiePrefix: [], }; diff --git a/plugins/browser-plugin-ga-cookies/test/__snapshots__/ga-cookies.test.ts.snap b/plugins/browser-plugin-ga-cookies/test/__snapshots__/ga-cookies.test.ts.snap index fff2ed783..6ec8c8107 100644 --- a/plugins/browser-plugin-ga-cookies/test/__snapshots__/ga-cookies.test.ts.snap +++ b/plugins/browser-plugin-ga-cookies/test/__snapshots__/ga-cookies.test.ts.snap @@ -157,14 +157,15 @@ Array [ ] `; -exports[`GA Cookies plugin Returns values for Universal Analytics cookies by default 1`] = ` +exports[`GA Cookies plugin Returns values for GA4 cookies by default 1`] = ` Array [ Object { "data": Object { - "__utma": "567", "_ga": "1234", + "cookie_prefix": undefined, + "session_cookies": undefined, }, - "schema": "iglu:com.google.analytics/cookies/jsonschema/1-0-0", + "schema": "iglu:com.google.ga4/cookies/jsonschema/1-0-0", }, ] `; diff --git a/plugins/browser-plugin-ga-cookies/test/ga-cookies.test.ts b/plugins/browser-plugin-ga-cookies/test/ga-cookies.test.ts index 6e82c5ec3..168d30065 100644 --- a/plugins/browser-plugin-ga-cookies/test/ga-cookies.test.ts +++ b/plugins/browser-plugin-ga-cookies/test/ga-cookies.test.ts @@ -21,7 +21,7 @@ describe('GA Cookies plugin', () => { jest.clearAllMocks(); }); - it('Returns values for Universal Analytics cookies by default', (done) => { + it('Returns values for GA4 cookies by default', (done) => { const containerId = '1234'; document.cookie = `_ga=1234; __utma=567; _ga_${containerId}=567;`; const core = trackerCore({ @@ -42,7 +42,7 @@ describe('GA Cookies plugin', () => { const measurementId = `G-${containerId}`; document.cookie = ``; const core = trackerCore({ - corePlugins: [GaCookiesPlugin({ ga4: true, ga4MeasurementId: measurementId })], + corePlugins: [GaCookiesPlugin({ ua: true, ga4MeasurementId: measurementId })], callback: (payloadBuilder) => { const { data } = payloadBuilder.getJson()[1].json; expect(data).toMatchSnapshot(); @@ -58,7 +58,7 @@ describe('GA Cookies plugin', () => { const measurementId = `G-${containerId}`; document.cookie = `_ga=1234; __utma=567; _ga_${containerId}=567;`; const core = trackerCore({ - corePlugins: [GaCookiesPlugin({ ga4: true, ga4MeasurementId: measurementId })], + corePlugins: [GaCookiesPlugin({ ua: true, ga4MeasurementId: measurementId })], callback: (payloadBuilder) => { const { data } = payloadBuilder.getJson()[1].json; expect(data).toMatchSnapshot(); diff --git a/plugins/browser-plugin-link-click-tracking/README.md b/plugins/browser-plugin-link-click-tracking/README.md index 2de3feb81..39da9bff9 100644 --- a/plugins/browser-plugin-link-click-tracking/README.md +++ b/plugins/browser-plugin-link-click-tracking/README.md @@ -42,11 +42,18 @@ newTracker('sp1', '{{collector}}', { plugins: [ LinkClickTrackingPlugin() ] }); Then use the available functions from this package to track to all trackers which have been initialized with this plugin: ```js -import { enableLinkClickTracking, refreshLinkClickTracking } from '@snowplow/browser-plugin-link-click-tracking'; +import { enableLinkClickTracking } from '@snowplow/browser-plugin-link-click-tracking'; enableLinkClickTracking({ options: { ... }, psuedoClicks: true }); +``` + +You can also explicitly track a click without installing listeners: + +```js +import { trackLinkClick } from '@snowplow/browser-plugin-link-click-tracking'; -refreshLinkClickTracking(); +trackLinkClick({ element: document.querySelector("a, area") }); +trackLinkClick({ targetUrl: "http://example.com/" }); ``` ## Copyright and license diff --git a/plugins/browser-plugin-link-click-tracking/jest.config.js b/plugins/browser-plugin-link-click-tracking/jest.config.js index bd3ea4e2a..87d15da9b 100644 --- a/plugins/browser-plugin-link-click-tracking/jest.config.js +++ b/plugins/browser-plugin-link-click-tracking/jest.config.js @@ -1,5 +1,6 @@ module.exports = { preset: 'ts-jest', reporters: ['jest-standard-reporter'], + setupFilesAfterEnv: ['../../setupTestGlobals.ts'], testEnvironment: 'jest-environment-jsdom-global', }; diff --git a/plugins/browser-plugin-link-click-tracking/src/index.ts b/plugins/browser-plugin-link-click-tracking/src/index.ts index 0ed723d18..428752749 100644 --- a/plugins/browser-plugin-link-click-tracking/src/index.ts +++ b/plugins/browser-plugin-link-click-tracking/src/index.ts @@ -29,49 +29,66 @@ */ import { - getHostName, - getCssClasses, addEventListener, - getFilterByClass, - FilterCriterion, - BrowserPlugin, - BrowserTracker, dispatchToTrackersInCollection, + getCssClasses, + getFilterByClass, + getHostName, + flushPendingCookies, + type BrowserPlugin, + type BrowserTracker, + type FilterCriterion, } from '@snowplow/browser-tracker-core'; + import { - resolveDynamicContext, - DynamicContext, buildLinkClick, - CommonEventProperties, - LinkClickEvent, + resolveDynamicContext, + type CommonEventProperties, + type DynamicContext, + type LinkClickEvent, + type Logger, + type PayloadBuilder, } from '@snowplow/tracker-core'; interface LinkClickConfiguration { linkTrackingFilter?: (element: HTMLElement) => boolean; // Whether pseudo clicks are tracked - linkTrackingPseudoClicks?: boolean | null | undefined; + linkTrackingPseudoClicks?: boolean | null; // Whether to track the innerHTML of clicked links - linkTrackingContent?: boolean | null | undefined; + linkTrackingContent?: boolean | null; // The context attached to link click events - linkTrackingContext?: DynamicContext | null | undefined; - lastButton?: number | null; - lastTarget?: EventTarget | null; + linkTrackingContext?: DynamicContext | null; + lastButton?: number; + lastTarget?: EventTarget; } +type TrackableElement = HTMLAnchorElement | HTMLAreaElement; + +const TRACKABLE_ELEMENTS = ['a', 'area']; // should be kept in sync with TrackableElement type +const TRACKABLE_ELEMENTS_SELECTOR = TRACKABLE_ELEMENTS.join(', '); +const ELEMENT_PROTOCOL_FILTER = /^(javascript|vbscript|jscript|mocha|livescript|ecmascript):/i; + const _trackers: Record = {}; +const _listeners: Record = {}; const _configuration: Record = {}; +let _logger: Logger | undefined = undefined; /** - * Link click tracking + * Link click tracking. + * + * Will automatically track link clicks once enabled with `enableLinkClickTracking` + * or you can manually track link clicks with `trackLinkClick`. * - * Will automatically tracking link clicks once enabled with 'enableLinkClickTracking' - * or you can manually track link clicks with 'trackLinkClick' + * @returns Plugin instance */ export function LinkClickTrackingPlugin(): BrowserPlugin { return { activateBrowserPlugin: (tracker: BrowserTracker) => { _trackers[tracker.id] = tracker; }, + logger: (logger: Logger) => { + _logger = logger; + }, }; } @@ -83,15 +100,15 @@ export interface LinkClickTrackingConfiguration { * Captures middle click events in browsers that don't generate standard click * events for middle click actions */ - pseudoClicks?: boolean | null; + pseudoClicks?: boolean; /** Whether the content of the links should be tracked */ - trackContent?: boolean | null; - /** The dyanmic context which will be evaluated for each link click event */ + trackContent?: boolean; + /** The dynamic context which will be evaluated for each link click event */ context?: DynamicContext | null; } /** - * Enable link click tracking + * Enable link click tracking. * * @remarks * The default behaviour is to use actual click events. However, some browsers @@ -99,162 +116,192 @@ export interface LinkClickTrackingConfiguration { * * To capture more "clicks", the pseudo click-handler uses mousedown + mouseup events. * This is not industry standard and is vulnerable to false positives (e.g., drag events). + * + * @param configuration The link tracking configuration to use for the new click handlers + * @param trackers List of tracker IDs that should track the click events */ export function enableLinkClickTracking( configuration: LinkClickTrackingConfiguration = {}, trackers: Array = Object.keys(_trackers) ) { + // remove listeners in case pseudoclick support has been toggled, which may duplicate handlers + disableLinkClickTracking(trackers); trackers.forEach((id) => { if (_trackers[id]) { - if (_trackers[id].sharedState.hasLoaded) { - // the load event has already fired, add the click listeners now - configureLinkClickTracking(configuration, id); - addClickListeners(id); - } else { - // defer until page has loaded - _trackers[id].sharedState.registeredOnLoadHandlers.push(function () { - configureLinkClickTracking(configuration, id); - addClickListeners(id); - }); - } + configureLinkClickTracking(configuration, id); + addClickListeners(id); } }); } /** - * Add click event listeners to links which have been added to the page since the - * last time enableLinkClickTracking or refreshLinkClickTracking was used + * Disable link click tracking. * - * @param trackers - The tracker identifiers which the have their link click state refreshed + * Removes all document-level click event handlers installed by the plugin for + * provided tracker instances. + * + * @param trackers The tracker identifiers that will have their listeners removed */ -export function refreshLinkClickTracking(trackers: Array = Object.keys(_trackers)) { +export function disableLinkClickTracking(trackers: Array = Object.keys(_trackers)) { trackers.forEach((id) => { - if (_trackers[id]) { - if (_trackers[id].sharedState.hasLoaded) { - addClickListeners(id); - } else { - _trackers[id].sharedState.registeredOnLoadHandlers.push(function () { - addClickListeners(id); - }); - } + if (_trackers[id] && _listeners[id]) { + // remove all possible cases where the handler may have been attached + window.removeEventListener('click', _listeners[id], true); + window.removeEventListener('mouseup', _listeners[id], true); + window.removeEventListener('mousedown', _listeners[id], true); } }); } /** - * Manually log a click + * Add click event listeners to links which have been added to the page since the + * last time `enableLinkClickTracking` or `refreshLinkClickTracking` was called. + * + * @deprecated v4.0 moved to event delegation and this is no longer required + * @param trackers The tracker identifiers which the have their link click state refreshed + */ +export function refreshLinkClickTracking(_trackers: Array = []) { + _logger?.warn('refreshLinkClickTracking is deprecated in v4 and has no effect'); +} + +/** + * Manually log a click. * - * @param event - The event information - * @param trackers - The tracker identifiers which the event will be sent to + * @param event The event or element information + * @param trackers The tracker identifiers which the event will be sent to */ export function trackLinkClick( - event: LinkClickEvent & CommonEventProperties, + event: (LinkClickEvent | { element: TrackableElement; trackContent?: boolean }) & CommonEventProperties, trackers: Array = Object.keys(_trackers) ) { dispatchToTrackersInCollection(trackers, _trackers, (t) => { - t.core.track(buildLinkClick(event), event.context, event.timestamp); + let payload: PayloadBuilder | undefined; + if ('element' in event) { + const includeContent = event.trackContent ?? _configuration[t.id]?.linkTrackingContent ?? false; + payload = processClick(event.element, includeContent); + } else { + payload = buildLinkClick(event); + } + if (payload) t.core.track(payload, event.context, event.timestamp); }); + + flushPendingCookies(); } -/* - * Process clicks +/** + * Process a clicked element into a link_click event payload. + * + * In case the href of the element is empty, "about:invalid" is used as the target URL. + * + * @param sourceElement The trackable element to be used to build the payload + * @param includeContent Whether to include the element's contents in the payload + * @returns A link_click SDE payload for the given element, or nothing if the element shouldn't be tracked */ -function processClick(tracker: BrowserTracker, sourceElement: Element, context?: DynamicContext | null) { - let parentElement, tag, elementId, elementClasses, elementTarget, elementContent; - - while ( - (parentElement = sourceElement.parentElement) !== null && - parentElement != null && - (tag = sourceElement.tagName.toUpperCase()) !== 'A' && - tag !== 'AREA' - ) { - sourceElement = parentElement; - } +function processClick(sourceElement: TrackableElement, includeContent: boolean = false) { + let elementId, elementClasses, elementTarget, elementContent; - const anchorElement = sourceElement; - if (anchorElement.href != null) { - // browsers, such as Safari, don't downcase hostname and href - var originalSourceHostName = anchorElement.hostname || getHostName(anchorElement.href), - sourceHostName = originalSourceHostName.toLowerCase(), - sourceHref = anchorElement.href.replace(originalSourceHostName, sourceHostName), - scriptProtocol = new RegExp('^(javascript|vbscript|jscript|mocha|livescript|ecmascript):', 'i'); + const anchorElement = sourceElement; + // browsers, such as Safari, don't downcase hostname and href + const originalSourceHostName = anchorElement.hostname || getHostName(anchorElement.href); + const targetUrl = anchorElement.href.replace(originalSourceHostName, (s) => s.toLowerCase()); - // Ignore script pseudo-protocol links - if (!scriptProtocol.test(sourceHref)) { - elementId = anchorElement.id; - elementClasses = getCssClasses(anchorElement); - elementTarget = anchorElement.target; - elementContent = _configuration[tracker.id].linkTrackingContent ? anchorElement.innerHTML : undefined; + // Ignore script pseudo-protocol links + if (!ELEMENT_PROTOCOL_FILTER.test(targetUrl)) { + elementId = anchorElement.id; + elementClasses = getCssClasses(anchorElement); + elementTarget = anchorElement.target; + elementContent = includeContent ? anchorElement.innerHTML : undefined; - // decodeUrl %xx - sourceHref = unescape(sourceHref); - tracker.core.track( - buildLinkClick({ - targetUrl: sourceHref, - elementId, - elementClasses, - elementTarget, - elementContent, - }), - resolveDynamicContext(context, sourceElement) - ); + if (!targetUrl) { + _logger?.warn('Link click target URL empty', anchorElement); } + + // decodeUrl %xx + return buildLinkClick({ + targetUrl: targetUrl || 'about:invalid', + elementId, + elementClasses, + elementTarget, + elementContent, + }); } + + return; } -/* - * Return function to handle click event +/** + * Find the nearest trackable element to the given element; this is Element.closest() + * with a polyfill if required. + * + * @param target + * @returns An element that may be used to track a link click event, or null if `target` has no eligible parent */ -function getClickHandler(tracker: string, context?: DynamicContext | null): EventListenerOrEventListenerObject { - return function (evt: Event) { - var button, target; - - evt = evt || window.event; - button = (evt as MouseEvent).which || (evt as MouseEvent).button; - target = evt.target || evt.srcElement; +function findNearestEligibleElement(target: EventTarget | null): TrackableElement | null { + if (target instanceof Element) { + if (typeof target['closest'] === 'function') return target.closest(TRACKABLE_ELEMENTS_SELECTOR); + let sourceElement: Element | null = target; - // Using evt.type (added in IE4), we avoid defining separate handlers for mouseup and mousedown. - if (evt.type === 'click') { - if (target) { - processClick(_trackers[tracker], target as Element, context); - } - } else if (evt.type === 'mousedown') { - if ((button === 1 || button === 2) && target) { - _configuration[tracker].lastButton = button; - _configuration[tracker].lastTarget = target; - } else { - _configuration[tracker].lastButton = _configuration[tracker].lastTarget = null; - } - } else if (evt.type === 'mouseup') { - if (button === _configuration[tracker].lastButton && target === _configuration[tracker].lastTarget) { - processClick(_trackers[tracker], target as Element, context); - } - _configuration[tracker].lastButton = _configuration[tracker].lastTarget = null; + while (sourceElement) { + const tagName = sourceElement.tagName.toLowerCase(); + if (TRACKABLE_ELEMENTS.indexOf(tagName) !== -1) return sourceElement as TrackableElement; + sourceElement = sourceElement.parentElement; } - }; + } + + return null; } -/* - * Add click listener to a DOM element +/** + * Handle a (pseudo)click event; decide if the click is for a valid, unfiltered, + * element and track the click. + * + * @param tracker Tracker ID to generate the event for + * @param evt The DOM click event itself */ -function addClickListener(tracker: string, element: HTMLAnchorElement | HTMLAreaElement) { - if (_configuration[tracker].linkTrackingPseudoClicks) { - // for simplicity and performance, we ignore drag events - addEventListener(element, 'mouseup', getClickHandler(tracker, _configuration[tracker].linkTrackingContext), false); - addEventListener( - element, - 'mousedown', - getClickHandler(tracker, _configuration[tracker].linkTrackingContext), - false - ); - } else { - addEventListener(element, 'click', getClickHandler(tracker, _configuration[tracker].linkTrackingContext), false); +function clickHandler(tracker: string, evt: MouseEvent | undefined): void { + const context = _configuration[tracker].linkTrackingContext; + const filter = _configuration[tracker].linkTrackingFilter; + + const event = evt || (window.event as MouseEvent); + + const button = event.which || event.button; + + const clicked = event.composed ? event.composedPath()[0] : event.target || event.srcElement; + const target = findNearestEligibleElement(clicked); + + if (!target || target.href == null) return; + if (filter && !filter(target)) return; + + // Using evt.type (added in IE4), we avoid defining separate handlers for mouseup and mousedown. + if (event.type === 'click') { + trackLinkClick({ + element: target, + context: resolveDynamicContext(context, target), + }); + } else if (event.type === 'mousedown') { + if (button === 1 || button === 2) { + _configuration[tracker].lastButton = button; + _configuration[tracker].lastTarget = target; + } else { + delete _configuration[tracker].lastButton; + } + } else if (event.type === 'mouseup') { + if (button === _configuration[tracker].lastButton && target === _configuration[tracker].lastTarget) { + trackLinkClick({ + element: target, + context: resolveDynamicContext(context, target), + }); + } + delete _configuration[tracker].lastButton; + delete _configuration[tracker].lastTarget; } } -/* - * Configures link click tracking: how to filter which links will be tracked, - * whether to use pseudo click tracking, and what context to attach to link_click events +/** + * Update the link-tracking configuration for the given tracker ID. + * + * @param param0 The new link-tracking configuration + * @param tracker The tracker ID to update configuration for */ function configureLinkClickTracking( { options, pseudoClicks, trackContent, context }: LinkClickTrackingConfiguration = {}, @@ -268,18 +315,20 @@ function configureLinkClickTracking( }; } -/* - * Add click handlers to anchor and AREA elements, except those to be ignored +/** + * Add (psuedo)click handlers to the window for the given tracker ID. + * + * @param trackerId Tracker ID to install a listener for */ function addClickListeners(trackerId: string) { - var linkElements = document.links, - i; + // by re-using exact function references the browser will prevent dupes and allow removal + _listeners[trackerId] = _listeners[trackerId] || clickHandler.bind(null, trackerId); - for (i = 0; i < linkElements.length; i++) { - // Add a listener to link elements which pass the filter and aren't already tracked - if (_configuration[trackerId].linkTrackingFilter?.(linkElements[i]) && !(linkElements[i] as any)[trackerId]) { - addClickListener(trackerId, linkElements[i]); - (linkElements[i] as any)[trackerId] = true; - } + if (_configuration[trackerId].linkTrackingPseudoClicks) { + // for simplicity and performance, we ignore drag events + addEventListener(window, 'mouseup', _listeners[trackerId], true); + addEventListener(window, 'mousedown', _listeners[trackerId], true); + } else { + addEventListener(window, 'click', _listeners[trackerId], true); } } diff --git a/plugins/browser-plugin-link-click-tracking/test/events.test.ts b/plugins/browser-plugin-link-click-tracking/test/events.test.ts index 1685155c9..9e3b30e07 100644 --- a/plugins/browser-plugin-link-click-tracking/test/events.test.ts +++ b/plugins/browser-plugin-link-click-tracking/test/events.test.ts @@ -30,50 +30,329 @@ import { addTracker, SharedState } from '@snowplow/browser-tracker-core'; import F from 'lodash/fp'; -import { LinkClickTrackingPlugin, trackLinkClick } from '../src'; +import { LinkClickTrackingPlugin, disableLinkClickTracking, enableLinkClickTracking, trackLinkClick } from '../src'; +import { newInMemoryEventStore } from '@snowplow/tracker-core'; -const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('evt.e')))); -const extractEventProperties = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('evt.ue_pr'))); +const getUEEvents = F.compose(F.filter(F.compose(F.eq('ue'), F.get('e')))); +const extractEventProperties = F.map(F.compose(F.get('data'), (cx: string) => JSON.parse(cx), F.get('ue_pr'))); const extractUeEvent = (schema: string) => { return { - from: F.compose( - F.first, - F.filter(F.compose(F.eq(schema), F.get('schema'))), - F.flatten, - extractEventProperties, - getUEEvents - ), + from: (a: any, n: number = 0) => + F.nth( + n, + F.compose(F.filter(F.compose(F.eq(schema), F.get('schema'))), F.flatten, extractEventProperties, getUEEvents)(a) + ), }; }; -describe('AdTrackingPlugin', () => { - const state = new SharedState(); - addTracker('sp1', 'sp1', 'js-3.0.0', '', state, { +describe('LinkClickTrackingPlugin', () => { + const eventStore = newInMemoryEventStore({}); + addTracker('sp1', 'sp1', 'js-3.0.0', '', new SharedState(), { stateStorageStrategy: 'cookie', encodeBase64: false, plugins: [LinkClickTrackingPlugin()], + eventStore, + customFetch: async () => new Response(null, { status: 500 }), }); - it('trackLinkClick adds the expected link click event to the queue', () => { - trackLinkClick({ - targetUrl: 'https://www.example.com', - elementClasses: ['class-1', 'class-2'], - elementContent: 'content-1', - elementId: 'id-1234', - elementTarget: '_blank', - }); + const $addEventListener = jest.spyOn(window, 'addEventListener'); + const $removeEventListener = jest.spyOn(window, 'removeEventListener'); + + afterEach(async () => { + // clear the outQueue(s) after each test + await eventStore.removeHead(await eventStore.count()); + jest.clearAllMocks(); + }); - expect( - extractUeEvent('iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1').from(state.outQueues[0]) - ).toMatchObject({ - schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1', - data: { + describe('trackLinkClick', () => { + it('adds the specified link click event to the queue', async () => { + trackLinkClick({ targetUrl: 'https://www.example.com', elementClasses: ['class-1', 'class-2'], elementContent: 'content-1', elementId: 'id-1234', elementTarget: '_blank', - }, + }); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1', + data: { + targetUrl: 'https://www.example.com', + elementClasses: ['class-1', 'class-2'], + elementContent: 'content-1', + elementId: 'id-1234', + elementTarget: '_blank', + }, + }); + }); + + it('generates a link click event from a given element and adds it to the queue', async () => { + const a = Object.assign(document.createElement('a'), { + href: 'https://www.example.com/abc', + className: 'class-1 class-2', + textContent: 'content-1', + id: 'id-1234', + target: '_blank', + }); + + trackLinkClick({ element: a }); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1', + data: { + targetUrl: 'https://www.example.com/abc', + elementClasses: ['class-1', 'class-2'], + //elementContent: missing because disabled in default configuration + elementId: 'id-1234', + elementTarget: '_blank', + }, + }); + }); + + it('does nothing for no trackers', async () => { + trackLinkClick( + { + targetUrl: 'https://www.example.com', + elementClasses: ['class-1', 'class-2'], + elementContent: 'content-1', + elementId: 'id-1234', + elementTarget: '_blank', + }, + [] + ); + + expect(await eventStore.getAllPayloads()).toHaveLength(0); + }); + + it('does nothing for fake trackers', async () => { + trackLinkClick( + { + targetUrl: 'https://www.example.com', + elementClasses: ['class-1', 'class-2'], + elementContent: 'content-1', + elementId: 'id-1234', + elementTarget: '_blank', + }, + ['doesNotExist'] + ); + + expect(await eventStore.getAllPayloads()).toHaveLength(0); + }); + }); + + describe('enableLinkClickTracking', () => { + it('does nothing for no trackers', () => { + enableLinkClickTracking({}, []); + expect($addEventListener).not.toBeCalled(); + }); + + it('adds click listeners by default', () => { + enableLinkClickTracking(); + + expect($addEventListener).lastCalledWith('click', expect.anything(), true); + }); + + it('adds pseudo-click listeners when requested', () => { + enableLinkClickTracking({ pseudoClicks: true }); + expect($addEventListener).lastCalledWith('mousedown', expect.anything(), true); + }); + + it('tracks clicks on links that already exist', async () => { + const target = document.createElement('a'); + target.href = 'https://www.example.com/exists'; + document.body.appendChild(target); + + enableLinkClickTracking(); + + target.click(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1', + data: { + targetUrl: 'https://www.example.com/exists', + }, + }); + }); + + it('tracks clicks on links added after enabling', async () => { + enableLinkClickTracking(); + + const target = document.createElement('a'); + target.href = 'https://www.example.com/dynamic'; + document.body.appendChild(target); + + target.click(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1', + data: { + targetUrl: 'https://www.example.com/dynamic', + }, + }); + }); + + it('tracks clicks on links without href', async () => { + enableLinkClickTracking(); + + const target = document.createElement('a'); + document.body.appendChild(target); + + target.click(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1', + data: { + targetUrl: 'about:invalid', + }, + }); + }); + + it('tracks clicks on child elements of links and contents', async () => { + enableLinkClickTracking({ trackContent: true }); + + const parent = document.createElement('a'); + parent.href = 'https://www.example.com/parent'; + + const target = document.createElement('span'); + target.textContent = 'child'; + parent.appendChild(target); + + document.body.appendChild(parent); + + target.click(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1', + data: { + targetUrl: 'https://www.example.com/parent', + elementContent: 'child', + }, + }); + }); + + it('tracks clicks on links in custom components', async () => { + enableLinkClickTracking(); + + window.customElements.define( + 'shadow-link', + class extends HTMLElement { + connectedCallback() { + const a = document.createElement('a'); + a.textContent = 'Shadow'; + a.href = 'https://www.example.com/shadow'; + + const shadowRoot = this.attachShadow({ mode: 'open' }); + shadowRoot.appendChild(a); + } + } + ); + + const shadow = document.createElement('shadow-link'); + document.body.appendChild(shadow); + + shadow.shadowRoot!.querySelector('a')!.click(); + + expect( + extractUeEvent('iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1').from( + await eventStore.getAllPayloads() + ) + ).toMatchObject({ + schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1', + data: { + targetUrl: 'https://www.example.com/shadow', + }, + }); + }); + + it('doesnt double track clicks', async () => { + enableLinkClickTracking({ pseudoClicks: true }); + enableLinkClickTracking({ pseudoClicks: false }); + + const target = document.createElement('a'); + target.href = 'https://www.example.com/multiple'; + document.body.appendChild(target); + + expect(await eventStore.getAllPayloads()).toHaveLength(0); + + target.click(); + + expect(await eventStore.getAllPayloads()).toHaveLength(1); + }); + + it('ignores links that match denylist criteria', async () => { + enableLinkClickTracking({ options: { denylist: ['exclude'] } }); + + const target = document.createElement('a'); + target.href = 'https://www.example.com/exclude'; + target.className = 'exclude'; + document.body.appendChild(target); + + expect(await eventStore.getAllPayloads()).toHaveLength(0); + + target.click(); + + expect(await eventStore.getAllPayloads()).toHaveLength(0); + + target.className = 'include'; + target.click(); + + expect(await eventStore.getAllPayloads()).toHaveLength(1); + }); + + it('ignores links that dont match allowlist criteria', async () => { + enableLinkClickTracking({ options: { allowlist: ['include'] } }); + + const target = document.createElement('a'); + target.href = 'https://www.example.com/include'; + target.className = 'exclude'; + document.body.appendChild(target); + + expect(await eventStore.getAllPayloads()).toHaveLength(0); + + target.click(); + + expect(await eventStore.getAllPayloads()).toHaveLength(0); + + target.className = 'include'; + target.click(); + + expect(await eventStore.getAllPayloads()).toHaveLength(1); + }); + }); + + describe('disableLinkClickTracking', () => { + it('removes any listeners added', () => { + enableLinkClickTracking(); + disableLinkClickTracking(); + + const addCalls = $addEventListener.mock.calls; + + expect(addCalls).toHaveLength(1); + expect($removeEventListener.mock.calls).toContainEqual(addCalls[0]); }); }); }); diff --git a/plugins/browser-plugin-media-tracking/jest.config.js b/plugins/browser-plugin-media-tracking/jest.config.js index bd3ea4e2a..90f99fb5b 100644 --- a/plugins/browser-plugin-media-tracking/jest.config.js +++ b/plugins/browser-plugin-media-tracking/jest.config.js @@ -2,4 +2,5 @@ module.exports = { preset: 'ts-jest', reporters: ['jest-standard-reporter'], testEnvironment: 'jest-environment-jsdom-global', + setupFilesAfterEnv: ['../../setupTestGlobals.ts'], }; diff --git a/plugins/browser-plugin-media-tracking/package.json b/plugins/browser-plugin-media-tracking/package.json index 6e93cbd0a..ebf5e6136 100644 --- a/plugins/browser-plugin-media-tracking/package.json +++ b/plugins/browser-plugin-media-tracking/package.json @@ -21,9 +21,11 @@ "test": "jest" }, "dependencies": { + "@snowplow/browser-plugin-media": "workspace:*", "@snowplow/browser-tracker-core": "workspace:*", "@snowplow/tracker-core": "workspace:*", - "tslib": "^2.3.1" + "tslib": "^2.3.1", + "uuid": "^10.0.0" }, "devDependencies": { "@ampproject/rollup-plugin-closure-compiler": "~0.27.0", @@ -31,6 +33,7 @@ "@rollup/plugin-node-resolve": "~13.1.3", "@types/jest": "~27.4.1", "@types/jsdom": "~16.2.14", + "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "~5.15.0", "@typescript-eslint/parser": "~5.15.0", "eslint": "~8.11.0", diff --git a/plugins/browser-plugin-media-tracking/src/api.ts b/plugins/browser-plugin-media-tracking/src/api.ts new file mode 100644 index 000000000..622df6a72 --- /dev/null +++ b/plugins/browser-plugin-media-tracking/src/api.ts @@ -0,0 +1,82 @@ +import { Logger } from '@snowplow/tracker-core'; +import { BrowserPlugin, BrowserTracker } from '@snowplow/browser-tracker-core'; +import { waitForElement } from './findElem'; +import { Config, isElementConfig, isStringConfig } from './config'; +import { setUpListeners } from './player'; +import { setConfigDefaults } from './helperFunctions'; + +// These imports are used for documentation purposes only. +// Typescript complains that they are unused. +// @ts-ignore: TS6133 +import { DynamicContext } from '@snowplow/tracker-core'; +// @ts-ignore: TS6133 +import { HTML5MediaEventTypes } from './config'; +// @ts-ignore: TS6133 +import { FilterOutRepeatedEvents } from '@snowplow/browser-plugin-media/src/types'; + +import { endMediaTracking } from '@snowplow/browser-plugin-media'; + +let LOG: Logger; +const _trackers: Record = {}; + +export function MediaTrackingPlugin(): BrowserPlugin { + return { + activateBrowserPlugin: (tracker: BrowserTracker) => { + _trackers[tracker.id] = tracker; + }, + logger: (logger) => { + LOG = logger; + }, + }; +} + +/** + * Enables media tracking on an HTML `