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.
- package/README.md +37 -43
- package/dist/browser.mjs +513 -357
- package/dist/coveoua.browser.js +1 -1
- package/dist/coveoua.browser.js.map +1 -1
- package/dist/coveoua.debug.js +542 -359
- package/dist/coveoua.debug.js.map +1 -1
- package/dist/coveoua.js +1 -1
- package/dist/coveoua.js.map +1 -1
- package/dist/definitions/client/analytics.d.ts +3 -2
- package/dist/definitions/client/runtimeEnvironment.d.ts +1 -1
- package/dist/definitions/donottrack.d.ts +1 -0
- package/dist/definitions/react-native/index.d.ts +1 -2
- package/dist/definitions/version.d.ts +1 -1
- package/dist/library.cjs +15350 -3699
- package/dist/library.es.js +513 -357
- package/dist/library.js +15350 -3699
- package/dist/library.mjs +15350 -3699
- package/dist/react-native.es.js +634 -460
- package/modules/package.json +9 -9
- package/package.json +60 -91
- package/react-native/package.json +7 -7
- package/src/caseAssist/caseAssistActions.ts +51 -50
- package/src/caseAssist/caseAssistClient.spec.ts +328 -297
- package/src/caseAssist/caseAssistClient.ts +201 -185
- package/src/client/analytics.spec.ts +724 -703
- package/src/client/analytics.ts +677 -602
- package/src/client/analyticsBeaconClient.spec.ts +195 -188
- package/src/client/analyticsBeaconClient.ts +99 -88
- package/src/client/analyticsFetchClient.ts +77 -61
- package/src/client/analyticsRequestClient.ts +17 -17
- package/src/client/location.ts +3 -3
- package/src/client/measurementProtocolMapper.ts +54 -45
- package/src/client/measurementProtocolMapping/baseMeasurementProtocolMapper.ts +48 -44
- package/src/client/measurementProtocolMapping/commerceMeasurementProtocolMapper.ts +133 -121
- package/src/client/measurementProtocolMapping/serviceMeasurementProtocolMapper.ts +17 -17
- package/src/client/noopAnalytics.ts +80 -68
- package/src/client/runtimeEnvironment.ts +50 -41
- package/src/client/utils.spec.ts +112 -97
- package/src/client/utils.ts +39 -39
- package/src/cookieutils.spec.ts +59 -0
- package/src/cookieutils.ts +40 -39
- package/src/coveoua/browser.ts +14 -12
- package/src/coveoua/library.ts +4 -4
- package/src/coveoua/plugins.ts +36 -34
- package/src/coveoua/simpleanalytics.spec.ts +467 -452
- package/src/coveoua/simpleanalytics.ts +146 -140
- package/src/detector.ts +17 -17
- package/src/donottrack.spec.ts +199 -121
- package/src/donottrack.ts +31 -8
- package/src/events.ts +86 -79
- package/src/formatting/format-array-for-coveo-custom-data.spec.ts +13 -12
- package/src/formatting/format-array-for-coveo-custom-data.ts +18 -18
- package/src/formatting/format-omnibox-metadata.ts +16 -12
- package/src/history.spec.ts +197 -195
- package/src/history.ts +143 -133
- package/src/hook/addDefaultValues.ts +13 -8
- package/src/hook/enhanceViewEvent.ts +17 -17
- package/src/insight/insightClient.spec.ts +1848 -1717
- package/src/insight/insightClient.ts +866 -761
- package/src/insight/insightEvents.ts +57 -56
- package/src/plugins/BasePlugin.ts +125 -121
- package/src/plugins/ec.spec.ts +457 -429
- package/src/plugins/ec.ts +202 -199
- package/src/plugins/link.spec.ts +183 -159
- package/src/plugins/link.ts +88 -85
- package/src/plugins/svc.spec.ts +161 -155
- package/src/plugins/svc.ts +93 -91
- package/src/react-native/index.ts +8 -1
- package/src/react-native/react-native-runtime.ts +33 -33
- package/src/react-native/react-native-utils.ts +1 -1
- package/src/react-native/react-native.spec.ts +31 -21
- package/src/searchPage/searchPageClient.spec.ts +1944 -1794
- package/src/searchPage/searchPageClient.ts +1261 -1040
- package/src/searchPage/searchPageEvents.ts +524 -511
- package/src/storage.spec.ts +51 -51
- package/src/storage.ts +47 -47
- package/dist/definitions/bundle/browser-fetch.d.ts +0 -1
- package/dist/definitions/src/coveoua/browser.d.ts +0 -6
- package/dist/definitions/src/coveoua/headless.d.ts +0 -6
- package/dist/definitions/src/coveoua/library.d.ts +0 -17
- package/dist/definitions/src/coveoua/plugins.d.ts +0 -10
- package/dist/definitions/src/coveoua/simpleanalytics.d.ts +0 -33
- package/dist/definitions/src/react-native/index.d.ts +0 -3
- package/dist/definitions/src/react-native/react-native-runtime.d.ts +0 -19
package/src/client/analytics.ts
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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 {
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
convertKeysToMeasurementProtocol,
|
|
34
|
+
isMeasurementProtocolKey,
|
|
35
|
+
convertCustomMeasurementProtocolKeys,
|
|
32
36
|
} from './measurementProtocolMapper';
|
|
33
|
-
import {
|
|
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 {
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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>(
|
|
74
|
+
export type AnalyticsClientSendEventHook = <TResult>(
|
|
75
|
+
eventType: string,
|
|
76
|
+
payload: any
|
|
77
|
+
) => TResult | Promise<TResult>;
|
|
60
78
|
export type EventTypeConfig = {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
newEventType: EventType;
|
|
80
|
+
variableLengthArgumentsNames?: string[];
|
|
81
|
+
addVisitorIdParameter?: boolean;
|
|
82
|
+
addClientIdParameter?: boolean;
|
|
83
|
+
usesMeasurementProtocol?: boolean;
|
|
66
84
|
};
|
|
67
85
|
|
|
68
|
-
export interface PreparedEvent<
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
114
|
-
|
|
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(
|
|
121
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
151
|
+
if (isCustomEndpoint) {
|
|
152
|
+
return `${endpoint}/${apiVersion}`;
|
|
153
|
+
}
|
|
126
154
|
|
|
127
|
-
|
|
155
|
+
const hasUARestEndpoint = endpoint.endsWith('/rest') || endpoint.endsWith('/rest/ua');
|
|
128
156
|
|
|
129
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
390
|
-
|
|
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
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
572
|
-
|
|
576
|
+
...parsedArguments,
|
|
577
|
+
...currentArgument,
|
|
573
578
|
};
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
-
|
|
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
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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;
|