coveo.analytics 2.30.56 → 2.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +37 -43
  2. package/dist/browser.mjs +513 -357
  3. package/dist/coveoua.browser.js +1 -1
  4. package/dist/coveoua.browser.js.map +1 -1
  5. package/dist/coveoua.debug.js +542 -359
  6. package/dist/coveoua.debug.js.map +1 -1
  7. package/dist/coveoua.js +1 -1
  8. package/dist/coveoua.js.map +1 -1
  9. package/dist/definitions/client/analytics.d.ts +3 -2
  10. package/dist/definitions/client/runtimeEnvironment.d.ts +1 -1
  11. package/dist/definitions/donottrack.d.ts +1 -0
  12. package/dist/definitions/react-native/index.d.ts +1 -2
  13. package/dist/definitions/version.d.ts +1 -1
  14. package/dist/library.cjs +15350 -3699
  15. package/dist/library.es.js +513 -357
  16. package/dist/library.js +15350 -3699
  17. package/dist/library.mjs +15350 -3699
  18. package/dist/react-native.es.js +634 -460
  19. package/modules/package.json +9 -9
  20. package/package.json +60 -91
  21. package/react-native/package.json +7 -7
  22. package/src/caseAssist/caseAssistActions.ts +51 -50
  23. package/src/caseAssist/caseAssistClient.spec.ts +328 -297
  24. package/src/caseAssist/caseAssistClient.ts +201 -185
  25. package/src/client/analytics.spec.ts +724 -703
  26. package/src/client/analytics.ts +677 -602
  27. package/src/client/analyticsBeaconClient.spec.ts +195 -188
  28. package/src/client/analyticsBeaconClient.ts +99 -88
  29. package/src/client/analyticsFetchClient.ts +77 -61
  30. package/src/client/analyticsRequestClient.ts +17 -17
  31. package/src/client/location.ts +3 -3
  32. package/src/client/measurementProtocolMapper.ts +54 -45
  33. package/src/client/measurementProtocolMapping/baseMeasurementProtocolMapper.ts +48 -44
  34. package/src/client/measurementProtocolMapping/commerceMeasurementProtocolMapper.ts +133 -121
  35. package/src/client/measurementProtocolMapping/serviceMeasurementProtocolMapper.ts +17 -17
  36. package/src/client/noopAnalytics.ts +80 -68
  37. package/src/client/runtimeEnvironment.ts +50 -41
  38. package/src/client/utils.spec.ts +112 -97
  39. package/src/client/utils.ts +39 -39
  40. package/src/cookieutils.spec.ts +59 -0
  41. package/src/cookieutils.ts +40 -39
  42. package/src/coveoua/browser.ts +14 -12
  43. package/src/coveoua/library.ts +4 -4
  44. package/src/coveoua/plugins.ts +36 -34
  45. package/src/coveoua/simpleanalytics.spec.ts +467 -452
  46. package/src/coveoua/simpleanalytics.ts +146 -140
  47. package/src/detector.ts +17 -17
  48. package/src/donottrack.spec.ts +199 -121
  49. package/src/donottrack.ts +31 -8
  50. package/src/events.ts +86 -79
  51. package/src/formatting/format-array-for-coveo-custom-data.spec.ts +13 -12
  52. package/src/formatting/format-array-for-coveo-custom-data.ts +18 -18
  53. package/src/formatting/format-omnibox-metadata.ts +16 -12
  54. package/src/history.spec.ts +197 -195
  55. package/src/history.ts +143 -133
  56. package/src/hook/addDefaultValues.ts +13 -8
  57. package/src/hook/enhanceViewEvent.ts +17 -17
  58. package/src/insight/insightClient.spec.ts +1848 -1717
  59. package/src/insight/insightClient.ts +866 -761
  60. package/src/insight/insightEvents.ts +57 -56
  61. package/src/plugins/BasePlugin.ts +125 -121
  62. package/src/plugins/ec.spec.ts +457 -429
  63. package/src/plugins/ec.ts +202 -199
  64. package/src/plugins/link.spec.ts +183 -159
  65. package/src/plugins/link.ts +88 -85
  66. package/src/plugins/svc.spec.ts +161 -155
  67. package/src/plugins/svc.ts +93 -91
  68. package/src/react-native/index.ts +8 -1
  69. package/src/react-native/react-native-runtime.ts +33 -33
  70. package/src/react-native/react-native-utils.ts +1 -1
  71. package/src/react-native/react-native.spec.ts +31 -21
  72. package/src/searchPage/searchPageClient.spec.ts +1944 -1794
  73. package/src/searchPage/searchPageClient.ts +1261 -1040
  74. package/src/searchPage/searchPageEvents.ts +524 -511
  75. package/src/storage.spec.ts +51 -51
  76. package/src/storage.ts +47 -47
  77. package/dist/definitions/bundle/browser-fetch.d.ts +0 -1
  78. package/dist/definitions/src/coveoua/browser.d.ts +0 -6
  79. package/dist/definitions/src/coveoua/headless.d.ts +0 -6
  80. package/dist/definitions/src/coveoua/library.d.ts +0 -17
  81. package/dist/definitions/src/coveoua/plugins.d.ts +0 -10
  82. package/dist/definitions/src/coveoua/simpleanalytics.d.ts +0 -33
  83. package/dist/definitions/src/react-native/index.d.ts +0 -3
  84. package/dist/definitions/src/react-native/react-native-runtime.d.ts +0 -19
@@ -1,24 +1,28 @@
1
1
  import {
2
- AnyEventResponse,
3
- ClickEventRequest,
4
- ClickEventResponse,
5
- CustomEventRequest,
6
- CustomEventResponse,
7
- EventType,
8
- HealthResponse,
9
- SearchEventRequest,
10
- SearchEventResponse,
11
- ViewEventRequest,
12
- ViewEventResponse,
13
- VisitResponse,
14
- IRequestPayload,
15
- VariableArgumentsPayload,
16
- PreparedClickEventRequest,
17
- PreparedCustomEventRequest,
18
- PreparedViewEventRequest,
19
- PreparedSearchEventRequest,
2
+ AnyEventResponse,
3
+ ClickEventRequest,
4
+ ClickEventResponse,
5
+ CustomEventRequest,
6
+ CustomEventResponse,
7
+ EventType,
8
+ HealthResponse,
9
+ SearchEventRequest,
10
+ SearchEventResponse,
11
+ ViewEventRequest,
12
+ ViewEventResponse,
13
+ VisitResponse,
14
+ IRequestPayload,
15
+ VariableArgumentsPayload,
16
+ PreparedClickEventRequest,
17
+ PreparedCustomEventRequest,
18
+ PreparedViewEventRequest,
19
+ PreparedSearchEventRequest,
20
20
  } from '../events';
21
- import {IAnalyticsClientOptions, PreprocessAnalyticsRequest, VisitorIdProvider} from './analyticsRequestClient';
21
+ import {
22
+ IAnalyticsClientOptions,
23
+ PreprocessAnalyticsRequest,
24
+ VisitorIdProvider,
25
+ } from './analyticsRequestClient';
22
26
  import {hasWindow, hasDocument} from '../detector';
23
27
  import {addDefaultValues} from '../hook/addDefaultValues';
24
28
  import {enhanceViewEvent} from '../hook/enhanceViewEvent';
@@ -26,107 +30,131 @@ import {v4 as uuidv4, v5 as uuidv5, validate as uuidValidate} from 'uuid';
26
30
  import {libVersion} from '../version';
27
31
  import {CoveoLinkParam} from '../plugins/link';
28
32
  import {
29
- convertKeysToMeasurementProtocol,
30
- isMeasurementProtocolKey,
31
- convertCustomMeasurementProtocolKeys,
33
+ convertKeysToMeasurementProtocol,
34
+ isMeasurementProtocolKey,
35
+ convertCustomMeasurementProtocolKeys,
32
36
  } from './measurementProtocolMapper';
33
- import {IRuntimeEnvironment, BrowserRuntime, NodeJSRuntime, NoopRuntime} from './runtimeEnvironment';
37
+ import {
38
+ IRuntimeEnvironment,
39
+ BrowserRuntime,
40
+ NodeJSRuntime,
41
+ NoopRuntime,
42
+ } from './runtimeEnvironment';
34
43
  import HistoryStore from '../history';
35
44
  import {isApiKey} from './token';
36
45
  import {isReactNative, ReactNativeRuntimeWarning} from '../react-native/react-native-utils';
37
- import {doNotTrack} from '../donottrack';
46
+ import {shouldDisableAnalyticsForPrivacy} from '../donottrack';
38
47
  import {isObject, truncateUrl} from './utils';
39
48
 
40
49
  export const Version = 'v15';
41
50
 
42
51
  export const Endpoints = {
43
- default: 'https://analytics.cloud.coveo.com/rest/ua',
44
- production: 'https://analytics.cloud.coveo.com/rest/ua',
45
- hipaa: 'https://analyticshipaa.cloud.coveo.com/rest/ua',
52
+ default: 'https://analytics.cloud.coveo.com/rest/ua',
53
+ production: 'https://analytics.cloud.coveo.com/rest/ua',
54
+ hipaa: 'https://analyticshipaa.cloud.coveo.com/rest/ua',
46
55
  };
47
56
 
48
57
  export interface ClientOptions {
49
- token?: string;
50
- endpoint: string;
51
- isCustomEndpoint?: boolean;
52
- version: string;
53
- runtimeEnvironment?: IRuntimeEnvironment;
54
- beforeSendHooks: AnalyticsClientSendEventHook[];
55
- afterSendHooks: AnalyticsClientSendEventHook[];
56
- preprocessRequest?: PreprocessAnalyticsRequest;
58
+ token?: string;
59
+ endpoint: string;
60
+ isCustomEndpoint?: boolean;
61
+ version: string;
62
+ runtimeEnvironment?: IRuntimeEnvironment;
63
+ beforeSendHooks: AnalyticsClientSendEventHook[];
64
+ afterSendHooks: AnalyticsClientSendEventHook[];
65
+ preprocessRequest?: PreprocessAnalyticsRequest;
66
+ /**
67
+ * When `true`, browser privacy signals (Do Not Track and Global Privacy Control)
68
+ * are not honored and analytics is sent regardless of them. Defaults to `false`,
69
+ * which preserves the privacy-friendly behavior of honoring these signals.
70
+ */
71
+ disableBrowserPrivacySignals?: boolean;
57
72
  }
58
73
 
59
- export type AnalyticsClientSendEventHook = <TResult>(eventType: string, payload: any) => TResult | Promise<TResult>;
74
+ export type AnalyticsClientSendEventHook = <TResult>(
75
+ eventType: string,
76
+ payload: any
77
+ ) => TResult | Promise<TResult>;
60
78
  export type EventTypeConfig = {
61
- newEventType: EventType;
62
- variableLengthArgumentsNames?: string[];
63
- addVisitorIdParameter?: boolean;
64
- addClientIdParameter?: boolean;
65
- usesMeasurementProtocol?: boolean;
79
+ newEventType: EventType;
80
+ variableLengthArgumentsNames?: string[];
81
+ addVisitorIdParameter?: boolean;
82
+ addClientIdParameter?: boolean;
83
+ usesMeasurementProtocol?: boolean;
66
84
  };
67
85
 
68
- export interface PreparedEvent<TPreparedRequest, TCompleteRequest, TResponse extends AnyEventResponse>
69
- extends BufferedRequest {
70
- log(remainingPayload: Omit<TCompleteRequest, keyof TPreparedRequest>): Promise<TResponse | void>;
86
+ export interface PreparedEvent<
87
+ TPreparedRequest,
88
+ TCompleteRequest,
89
+ TResponse extends AnyEventResponse,
90
+ > extends BufferedRequest {
91
+ log(remainingPayload: Omit<TCompleteRequest, keyof TPreparedRequest>): Promise<TResponse | void>;
71
92
  }
72
93
 
73
94
  export interface AnalyticsClient {
74
- getPayload(eventType: string, ...payload: VariableArgumentsPayload): Promise<any>;
75
- getParameters(eventType: string, ...payload: VariableArgumentsPayload): Promise<any>;
76
- makeEvent<TPreparedRequest, TCompleteRequest, TResponse extends AnyEventResponse>(
77
- eventType: string,
78
- ...payload: VariableArgumentsPayload
79
- ): Promise<PreparedEvent<TPreparedRequest, TCompleteRequest, TResponse>>;
80
- sendEvent(eventType: string, ...payload: VariableArgumentsPayload): Promise<AnyEventResponse | void>;
81
- makeSearchEvent(
82
- request: PreparedSearchEventRequest,
83
- ): Promise<PreparedEvent<PreparedSearchEventRequest, SearchEventRequest, SearchEventResponse>>;
84
- sendSearchEvent(request: SearchEventRequest): Promise<SearchEventResponse | void>;
85
- makeClickEvent(
86
- request: PreparedClickEventRequest,
87
- ): Promise<PreparedEvent<PreparedClickEventRequest, ClickEventRequest, ClickEventResponse>>;
88
- sendClickEvent(request: ClickEventRequest): Promise<ClickEventResponse | void>;
89
- makeCustomEvent(
90
- request: PreparedCustomEventRequest,
91
- ): Promise<PreparedEvent<PreparedCustomEventRequest, CustomEventRequest, CustomEventResponse>>;
92
- sendCustomEvent(request: CustomEventRequest): Promise<CustomEventResponse | void>;
93
- makeViewEvent(
94
- request: PreparedViewEventRequest,
95
- ): Promise<PreparedEvent<PreparedViewEventRequest, ViewEventRequest, ViewEventResponse>>;
96
- sendViewEvent(request: ViewEventRequest): Promise<ViewEventResponse | void>;
97
- getVisit(): Promise<VisitResponse>;
98
- getHealth(): Promise<HealthResponse>;
99
- registerBeforeSendEventHook(hook: AnalyticsClientSendEventHook): void;
100
- registerAfterSendEventHook(hook: AnalyticsClientSendEventHook): void;
101
- addEventTypeMapping(eventType: string, eventConfig: EventTypeConfig): void;
102
- runtime: IRuntimeEnvironment;
103
- version: string;
104
- /**
105
- * @deprecated
106
- */
107
- readonly currentVisitorId: string;
108
- getCurrentVisitorId?(): Promise<string>; // TODO: v3 make required
109
- setAcceptedLinkReferrers?(hosts: string[]): void;
95
+ getPayload(eventType: string, ...payload: VariableArgumentsPayload): Promise<any>;
96
+ getParameters(eventType: string, ...payload: VariableArgumentsPayload): Promise<any>;
97
+ makeEvent<TPreparedRequest, TCompleteRequest, TResponse extends AnyEventResponse>(
98
+ eventType: string,
99
+ ...payload: VariableArgumentsPayload
100
+ ): Promise<PreparedEvent<TPreparedRequest, TCompleteRequest, TResponse>>;
101
+ sendEvent(
102
+ eventType: string,
103
+ ...payload: VariableArgumentsPayload
104
+ ): Promise<AnyEventResponse | void>;
105
+ makeSearchEvent(
106
+ request: PreparedSearchEventRequest
107
+ ): Promise<PreparedEvent<PreparedSearchEventRequest, SearchEventRequest, SearchEventResponse>>;
108
+ sendSearchEvent(request: SearchEventRequest): Promise<SearchEventResponse | void>;
109
+ makeClickEvent(
110
+ request: PreparedClickEventRequest
111
+ ): Promise<PreparedEvent<PreparedClickEventRequest, ClickEventRequest, ClickEventResponse>>;
112
+ sendClickEvent(request: ClickEventRequest): Promise<ClickEventResponse | void>;
113
+ makeCustomEvent(
114
+ request: PreparedCustomEventRequest
115
+ ): Promise<PreparedEvent<PreparedCustomEventRequest, CustomEventRequest, CustomEventResponse>>;
116
+ sendCustomEvent(request: CustomEventRequest): Promise<CustomEventResponse | void>;
117
+ makeViewEvent(
118
+ request: PreparedViewEventRequest
119
+ ): Promise<PreparedEvent<PreparedViewEventRequest, ViewEventRequest, ViewEventResponse>>;
120
+ sendViewEvent(request: ViewEventRequest): Promise<ViewEventResponse | void>;
121
+ getVisit(): Promise<VisitResponse>;
122
+ getHealth(): Promise<HealthResponse>;
123
+ registerBeforeSendEventHook(hook: AnalyticsClientSendEventHook): void;
124
+ registerAfterSendEventHook(hook: AnalyticsClientSendEventHook): void;
125
+ addEventTypeMapping(eventType: string, eventConfig: EventTypeConfig): void;
126
+ runtime: IRuntimeEnvironment;
127
+ version: string;
128
+ /**
129
+ * @deprecated
130
+ */
131
+ readonly currentVisitorId: string;
132
+ getCurrentVisitorId?(): Promise<string>; // TODO: v3 make required
133
+ setAcceptedLinkReferrers?(hosts: string[]): void;
110
134
  }
111
135
 
112
136
  export interface BufferedRequest {
113
- eventType: EventType;
114
- payload: any;
137
+ eventType: EventType;
138
+ payload: any;
115
139
  }
116
140
 
117
141
  type ProcessPayloadStep = (currentPayload: any) => any;
118
142
  type AsyncProcessPayloadStep = (currentPayload: any) => Promise<any>;
119
143
 
120
- export function buildBaseUrl(endpoint = Endpoints.default, apiVersion = Version, isCustomEndpoint = false) {
121
- endpoint = endpoint.replace(/\/$/, ''); // Remove trailing slash in endpoint.
144
+ export function buildBaseUrl(
145
+ endpoint = Endpoints.default,
146
+ apiVersion = Version,
147
+ isCustomEndpoint = false
148
+ ) {
149
+ endpoint = endpoint.replace(/\/$/, ''); // Remove trailing slash in endpoint.
122
150
 
123
- if (isCustomEndpoint) {
124
- return `${endpoint}/${apiVersion}`;
125
- }
151
+ if (isCustomEndpoint) {
152
+ return `${endpoint}/${apiVersion}`;
153
+ }
126
154
 
127
- const hasUARestEndpoint = endpoint.endsWith('/rest') || endpoint.endsWith('/rest/ua');
155
+ const hasUARestEndpoint = endpoint.endsWith('/rest') || endpoint.endsWith('/rest/ua');
128
156
 
129
- return `${endpoint}${hasUARestEndpoint ? '' : '/rest'}/${apiVersion}`;
157
+ return `${endpoint}${hasUARestEndpoint ? '' : '/rest'}/${apiVersion}`;
130
158
  }
131
159
 
132
160
  // Note: Changing this value will destroy the mapping from tracking string to clientId for all customers
@@ -135,528 +163,575 @@ export function buildBaseUrl(endpoint = Endpoints.default, apiVersion = Version,
135
163
  const COVEO_NAMESPACE = '38824e1f-37f5-42d3-8372-a4b8fa9df946';
136
164
 
137
165
  export class CoveoAnalyticsClient implements AnalyticsClient, VisitorIdProvider {
138
- private get defaultOptions(): ClientOptions {
139
- return {
140
- endpoint: Endpoints.default,
141
- isCustomEndpoint: false,
142
- token: '',
143
- version: Version,
144
- beforeSendHooks: [],
145
- afterSendHooks: [],
146
- };
147
- }
148
-
149
- public runtime: IRuntimeEnvironment;
150
- public get version(): string {
151
- return libVersion;
152
- }
153
- private visitorId: string;
154
- private bufferedRequests: BufferedRequest[];
155
- private beforeSendHooks: AnalyticsClientSendEventHook[];
156
- private afterSendHooks: AnalyticsClientSendEventHook[];
157
- private eventTypeMapping: {[name: string]: EventTypeConfig};
158
- private options: ClientOptions;
159
- private acceptedLinkReferrers: string[] = [];
160
-
161
- constructor(opts: Partial<ClientOptions>) {
162
- if (!opts) {
163
- throw new Error('You have to pass options to this constructor');
164
- }
165
-
166
- this.options = {
167
- ...this.defaultOptions,
168
- ...opts,
169
- };
170
-
171
- this.visitorId = '';
166
+ private get defaultOptions(): ClientOptions {
167
+ return {
168
+ endpoint: Endpoints.default,
169
+ isCustomEndpoint: false,
170
+ token: '',
171
+ version: Version,
172
+ beforeSendHooks: [],
173
+ afterSendHooks: [],
174
+ };
175
+ }
176
+
177
+ public runtime: IRuntimeEnvironment;
178
+ public get version(): string {
179
+ return libVersion;
180
+ }
181
+ private visitorId: string;
182
+ private bufferedRequests: BufferedRequest[];
183
+ private beforeSendHooks: AnalyticsClientSendEventHook[];
184
+ private afterSendHooks: AnalyticsClientSendEventHook[];
185
+ private eventTypeMapping: {[name: string]: EventTypeConfig};
186
+ private options: ClientOptions;
187
+ private acceptedLinkReferrers: string[] = [];
188
+
189
+ constructor(opts: Partial<ClientOptions>) {
190
+ if (!opts) {
191
+ throw new Error('You have to pass options to this constructor');
192
+ }
193
+
194
+ this.options = {
195
+ ...this.defaultOptions,
196
+ ...opts,
197
+ };
198
+
199
+ this.visitorId = '';
200
+ this.bufferedRequests = [];
201
+ this.beforeSendHooks = [enhanceViewEvent, addDefaultValues].concat(
202
+ this.options.beforeSendHooks
203
+ );
204
+ this.afterSendHooks = this.options.afterSendHooks;
205
+ this.eventTypeMapping = {};
206
+
207
+ const clientsOptions: IAnalyticsClientOptions = {
208
+ baseUrl: this.baseUrl,
209
+ token: this.options.token,
210
+ visitorIdProvider: this,
211
+ preprocessRequest: this.options.preprocessRequest,
212
+ };
213
+
214
+ if (shouldDisableAnalyticsForPrivacy(this.options.disableBrowserPrivacySignals)) {
215
+ this.runtime = new NoopRuntime();
216
+ } else {
217
+ this.runtime = this.options.runtimeEnvironment || this.initRuntime(clientsOptions);
218
+ }
219
+
220
+ this.addEventTypeMapping(EventType.view, {
221
+ newEventType: EventType.view,
222
+ addClientIdParameter: true,
223
+ });
224
+ this.addEventTypeMapping(EventType.click, {
225
+ newEventType: EventType.click,
226
+ addClientIdParameter: true,
227
+ });
228
+ this.addEventTypeMapping(EventType.custom, {
229
+ newEventType: EventType.custom,
230
+ addClientIdParameter: true,
231
+ });
232
+ this.addEventTypeMapping(EventType.search, {
233
+ newEventType: EventType.search,
234
+ addClientIdParameter: true,
235
+ });
236
+ }
237
+
238
+ private initRuntime(clientsOptions: IAnalyticsClientOptions) {
239
+ if (hasWindow() && hasDocument()) {
240
+ return new BrowserRuntime(clientsOptions, () => {
241
+ const copy = [...this.bufferedRequests];
172
242
  this.bufferedRequests = [];
173
- this.beforeSendHooks = [enhanceViewEvent, addDefaultValues].concat(this.options.beforeSendHooks);
174
- this.afterSendHooks = this.options.afterSendHooks;
175
- this.eventTypeMapping = {};
176
-
177
- const clientsOptions: IAnalyticsClientOptions = {
178
- baseUrl: this.baseUrl,
179
- token: this.options.token,
180
- visitorIdProvider: this,
181
- preprocessRequest: this.options.preprocessRequest,
182
- };
183
-
184
- if (doNotTrack()) {
185
- this.runtime = new NoopRuntime();
186
- } else {
187
- this.runtime = this.options.runtimeEnvironment || this.initRuntime(clientsOptions);
188
- }
189
-
190
- this.addEventTypeMapping(EventType.view, {newEventType: EventType.view, addClientIdParameter: true});
191
- this.addEventTypeMapping(EventType.click, {newEventType: EventType.click, addClientIdParameter: true});
192
- this.addEventTypeMapping(EventType.custom, {newEventType: EventType.custom, addClientIdParameter: true});
193
- this.addEventTypeMapping(EventType.search, {newEventType: EventType.search, addClientIdParameter: true});
194
- }
195
-
196
- private initRuntime(clientsOptions: IAnalyticsClientOptions) {
197
- if (hasWindow() && hasDocument()) {
198
- return new BrowserRuntime(clientsOptions, () => {
199
- const copy = [...this.bufferedRequests];
200
- this.bufferedRequests = [];
201
- return copy;
202
- });
203
- } else if (isReactNative()) {
204
- console.warn(ReactNativeRuntimeWarning);
205
- }
206
- return new NodeJSRuntime(clientsOptions);
207
- }
208
-
209
- private get storage() {
210
- return this.runtime.storage;
211
- }
212
-
213
- private async determineVisitorId() {
214
- try {
215
- return (
216
- (hasWindow() && this.extractClientIdFromLink(window.location.href)) ||
217
- (await this.storage.getItem('visitorId')) ||
218
- uuidv4()
219
- );
220
- } catch (err) {
221
- console.log(
222
- 'Could not get visitor ID from the current runtime environment storage. Using a random ID instead.',
223
- err,
224
- );
225
- return uuidv4();
226
- }
227
- }
228
-
229
- async getCurrentVisitorId() {
230
- if (!this.visitorId) {
231
- const id = await this.determineVisitorId();
232
- await this.setCurrentVisitorId(id);
233
- }
234
- return this.visitorId;
235
- }
236
-
237
- async setCurrentVisitorId(visitorId: string) {
238
- this.visitorId = visitorId;
239
- await this.storage.setItem('visitorId', visitorId);
240
- }
241
-
242
- async setClientId(value: string, namespace?: string) {
243
- if (uuidValidate(value)) {
244
- this.setCurrentVisitorId(value.toLowerCase());
245
- } else {
246
- if (!namespace) {
247
- throw Error('Cannot generate uuid client id without a specific namespace string.');
248
- }
249
- this.setCurrentVisitorId(uuidv5(value, uuidv5(namespace, COVEO_NAMESPACE)));
250
- }
251
- }
252
-
253
- async getParameters(eventType: EventType | string, ...payload: VariableArgumentsPayload) {
254
- return await this.resolveParameters(eventType, ...payload);
255
- }
256
-
257
- async getPayload(eventType: EventType | string, ...payload: VariableArgumentsPayload) {
258
- const parametersToSend = await this.resolveParameters(eventType, ...payload);
259
- return await this.resolvePayloadForParameters(eventType, parametersToSend);
260
- }
261
-
262
- /**
263
- * @deprecated Synchronous method is deprecated, use getCurrentVisitorId instead. This method will NOT work with react-native.
264
- */
265
- get currentVisitorId() {
266
- const visitorId = this.visitorId || this.storage.getItem('visitorId');
267
- if (typeof visitorId !== 'string') {
268
- this.setCurrentVisitorId(uuidv4());
269
- }
270
- return this.visitorId;
271
- }
272
-
273
- /**
274
- * @deprecated Synchronous method is deprecated, use setCurrentVisitorId instead. This method will NOT work with react-native.
275
- */
276
- set currentVisitorId(visitorId: string) {
277
- this.visitorId = visitorId;
278
- this.storage.setItem('visitorId', visitorId);
279
- }
280
-
281
- private extractClientIdFromLink(urlString: string): string | null {
282
- if (doNotTrack()) {
283
- return null;
284
- }
285
- try {
286
- const linkParam: string | null = new URL(urlString).searchParams.get(CoveoLinkParam.cvo_cid);
287
- if (linkParam == null) {
288
- return null;
289
- }
290
- const linker: CoveoLinkParam | null = CoveoLinkParam.fromString(linkParam);
291
- if (!linker || !hasDocument() || !linker.validate(document.referrer, this.acceptedLinkReferrers)) {
292
- return null;
293
- }
294
- return linker.clientId;
295
- } catch (error) {
296
- // Ignore any parsing errors
297
- }
243
+ return copy;
244
+ });
245
+ } else if (isReactNative()) {
246
+ console.warn(ReactNativeRuntimeWarning);
247
+ }
248
+ return new NodeJSRuntime(clientsOptions);
249
+ }
250
+
251
+ private get storage() {
252
+ return this.runtime.storage;
253
+ }
254
+
255
+ private async determineVisitorId() {
256
+ try {
257
+ return (
258
+ (hasWindow() && this.extractClientIdFromLink(window.location.href)) ||
259
+ (await this.storage.getItem('visitorId')) ||
260
+ uuidv4()
261
+ );
262
+ } catch (err) {
263
+ console.log(
264
+ 'Could not get visitor ID from the current runtime environment storage. Using a random ID instead.',
265
+ err
266
+ );
267
+ return uuidv4();
268
+ }
269
+ }
270
+
271
+ async getCurrentVisitorId() {
272
+ if (!this.visitorId) {
273
+ const id = await this.determineVisitorId();
274
+ await this.setCurrentVisitorId(id);
275
+ }
276
+ return this.visitorId;
277
+ }
278
+
279
+ async setCurrentVisitorId(visitorId: string) {
280
+ this.visitorId = visitorId;
281
+ await this.storage.setItem('visitorId', visitorId);
282
+ }
283
+
284
+ async setClientId(value: string, namespace?: string) {
285
+ if (uuidValidate(value)) {
286
+ this.setCurrentVisitorId(value.toLowerCase());
287
+ } else {
288
+ if (!namespace) {
289
+ throw Error('Cannot generate uuid client id without a specific namespace string.');
290
+ }
291
+ this.setCurrentVisitorId(uuidv5(value, uuidv5(namespace, COVEO_NAMESPACE)));
292
+ }
293
+ }
294
+
295
+ async getParameters(
296
+ eventType: EventType | string,
297
+ ...payload: VariableArgumentsPayload
298
+ ): Promise<VariableArgumentsPayload> {
299
+ return await this.resolveParameters(eventType, ...payload);
300
+ }
301
+
302
+ async getPayload(eventType: EventType | string, ...payload: VariableArgumentsPayload) {
303
+ const parametersToSend = await this.resolveParameters(eventType, ...payload);
304
+ return await this.resolvePayloadForParameters(eventType, parametersToSend);
305
+ }
306
+
307
+ /**
308
+ * @deprecated Synchronous method is deprecated, use getCurrentVisitorId instead. This method will NOT work with react-native.
309
+ */
310
+ get currentVisitorId() {
311
+ const visitorId = this.visitorId || this.storage.getItem('visitorId');
312
+ if (typeof visitorId !== 'string') {
313
+ this.setCurrentVisitorId(uuidv4());
314
+ }
315
+ return this.visitorId;
316
+ }
317
+
318
+ /**
319
+ * @deprecated Synchronous method is deprecated, use setCurrentVisitorId instead. This method will NOT work with react-native.
320
+ */
321
+ set currentVisitorId(visitorId: string) {
322
+ this.visitorId = visitorId;
323
+ this.storage.setItem('visitorId', visitorId);
324
+ }
325
+
326
+ private extractClientIdFromLink(urlString: string): string | null {
327
+ if (shouldDisableAnalyticsForPrivacy(this.options.disableBrowserPrivacySignals)) {
328
+ return null;
329
+ }
330
+ try {
331
+ const linkParam: string | null = new URL(urlString).searchParams.get(CoveoLinkParam.cvo_cid);
332
+ if (linkParam == null) {
298
333
  return null;
299
- }
300
-
301
- async resolveParameters(eventType: EventType | string, ...payload: VariableArgumentsPayload) {
302
- const {
303
- variableLengthArgumentsNames = [],
304
- addVisitorIdParameter = false,
305
- usesMeasurementProtocol = false,
306
- addClientIdParameter = false,
307
- } = this.eventTypeMapping[eventType] || {};
308
-
309
- const processVariableArgumentNamesStep: ProcessPayloadStep = (currentPayload) =>
310
- variableLengthArgumentsNames.length > 0
311
- ? this.parseVariableArgumentsPayload(variableLengthArgumentsNames, currentPayload)
312
- : currentPayload[0];
313
- const addVisitorIdStep: AsyncProcessPayloadStep = async (currentPayload) => ({
314
- ...currentPayload,
315
- visitorId: addVisitorIdParameter ? await this.getCurrentVisitorId() : '',
316
- });
317
- const addClientIdStep: AsyncProcessPayloadStep = async (currentPayload) => {
318
- if (addClientIdParameter) {
319
- return {
320
- ...currentPayload,
321
- clientId: await this.getCurrentVisitorId(),
322
- };
323
- }
324
- return currentPayload;
325
- };
326
- const setAnonymousUserStep: ProcessPayloadStep = (currentPayload) =>
327
- usesMeasurementProtocol ? this.ensureAnonymousUserWhenUsingApiKey(currentPayload) : currentPayload;
328
- const processBeforeSendHooksStep: AsyncProcessPayloadStep = (currentPayload) =>
329
- this.beforeSendHooks.reduce(async (promisePayload, current) => {
330
- const payload = await promisePayload;
331
- return await current(eventType, payload);
332
- }, currentPayload);
333
-
334
- const parametersToSend = await [
335
- processVariableArgumentNamesStep,
336
- addVisitorIdStep,
337
- addClientIdStep,
338
- setAnonymousUserStep,
339
- processBeforeSendHooksStep,
340
- ].reduce(async (payloadPromise, step) => {
341
- const payload = await payloadPromise;
342
- return await step(payload);
343
- }, Promise.resolve(payload));
344
-
345
- return parametersToSend;
346
- }
347
-
348
- async resolvePayloadForParameters(eventType: EventType | string, parameters: any) {
349
- const {usesMeasurementProtocol = false} = this.eventTypeMapping[eventType] || {};
350
-
351
- const addTrackingIdStep: ProcessPayloadStep = (currentPayload) =>
352
- this.setTrackingIdIfTrackingIdNotPresent(currentPayload);
353
- const cleanPayloadStep: ProcessPayloadStep = (currentPayload) =>
354
- this.removeEmptyPayloadValues(currentPayload, eventType);
355
- const validateParams: ProcessPayloadStep = (currentPayload) => this.validateParams(currentPayload, eventType);
356
- const processMeasurementProtocolConversionStep: ProcessPayloadStep = (currentPayload) =>
357
- usesMeasurementProtocol ? convertKeysToMeasurementProtocol(currentPayload) : currentPayload;
358
- const removeUnknownParameters: ProcessPayloadStep = (currentPayload) =>
359
- usesMeasurementProtocol ? this.removeUnknownParameters(currentPayload) : currentPayload;
360
- const processCustomParameters: ProcessPayloadStep = (currentPayload) =>
361
- usesMeasurementProtocol
362
- ? this.processCustomParameters(currentPayload)
363
- : this.mapCustomParametersToCustomData(currentPayload);
364
-
365
- const payloadToSend = await [
366
- addTrackingIdStep,
367
- cleanPayloadStep,
368
- validateParams,
369
- processMeasurementProtocolConversionStep,
370
- removeUnknownParameters,
371
- processCustomParameters,
372
- ].reduce(async (payloadPromise, step) => {
373
- const payload = await payloadPromise;
374
- return await step(payload);
375
- }, Promise.resolve(parameters));
376
-
377
- return payloadToSend;
378
- }
379
-
380
- async makeEvent<TPreparedRequest, TCompleteRequest, TResponse extends AnyEventResponse>(
381
- eventType: EventType | string,
382
- ...payload: VariableArgumentsPayload
383
- ): Promise<PreparedEvent<TPreparedRequest, TCompleteRequest, TResponse>> {
384
- const {newEventType: eventTypeToSend = eventType as EventType} = this.eventTypeMapping[eventType] || {};
385
-
386
- const parametersToSend = await this.resolveParameters(eventType, ...payload);
387
- const payloadToSend = await this.resolvePayloadForParameters(eventType, parametersToSend);
334
+ }
335
+ const linker: CoveoLinkParam | null = CoveoLinkParam.fromString(linkParam);
336
+ if (
337
+ !linker ||
338
+ !hasDocument() ||
339
+ !linker.validate(document.referrer, this.acceptedLinkReferrers)
340
+ ) {
341
+ return null;
342
+ }
343
+ return linker.clientId;
344
+ } catch (error) {
345
+ // Ignore any parsing errors
346
+ }
347
+ return null;
348
+ }
349
+
350
+ async resolveParameters(
351
+ eventType: EventType | string,
352
+ ...payload: VariableArgumentsPayload
353
+ ): Promise<VariableArgumentsPayload> {
354
+ const {
355
+ variableLengthArgumentsNames = [],
356
+ addVisitorIdParameter = false,
357
+ usesMeasurementProtocol = false,
358
+ addClientIdParameter = false,
359
+ } = this.eventTypeMapping[eventType] || {};
360
+
361
+ const processVariableArgumentNamesStep: ProcessPayloadStep = (currentPayload) =>
362
+ variableLengthArgumentsNames.length > 0
363
+ ? this.parseVariableArgumentsPayload(variableLengthArgumentsNames, currentPayload)
364
+ : currentPayload[0];
365
+ const addVisitorIdStep: AsyncProcessPayloadStep = async (currentPayload) => ({
366
+ ...currentPayload,
367
+ visitorId: addVisitorIdParameter ? await this.getCurrentVisitorId() : '',
368
+ });
369
+ const addClientIdStep: AsyncProcessPayloadStep = async (currentPayload) => {
370
+ if (addClientIdParameter) {
388
371
  return {
389
- eventType: eventTypeToSend,
390
- payload: payloadToSend,
391
- log: async (remainingPayload) => {
392
- this.bufferedRequests.push(<BufferedRequest>{
393
- eventType: eventTypeToSend,
394
- payload: {...payloadToSend, ...remainingPayload},
395
- });
396
- await Promise.all(
397
- this.afterSendHooks.map((hook) => hook(eventType, {...parametersToSend, ...remainingPayload})),
398
- );
399
- await this.deferExecution();
400
- return (await this.sendFromBuffer()) as TResponse | void;
401
- },
372
+ ...currentPayload,
373
+ clientId: await this.getCurrentVisitorId(),
402
374
  };
403
- }
404
-
405
- async sendEvent(eventType: EventType | string, ...payload: VariableArgumentsPayload) {
406
- return (await this.makeEvent<any, any, AnyEventResponse>(eventType, ...payload)).log({});
407
- }
408
-
409
- private deferExecution(): Promise<void> {
410
- return new Promise((resolve) => setTimeout(resolve, 0));
411
- }
412
-
413
- private async sendFromBuffer(): Promise<AnyEventResponse | void> {
414
- const popped = this.bufferedRequests.shift();
415
- if (popped) {
416
- const {eventType, payload} = popped;
417
- return this.runtime.getClientDependingOnEventType(eventType).sendEvent(eventType, payload);
418
- }
419
- }
420
-
421
- public clear() {
422
- this.storage.removeItem('visitorId');
423
- const store = new HistoryStore();
424
- store.clear();
425
- }
426
-
427
- public deleteHttpOnlyVisitorId() {
428
- this.runtime.client.deleteHttpCookieVisitorId();
429
- }
430
-
431
- async makeSearchEvent(request: PreparedSearchEventRequest) {
432
- return this.makeEvent<PreparedSearchEventRequest, SearchEventRequest, SearchEventResponse>(
433
- EventType.search,
434
- request,
435
- );
436
- }
437
-
438
- async sendSearchEvent({searchQueryUid, ...preparedRequest}: SearchEventRequest) {
439
- return (await this.makeSearchEvent(preparedRequest)).log({searchQueryUid});
440
- }
441
-
442
- async makeClickEvent(request: PreparedClickEventRequest) {
443
- return this.makeEvent<PreparedClickEventRequest, ClickEventRequest, ClickEventResponse>(
444
- EventType.click,
445
- request,
446
- );
447
- }
448
-
449
- async sendClickEvent({searchQueryUid, ...preparedRequest}: ClickEventRequest) {
450
- return (await this.makeClickEvent(preparedRequest)).log({searchQueryUid});
451
- }
452
-
453
- async makeCustomEvent(request: PreparedCustomEventRequest) {
454
- return this.makeEvent<PreparedCustomEventRequest, CustomEventRequest, CustomEventResponse>(
455
- EventType.custom,
456
- request,
375
+ }
376
+ return currentPayload;
377
+ };
378
+ const setAnonymousUserStep: ProcessPayloadStep = (currentPayload) =>
379
+ usesMeasurementProtocol
380
+ ? this.ensureAnonymousUserWhenUsingApiKey(currentPayload)
381
+ : currentPayload;
382
+ const processBeforeSendHooksStep: AsyncProcessPayloadStep = (currentPayload) =>
383
+ this.beforeSendHooks.reduce(async (promisePayload, current) => {
384
+ const payload = await promisePayload;
385
+ return await current(eventType, payload);
386
+ }, currentPayload);
387
+
388
+ const parametersToSend = await [
389
+ processVariableArgumentNamesStep,
390
+ addVisitorIdStep,
391
+ addClientIdStep,
392
+ setAnonymousUserStep,
393
+ processBeforeSendHooksStep,
394
+ ].reduce(async (payloadPromise, step) => {
395
+ const payload = await payloadPromise;
396
+ return await step(payload);
397
+ }, Promise.resolve(payload));
398
+
399
+ return parametersToSend;
400
+ }
401
+
402
+ async resolvePayloadForParameters(eventType: EventType | string, parameters: any) {
403
+ const {usesMeasurementProtocol = false} = this.eventTypeMapping[eventType] || {};
404
+
405
+ const addTrackingIdStep: ProcessPayloadStep = (currentPayload) =>
406
+ this.setTrackingIdIfTrackingIdNotPresent(currentPayload);
407
+ const cleanPayloadStep: ProcessPayloadStep = (currentPayload) =>
408
+ this.removeEmptyPayloadValues(currentPayload, eventType);
409
+ const validateParams: ProcessPayloadStep = (currentPayload) =>
410
+ this.validateParams(currentPayload, eventType);
411
+ const processMeasurementProtocolConversionStep: ProcessPayloadStep = (currentPayload) =>
412
+ usesMeasurementProtocol ? convertKeysToMeasurementProtocol(currentPayload) : currentPayload;
413
+ const removeUnknownParameters: ProcessPayloadStep = (currentPayload) =>
414
+ usesMeasurementProtocol ? this.removeUnknownParameters(currentPayload) : currentPayload;
415
+ const processCustomParameters: ProcessPayloadStep = (currentPayload) =>
416
+ usesMeasurementProtocol
417
+ ? this.processCustomParameters(currentPayload)
418
+ : this.mapCustomParametersToCustomData(currentPayload);
419
+
420
+ const payloadToSend = await [
421
+ addTrackingIdStep,
422
+ cleanPayloadStep,
423
+ validateParams,
424
+ processMeasurementProtocolConversionStep,
425
+ removeUnknownParameters,
426
+ processCustomParameters,
427
+ ].reduce(async (payloadPromise, step) => {
428
+ const payload = await payloadPromise;
429
+ return await step(payload);
430
+ }, Promise.resolve(parameters));
431
+
432
+ return payloadToSend;
433
+ }
434
+
435
+ async makeEvent<TPreparedRequest, TCompleteRequest, TResponse extends AnyEventResponse>(
436
+ eventType: EventType | string,
437
+ ...payload: VariableArgumentsPayload
438
+ ): Promise<PreparedEvent<TPreparedRequest, TCompleteRequest, TResponse>> {
439
+ const {newEventType: eventTypeToSend = eventType as EventType} =
440
+ this.eventTypeMapping[eventType] || {};
441
+
442
+ const parametersToSend = await this.resolveParameters(eventType, ...payload);
443
+ const payloadToSend = await this.resolvePayloadForParameters(eventType, parametersToSend);
444
+ return {
445
+ eventType: eventTypeToSend,
446
+ payload: payloadToSend,
447
+ log: async (remainingPayload) => {
448
+ this.bufferedRequests.push(<BufferedRequest>{
449
+ eventType: eventTypeToSend,
450
+ payload: {...payloadToSend, ...remainingPayload},
451
+ });
452
+ await Promise.all(
453
+ this.afterSendHooks.map((hook) =>
454
+ hook(eventType, {...parametersToSend, ...remainingPayload})
455
+ )
457
456
  );
458
- }
459
-
460
- async sendCustomEvent({lastSearchQueryUid, ...preparedRequest}: CustomEventRequest) {
461
- return (await this.makeCustomEvent(preparedRequest)).log({lastSearchQueryUid});
462
- }
463
-
464
- async makeViewEvent(request: PreparedViewEventRequest) {
465
- return this.makeEvent<PreparedViewEventRequest, ViewEventRequest, ViewEventResponse>(EventType.view, request);
466
- }
467
-
468
- async sendViewEvent(request: ViewEventRequest): Promise<ViewEventResponse | void> {
469
- return (await this.makeViewEvent(request)).log({});
470
- }
471
-
472
- async getVisit(): Promise<VisitResponse> {
473
- // deepcode ignore Ssrf: url is supplied by script owner
474
- const response = await fetch(`${this.baseUrl}/analytics/visit`);
475
- const visit = (await response.json()) as VisitResponse;
476
- this.visitorId = visit.visitorId;
477
- return visit;
478
- }
479
-
480
- async getHealth(): Promise<HealthResponse> {
481
- // deepcode ignore Ssrf: url is supplied by script owner
482
- const response = await fetch(`${this.baseUrl}/analytics/monitoring/health`);
483
- return (await response.json()) as HealthResponse;
484
- }
485
-
486
- registerBeforeSendEventHook(hook: AnalyticsClientSendEventHook): void {
487
- this.beforeSendHooks.push(hook);
488
- }
489
-
490
- registerAfterSendEventHook(hook: AnalyticsClientSendEventHook): void {
491
- this.afterSendHooks.push(hook);
492
- }
493
-
494
- addEventTypeMapping(eventType: string, eventConfig: EventTypeConfig): void {
495
- this.eventTypeMapping[eventType] = eventConfig;
496
- }
497
-
498
- setAcceptedLinkReferrers(hosts: string[]): void {
499
- if (Array.isArray(hosts) && hosts.every((host) => typeof host == 'string')) this.acceptedLinkReferrers = hosts;
500
- else throw Error('Parameter should be an array of domain strings');
501
- }
502
-
503
- private parseVariableArgumentsPayload(fieldsOrder: string[], payload: VariableArgumentsPayload) {
504
- const parsedArguments: {[name: string]: any} = {};
505
- for (let i = 0, length = payload.length; i < length; i++) {
506
- const currentArgument = payload[i];
507
- if (typeof currentArgument === 'string') {
508
- parsedArguments[fieldsOrder[i]] = currentArgument;
509
- } else if (typeof currentArgument === 'object') {
510
- // If the argument is an object, it is considered the last argument of the chain. Its values should be returned as-is.
511
- return {
512
- ...parsedArguments,
513
- ...currentArgument,
514
- };
515
- }
516
- }
517
- return parsedArguments;
518
- }
519
-
520
- private isKeyAllowedEmpty(evtType: string, key: string) {
521
- const keysThatCanBeEmpty: Record<string, string[]> = {
522
- [EventType.search]: ['queryText'],
523
- };
524
-
525
- const match = keysThatCanBeEmpty[evtType] || [];
526
- return match.indexOf(key) !== -1;
527
- }
528
-
529
- private removeEmptyPayloadValues(payload: IRequestPayload, eventType: string): IRequestPayload {
530
- const isNotEmptyValue = (value: any) => typeof value !== 'undefined' && value !== null && value !== '';
531
- return Object.keys(payload)
532
- .filter((key) => this.isKeyAllowedEmpty(eventType, key) || isNotEmptyValue(payload[key]))
533
- .reduce(
534
- (newPayload, key) => ({
535
- ...newPayload,
536
- [key]: payload[key],
537
- }),
538
- {},
539
- );
540
- }
541
-
542
- private removeUnknownParameters(payload: IRequestPayload): IRequestPayload {
543
- const newPayload = Object.keys(payload)
544
- .filter((key) => {
545
- if (isMeasurementProtocolKey(key)) {
546
- return true;
547
- } else {
548
- console.log(key, 'is not processed by coveoua');
549
- }
550
- })
551
- .reduce(
552
- (newPayload, key) => ({
553
- ...newPayload,
554
- [key]: payload[key],
555
- }),
556
- {},
557
- );
558
- return newPayload;
559
- }
560
-
561
- private processCustomParameters(payload: IRequestPayload): IRequestPayload {
562
- const {custom, ...rest} = payload;
563
- let lowercasedCustom = {};
564
- if (custom && isObject(custom)) {
565
- lowercasedCustom = this.lowercaseKeys(custom);
566
- }
567
-
568
- const newPayload = convertCustomMeasurementProtocolKeys(rest);
569
-
457
+ await this.deferExecution();
458
+ return (await this.sendFromBuffer()) as TResponse | void;
459
+ },
460
+ };
461
+ }
462
+
463
+ async sendEvent(eventType: EventType | string, ...payload: VariableArgumentsPayload) {
464
+ return (await this.makeEvent<any, any, AnyEventResponse>(eventType, ...payload)).log({});
465
+ }
466
+
467
+ private deferExecution(): Promise<void> {
468
+ return new Promise((resolve) => setTimeout(resolve, 0));
469
+ }
470
+
471
+ private async sendFromBuffer(): Promise<AnyEventResponse | void> {
472
+ const popped = this.bufferedRequests.shift();
473
+ if (popped) {
474
+ const {eventType, payload} = popped;
475
+ return this.runtime.getClientDependingOnEventType(eventType).sendEvent(eventType, payload);
476
+ }
477
+ }
478
+
479
+ public clear() {
480
+ this.storage.removeItem('visitorId');
481
+ const store = new HistoryStore();
482
+ store.clear();
483
+ }
484
+
485
+ public deleteHttpOnlyVisitorId() {
486
+ this.runtime.client.deleteHttpCookieVisitorId();
487
+ }
488
+
489
+ async makeSearchEvent(request: PreparedSearchEventRequest) {
490
+ return this.makeEvent<PreparedSearchEventRequest, SearchEventRequest, SearchEventResponse>(
491
+ EventType.search,
492
+ request
493
+ );
494
+ }
495
+
496
+ async sendSearchEvent({searchQueryUid, ...preparedRequest}: SearchEventRequest) {
497
+ return (await this.makeSearchEvent(preparedRequest)).log({searchQueryUid});
498
+ }
499
+
500
+ async makeClickEvent(request: PreparedClickEventRequest) {
501
+ return this.makeEvent<PreparedClickEventRequest, ClickEventRequest, ClickEventResponse>(
502
+ EventType.click,
503
+ request
504
+ );
505
+ }
506
+
507
+ async sendClickEvent({searchQueryUid, ...preparedRequest}: ClickEventRequest) {
508
+ return (await this.makeClickEvent(preparedRequest)).log({searchQueryUid});
509
+ }
510
+
511
+ async makeCustomEvent(request: PreparedCustomEventRequest) {
512
+ return this.makeEvent<PreparedCustomEventRequest, CustomEventRequest, CustomEventResponse>(
513
+ EventType.custom,
514
+ request
515
+ );
516
+ }
517
+
518
+ async sendCustomEvent({lastSearchQueryUid, ...preparedRequest}: CustomEventRequest) {
519
+ return (await this.makeCustomEvent(preparedRequest)).log({
520
+ lastSearchQueryUid,
521
+ });
522
+ }
523
+
524
+ async makeViewEvent(request: PreparedViewEventRequest) {
525
+ return this.makeEvent<PreparedViewEventRequest, ViewEventRequest, ViewEventResponse>(
526
+ EventType.view,
527
+ request
528
+ );
529
+ }
530
+
531
+ async sendViewEvent(request: ViewEventRequest): Promise<ViewEventResponse | void> {
532
+ return (await this.makeViewEvent(request)).log({});
533
+ }
534
+
535
+ async getVisit(): Promise<VisitResponse> {
536
+ // deepcode ignore Ssrf: url is supplied by script owner
537
+ const response = await fetch(`${this.baseUrl}/analytics/visit`);
538
+ const visit = (await response.json()) as VisitResponse;
539
+ this.visitorId = visit.visitorId;
540
+ return visit;
541
+ }
542
+
543
+ async getHealth(): Promise<HealthResponse> {
544
+ // deepcode ignore Ssrf: url is supplied by script owner
545
+ const response = await fetch(`${this.baseUrl}/analytics/monitoring/health`);
546
+ return (await response.json()) as HealthResponse;
547
+ }
548
+
549
+ registerBeforeSendEventHook(hook: AnalyticsClientSendEventHook): void {
550
+ this.beforeSendHooks.push(hook);
551
+ }
552
+
553
+ registerAfterSendEventHook(hook: AnalyticsClientSendEventHook): void {
554
+ this.afterSendHooks.push(hook);
555
+ }
556
+
557
+ addEventTypeMapping(eventType: string, eventConfig: EventTypeConfig): void {
558
+ this.eventTypeMapping[eventType] = eventConfig;
559
+ }
560
+
561
+ setAcceptedLinkReferrers(hosts: string[]): void {
562
+ if (Array.isArray(hosts) && hosts.every((host) => typeof host == 'string'))
563
+ this.acceptedLinkReferrers = hosts;
564
+ else throw Error('Parameter should be an array of domain strings');
565
+ }
566
+
567
+ private parseVariableArgumentsPayload(fieldsOrder: string[], payload: VariableArgumentsPayload) {
568
+ const parsedArguments: {[name: string]: any} = {};
569
+ for (let i = 0, length = payload.length; i < length; i++) {
570
+ const currentArgument = payload[i];
571
+ if (typeof currentArgument === 'string') {
572
+ parsedArguments[fieldsOrder[i]] = currentArgument;
573
+ } else if (typeof currentArgument === 'object') {
574
+ // If the argument is an object, it is considered the last argument of the chain. Its values should be returned as-is.
570
575
  return {
571
- ...lowercasedCustom,
572
- ...newPayload,
576
+ ...parsedArguments,
577
+ ...currentArgument,
573
578
  };
574
- }
575
-
576
- private mapCustomParametersToCustomData(payload: IRequestPayload): IRequestPayload {
577
- const {custom, ...rest} = payload;
578
- if (custom && isObject(custom)) {
579
- const lowercasedCustom = this.lowercaseKeys(custom);
580
- return {...rest, customData: {...lowercasedCustom, ...payload.customData}};
581
- } else {
582
- return payload;
583
- }
584
- }
585
-
586
- private lowercaseKeys(custom: Record<string, unknown>) {
587
- const keys = Object.keys(custom);
588
- let result: Record<string, unknown> = {};
589
- keys.forEach((key) => {
590
- result[key.toLowerCase() as string] = custom[key];
591
- });
592
- return result;
593
- }
594
-
595
- private validateParams(payload: IRequestPayload, eventType: EventType | string): IRequestPayload {
596
- const {anonymizeIp, ...rest} = payload;
597
- if (anonymizeIp !== undefined) {
598
- if (['0', 'false', 'undefined', 'null', '{}', '[]', ''].indexOf(`${anonymizeIp}`.toLowerCase()) == -1) {
599
- rest.anonymizeIp = 1;
600
- }
601
- }
602
-
603
- if (
604
- eventType == EventType.view ||
605
- eventType == EventType.click ||
606
- eventType == EventType.search ||
607
- eventType == EventType.custom
608
- ) {
609
- rest.originLevel3 = this.limit(rest.originLevel3, 1024);
610
- }
611
- if (eventType == EventType.view) {
612
- rest.location = this.limit(rest.location, 1024);
613
- }
614
- if (eventType == 'pageview' || eventType == 'event') {
615
- rest.referrer = this.limit(rest.referrer, 2048);
616
- rest.location = this.limit(rest.location, 2048);
617
- rest.page = this.limit(rest.page, 2048);
618
- }
619
- return rest;
620
- }
621
-
622
- private ensureAnonymousUserWhenUsingApiKey(payload: IRequestPayload): IRequestPayload {
623
- const {userId, ...rest} = payload;
624
- if (isApiKey(this.options.token) && !userId) {
625
- rest['userId'] = 'anonymous';
626
- return rest;
579
+ }
580
+ }
581
+ return parsedArguments;
582
+ }
583
+
584
+ private isKeyAllowedEmpty(evtType: string, key: string) {
585
+ const keysThatCanBeEmpty: Record<string, string[]> = {
586
+ [EventType.search]: ['queryText'],
587
+ };
588
+
589
+ const match = keysThatCanBeEmpty[evtType] || [];
590
+ return match.indexOf(key) !== -1;
591
+ }
592
+
593
+ private removeEmptyPayloadValues(payload: IRequestPayload, eventType: string): IRequestPayload {
594
+ const isNotEmptyValue = (value: any) =>
595
+ typeof value !== 'undefined' && value !== null && value !== '';
596
+ return Object.keys(payload)
597
+ .filter((key) => this.isKeyAllowedEmpty(eventType, key) || isNotEmptyValue(payload[key]))
598
+ .reduce(
599
+ (newPayload, key) => ({
600
+ ...newPayload,
601
+ [key]: payload[key],
602
+ }),
603
+ {}
604
+ );
605
+ }
606
+
607
+ private removeUnknownParameters(payload: IRequestPayload): IRequestPayload {
608
+ const newPayload = Object.keys(payload)
609
+ .filter((key) => {
610
+ if (isMeasurementProtocolKey(key)) {
611
+ return true;
627
612
  } else {
628
- return payload;
629
- }
630
- }
631
-
632
- private setTrackingIdIfTrackingIdNotPresent(payload: IRequestPayload): IRequestPayload {
633
- const {trackingId, ...rest} = payload;
634
- if (trackingId) {
635
- return payload;
636
- }
637
-
638
- if (rest.hasOwnProperty('custom') && isObject(rest.custom)) {
639
- if (rest.custom.hasOwnProperty('context_website') || rest.custom.hasOwnProperty('siteName')) {
640
- rest['trackingId'] = rest.custom.context_website || rest.custom.siteName;
641
- }
642
- }
643
-
644
- if (rest.hasOwnProperty('customData') && isObject(rest.customData)) {
645
- if (rest.customData.hasOwnProperty('context_website') || rest.customData.hasOwnProperty('siteName')) {
646
- rest['trackingId'] = rest.customData.context_website || rest.customData.siteName;
647
- }
613
+ console.log(key, 'is not processed by coveoua');
648
614
  }
649
-
650
- return rest;
651
- }
652
-
653
- private limit<T>(input: T, length: number): T {
654
- return typeof input === 'string' ? (truncateUrl(input, length) as T) : input;
655
- }
656
-
657
- private get baseUrl(): string {
658
- return buildBaseUrl(this.options.endpoint, this.options.version, this.options.isCustomEndpoint);
659
- }
615
+ })
616
+ .reduce(
617
+ (newPayload, key) => ({
618
+ ...newPayload,
619
+ [key]: payload[key],
620
+ }),
621
+ {}
622
+ );
623
+ return newPayload;
624
+ }
625
+
626
+ private processCustomParameters(payload: IRequestPayload): IRequestPayload {
627
+ const {custom, ...rest} = payload;
628
+ let lowercasedCustom = {};
629
+ if (custom && isObject(custom)) {
630
+ lowercasedCustom = this.lowercaseKeys(custom);
631
+ }
632
+
633
+ const newPayload = convertCustomMeasurementProtocolKeys(rest);
634
+
635
+ return {
636
+ ...lowercasedCustom,
637
+ ...newPayload,
638
+ };
639
+ }
640
+
641
+ private mapCustomParametersToCustomData(payload: IRequestPayload): IRequestPayload {
642
+ const {custom, ...rest} = payload;
643
+ if (custom && isObject(custom)) {
644
+ const lowercasedCustom = this.lowercaseKeys(custom);
645
+ return {
646
+ ...rest,
647
+ customData: {...lowercasedCustom, ...payload.customData},
648
+ };
649
+ } else {
650
+ return payload;
651
+ }
652
+ }
653
+
654
+ private lowercaseKeys(custom: Record<string, unknown>) {
655
+ const keys = Object.keys(custom);
656
+ let result: Record<string, unknown> = {};
657
+ keys.forEach((key) => {
658
+ result[key.toLowerCase() as string] = custom[key];
659
+ });
660
+ return result;
661
+ }
662
+
663
+ private validateParams(payload: IRequestPayload, eventType: EventType | string): IRequestPayload {
664
+ const {anonymizeIp, ...rest} = payload;
665
+ if (anonymizeIp !== undefined) {
666
+ if (
667
+ ['0', 'false', 'undefined', 'null', '{}', '[]', ''].indexOf(
668
+ `${anonymizeIp}`.toLowerCase()
669
+ ) == -1
670
+ ) {
671
+ rest.anonymizeIp = 1;
672
+ }
673
+ }
674
+
675
+ if (
676
+ eventType == EventType.view ||
677
+ eventType == EventType.click ||
678
+ eventType == EventType.search ||
679
+ eventType == EventType.custom
680
+ ) {
681
+ rest.originLevel3 = this.limit(rest.originLevel3, 1024);
682
+ }
683
+ if (eventType == EventType.view) {
684
+ rest.location = this.limit(rest.location, 1024);
685
+ }
686
+ if (eventType == 'pageview' || eventType == 'event') {
687
+ rest.referrer = this.limit(rest.referrer, 2048);
688
+ rest.location = this.limit(rest.location, 2048);
689
+ rest.page = this.limit(rest.page, 2048);
690
+ }
691
+ return rest;
692
+ }
693
+
694
+ private ensureAnonymousUserWhenUsingApiKey(payload: IRequestPayload): IRequestPayload {
695
+ const {userId, ...rest} = payload;
696
+ if (isApiKey(this.options.token) && !userId) {
697
+ rest['userId'] = 'anonymous';
698
+ return rest;
699
+ } else {
700
+ return payload;
701
+ }
702
+ }
703
+
704
+ private setTrackingIdIfTrackingIdNotPresent(payload: IRequestPayload): IRequestPayload {
705
+ const {trackingId, ...rest} = payload;
706
+ if (trackingId) {
707
+ return payload;
708
+ }
709
+
710
+ if (rest.hasOwnProperty('custom') && isObject(rest.custom)) {
711
+ if (rest.custom.hasOwnProperty('context_website') || rest.custom.hasOwnProperty('siteName')) {
712
+ rest['trackingId'] = rest.custom.context_website || rest.custom.siteName;
713
+ }
714
+ }
715
+
716
+ if (rest.hasOwnProperty('customData') && isObject(rest.customData)) {
717
+ if (
718
+ rest.customData.hasOwnProperty('context_website') ||
719
+ rest.customData.hasOwnProperty('siteName')
720
+ ) {
721
+ rest['trackingId'] = rest.customData.context_website || rest.customData.siteName;
722
+ }
723
+ }
724
+
725
+ return rest;
726
+ }
727
+
728
+ private limit<T>(input: T, length: number): T {
729
+ return typeof input === 'string' ? (truncateUrl(input, length) as T) : input;
730
+ }
731
+
732
+ private get baseUrl(): string {
733
+ return buildBaseUrl(this.options.endpoint, this.options.version, this.options.isCustomEndpoint);
734
+ }
660
735
  }
661
736
 
662
737
  export default CoveoAnalyticsClient;