coveo.analytics 2.30.55 → 2.31.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 (86) hide show
  1. package/README.md +37 -43
  2. package/dist/browser.mjs +525 -357
  3. package/dist/coveoua.browser.js +1 -1
  4. package/dist/coveoua.browser.js.map +1 -1
  5. package/dist/coveoua.debug.js +559 -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 -1
  13. package/dist/definitions/searchPage/searchPageClient.d.ts +3 -1
  14. package/dist/definitions/searchPage/searchPageEvents.d.ts +6 -0
  15. package/dist/definitions/version.d.ts +1 -1
  16. package/dist/library.cjs +15367 -3699
  17. package/dist/library.es.js +525 -357
  18. package/dist/library.js +15367 -3699
  19. package/dist/library.mjs +15367 -3699
  20. package/dist/react-native.es.js +646 -460
  21. package/modules/package.json +9 -9
  22. package/package.json +60 -91
  23. package/react-native/package.json +7 -7
  24. package/src/caseAssist/caseAssistActions.ts +51 -50
  25. package/src/caseAssist/caseAssistClient.spec.ts +328 -297
  26. package/src/caseAssist/caseAssistClient.ts +201 -185
  27. package/src/client/analytics.spec.ts +724 -703
  28. package/src/client/analytics.ts +677 -602
  29. package/src/client/analyticsBeaconClient.spec.ts +195 -188
  30. package/src/client/analyticsBeaconClient.ts +99 -88
  31. package/src/client/analyticsFetchClient.ts +77 -61
  32. package/src/client/analyticsRequestClient.ts +17 -17
  33. package/src/client/location.ts +3 -3
  34. package/src/client/measurementProtocolMapper.ts +54 -45
  35. package/src/client/measurementProtocolMapping/baseMeasurementProtocolMapper.ts +48 -44
  36. package/src/client/measurementProtocolMapping/commerceMeasurementProtocolMapper.ts +133 -121
  37. package/src/client/measurementProtocolMapping/serviceMeasurementProtocolMapper.ts +17 -17
  38. package/src/client/noopAnalytics.ts +80 -68
  39. package/src/client/runtimeEnvironment.ts +50 -41
  40. package/src/client/utils.spec.ts +112 -97
  41. package/src/client/utils.ts +39 -39
  42. package/src/cookieutils.spec.ts +59 -0
  43. package/src/cookieutils.ts +40 -39
  44. package/src/coveoua/browser.ts +14 -12
  45. package/src/coveoua/library.ts +4 -4
  46. package/src/coveoua/plugins.ts +36 -34
  47. package/src/coveoua/simpleanalytics.spec.ts +467 -452
  48. package/src/coveoua/simpleanalytics.ts +146 -140
  49. package/src/detector.ts +17 -17
  50. package/src/donottrack.spec.ts +199 -121
  51. package/src/donottrack.ts +31 -8
  52. package/src/events.ts +86 -79
  53. package/src/formatting/format-array-for-coveo-custom-data.spec.ts +13 -12
  54. package/src/formatting/format-array-for-coveo-custom-data.ts +18 -18
  55. package/src/formatting/format-omnibox-metadata.ts +16 -12
  56. package/src/history.spec.ts +197 -195
  57. package/src/history.ts +143 -133
  58. package/src/hook/addDefaultValues.ts +13 -8
  59. package/src/hook/enhanceViewEvent.ts +17 -17
  60. package/src/insight/insightClient.spec.ts +1848 -1717
  61. package/src/insight/insightClient.ts +866 -761
  62. package/src/insight/insightEvents.ts +57 -56
  63. package/src/plugins/BasePlugin.ts +125 -121
  64. package/src/plugins/ec.spec.ts +457 -429
  65. package/src/plugins/ec.ts +202 -199
  66. package/src/plugins/link.spec.ts +183 -159
  67. package/src/plugins/link.ts +88 -85
  68. package/src/plugins/svc.spec.ts +161 -155
  69. package/src/plugins/svc.ts +93 -91
  70. package/src/react-native/index.ts +5 -1
  71. package/src/react-native/react-native-runtime.ts +33 -33
  72. package/src/react-native/react-native-utils.ts +1 -1
  73. package/src/react-native/react-native.spec.ts +22 -21
  74. package/src/searchPage/searchPageClient.spec.ts +1944 -1766
  75. package/src/searchPage/searchPageClient.ts +1261 -1029
  76. package/src/searchPage/searchPageEvents.ts +529 -500
  77. package/src/storage.spec.ts +51 -51
  78. package/src/storage.ts +47 -47
  79. package/dist/definitions/bundle/browser-fetch.d.ts +0 -1
  80. package/dist/definitions/src/coveoua/browser.d.ts +0 -6
  81. package/dist/definitions/src/coveoua/headless.d.ts +0 -6
  82. package/dist/definitions/src/coveoua/library.d.ts +0 -17
  83. package/dist/definitions/src/coveoua/plugins.d.ts +0 -10
  84. package/dist/definitions/src/coveoua/simpleanalytics.d.ts +0 -33
  85. package/dist/definitions/src/react-native/index.d.ts +0 -3
  86. package/dist/definitions/src/react-native/react-native-runtime.d.ts +0 -19
@@ -1,207 +1,214 @@
1
+ import {vi} from 'vitest';
1
2
  import {AnalyticsBeaconClient} from './analyticsBeaconClient';
2
3
  import {EventType} from '../events';
3
- import {AnalyticsClientOrigin, IAnalyticsRequestOptions, PreprocessAnalyticsRequest} from './analyticsRequestClient';
4
+ import {
5
+ AnalyticsClientOrigin,
6
+ IAnalyticsRequestOptions,
7
+ PreprocessAnalyticsRequest,
8
+ } from './analyticsRequestClient';
4
9
 
5
10
  describe('AnalyticsBeaconClient', () => {
6
- const baseUrl = 'https://bloup.com';
7
- const token = '👛';
8
- const currentVisitorId = 'hereiam';
9
-
10
- const originalSendBeacon = navigator.sendBeacon;
11
- const sendBeaconMock = jest.fn();
12
- beforeAll(() => {
13
- navigator.sendBeacon = sendBeaconMock;
11
+ const baseUrl = 'https://bloup.com';
12
+ const token = '👛';
13
+ const currentVisitorId = 'mockVisitorId';
14
+
15
+ const originalSendBeacon = navigator.sendBeacon;
16
+ const sendBeaconMock = vi.fn();
17
+ beforeAll(() => {
18
+ navigator.sendBeacon = sendBeaconMock;
19
+ });
20
+
21
+ afterAll(() => {
22
+ navigator.sendBeacon = originalSendBeacon;
23
+ });
24
+
25
+ beforeEach(() => {
26
+ vi.clearAllMocks();
27
+ });
28
+
29
+ it('can send an event', async () => {
30
+ const eventType: EventType = EventType.custom;
31
+
32
+ const client = new AnalyticsBeaconClient({
33
+ baseUrl,
34
+ token,
35
+ visitorIdProvider: {
36
+ getCurrentVisitorId: () => Promise.resolve(currentVisitorId),
37
+ setCurrentVisitorId: (visitorId) => {},
38
+ },
14
39
  });
15
40
 
16
- afterAll(() => {
17
- navigator.sendBeacon = originalSendBeacon;
41
+ await client.sendEvent(eventType, {
42
+ wow: 'ok',
18
43
  });
19
44
 
20
- beforeEach(() => {
21
- jest.clearAllMocks();
45
+ expect(sendBeaconMock).toHaveBeenCalledWith(
46
+ `${baseUrl}/analytics/custom?access_token=👛&visitorId=${currentVisitorId}&discardVisitInfo=true`,
47
+ expect.anything()
48
+ );
49
+ expect(await getSendBeaconFirstCallBlobArgument()).toBe(
50
+ `customEvent=${encodeURIComponent('{"wow":"ok"}')}`
51
+ );
52
+ });
53
+
54
+ it('can send a collect event with the proper payload', async () => {
55
+ const eventType: EventType = EventType.collect;
56
+
57
+ const client = new AnalyticsBeaconClient({
58
+ baseUrl,
59
+ token,
60
+ visitorIdProvider: {
61
+ getCurrentVisitorId: () => Promise.resolve(currentVisitorId),
62
+ setCurrentVisitorId: (visitorId) => {},
63
+ },
22
64
  });
23
65
 
24
- it('can send an event', async () => {
25
- const eventType: EventType = EventType.custom;
26
-
27
- const client = new AnalyticsBeaconClient({
28
- baseUrl,
29
- token,
30
- visitorIdProvider: {
31
- getCurrentVisitorId: () => Promise.resolve(currentVisitorId),
32
- setCurrentVisitorId: (visitorId) => {},
33
- },
34
- });
35
-
36
- await client.sendEvent(eventType, {
37
- wow: 'ok',
38
- });
39
-
40
- expect(sendBeaconMock).toHaveBeenCalledWith(
41
- `${baseUrl}/analytics/custom?access_token=👛&visitorId=${currentVisitorId}&discardVisitInfo=true`,
42
- expect.anything(),
43
- );
44
- expect(await getSendBeaconFirstCallBlobArgument()).toBe(`customEvent=${encodeURIComponent('{"wow":"ok"}')}`);
66
+ await client.sendEvent(eventType, {
67
+ pr1a: 'value',
68
+ 'to encode': 'to encode',
45
69
  });
46
70
 
47
- it('can send a collect event with the proper payload', async () => {
48
- const eventType: EventType = EventType.collect;
49
-
50
- const client = new AnalyticsBeaconClient({
51
- baseUrl,
52
- token,
53
- visitorIdProvider: {
54
- getCurrentVisitorId: () => Promise.resolve(currentVisitorId),
55
- setCurrentVisitorId: (visitorId) => {},
56
- },
57
- });
58
-
59
- await client.sendEvent(eventType, {
60
- pr1a: 'value',
61
- 'to encode': 'to encode',
62
- });
63
-
64
- expect(sendBeaconMock).toHaveBeenCalledWith(
65
- `${baseUrl}/analytics/collect?visitorId=${currentVisitorId}&discardVisitInfo=true`,
66
- expect.anything(),
67
- );
68
- expect(await getSendBeaconFirstCallBlobArgument()).toBe(
69
- `access_token=${encodeURIComponent('👛')}&collectEvent=${encodeURIComponent(
70
- '{"pr1a":"value","to encode":"to encode"}',
71
- )}`,
72
- );
71
+ expect(sendBeaconMock).toHaveBeenCalledWith(
72
+ `${baseUrl}/analytics/collect?visitorId=${currentVisitorId}&discardVisitInfo=true`,
73
+ expect.anything()
74
+ );
75
+ expect(await getSendBeaconFirstCallBlobArgument()).toBe(
76
+ `access_token=${encodeURIComponent('👛')}&collectEvent=${encodeURIComponent(
77
+ '{"pr1a":"value","to encode":"to encode"}'
78
+ )}`
79
+ );
80
+ });
81
+
82
+ it('can send a collect event with a more complex payload', async () => {
83
+ const eventType: EventType = EventType.collect;
84
+
85
+ const client = new AnalyticsBeaconClient({
86
+ baseUrl,
87
+ token,
88
+ visitorIdProvider: {
89
+ getCurrentVisitorId: () => {
90
+ return Promise.resolve(currentVisitorId);
91
+ },
92
+ setCurrentVisitorId: (visitorId) => {},
93
+ },
73
94
  });
74
95
 
75
- it('can send a collect event with a more complex payload', async () => {
76
- const eventType: EventType = EventType.collect;
77
-
78
- const client = new AnalyticsBeaconClient({
79
- baseUrl,
80
- token,
81
- visitorIdProvider: {
82
- getCurrentVisitorId: () => {
83
- return Promise.resolve(currentVisitorId);
84
- },
85
- setCurrentVisitorId: (visitorId) => {},
86
- },
87
- });
88
-
89
- await client.sendEvent(eventType, {
90
- value: {
91
- subvalue: 'ok',
92
- },
93
- });
94
-
95
- expect(sendBeaconMock).toHaveBeenCalledWith(
96
- `${baseUrl}/analytics/collect?visitorId=${currentVisitorId}&discardVisitInfo=true`,
97
- expect.anything(),
98
- );
99
- expect(await getSendBeaconFirstCallBlobArgument()).toBe(
100
- `access_token=${encodeURIComponent('👛')}&collectEvent=${encodeURIComponent('{"value":{"subvalue":"ok"}}')}`,
101
- );
96
+ await client.sendEvent(eventType, {
97
+ value: {
98
+ subvalue: 'ok',
99
+ },
102
100
  });
103
101
 
104
- describe('allows to preprocessRequest', () => {
105
- const setupClient = (preprocessRequest: PreprocessAnalyticsRequest) => {
106
- return new AnalyticsBeaconClient({
107
- baseUrl,
108
- token,
109
- visitorIdProvider: {
110
- getCurrentVisitorId: () => {
111
- return Promise.resolve(currentVisitorId);
112
- },
113
- setCurrentVisitorId: () => {},
114
- },
115
- preprocessRequest,
116
- });
117
- };
118
-
119
- it('to modify the origin and the body of the request', async () => {
120
- let clientOrigin: AnalyticsClientOrigin;
121
- const processedRequest: IAnalyticsRequestOptions = {
122
- url: 'https://www.myownanalytics.com/endpoint',
123
- body: JSON.stringify({
124
- test: 'custom',
125
- }),
126
- };
127
- const client = setupClient((_request, type) => {
128
- clientOrigin = type;
129
- return processedRequest;
130
- });
131
-
132
- await client.sendEvent(EventType.collect, {});
133
-
134
- expect(clientOrigin!).toBe('analyticsBeacon');
135
- expect(sendBeaconMock).toHaveBeenCalledWith(processedRequest.url, expect.anything());
136
- expect(await getSendBeaconFirstCallBlobArgument()).toContain('%22test%22%3A%22custom%22');
137
- });
138
-
139
- it('to modify the request body as a JSON string for a collect event', async () => {
140
- const client = setupClient((request) => {
141
- const bodyShouldBeAvailableAsJSONString = JSON.parse(request.body as string);
142
- expect(bodyShouldBeAvailableAsJSONString).toEqual({foo: 'bar'});
143
- bodyShouldBeAvailableAsJSONString.foo = 'baz';
144
- request.body = JSON.stringify(bodyShouldBeAvailableAsJSONString);
145
- return request;
146
- });
147
-
148
- await client.sendEvent(EventType.collect, {foo: 'bar'});
149
- expect(await getSendBeaconFirstCallBlobArgument()).toBe(
150
- 'access_token=%F0%9F%91%9B&collectEvent=%7B%22foo%22%3A%22baz%22%7D',
151
- );
152
- });
153
-
154
- it('to modify the request body as a JSON string for a click event', async () => {
155
- const client = setupClient((request) => {
156
- const bodyParsedAsJSON = JSON.parse(request.body as string);
157
- expect(bodyParsedAsJSON).toEqual({actionCause: 'foo'});
158
- bodyParsedAsJSON.actionCause = 'bar';
159
- request.body = JSON.stringify(bodyParsedAsJSON);
160
- return request;
161
- });
162
-
163
- await client.sendEvent(EventType.click, {actionCause: 'foo'});
164
- expect(await getSendBeaconFirstCallBlobArgument()).toContain(
165
- `clickEvent=${encodeURIComponent('{"actionCause":"bar"}')}`,
166
- );
167
- });
168
-
169
- it('to augment the request body as a JSON string for a click event', async () => {
170
- const client = setupClient((request) => {
171
- const bodyParsedAsJSON = JSON.parse(request.body as string);
172
- bodyParsedAsJSON.aNewProperty = 'bar';
173
- request.body = JSON.stringify(bodyParsedAsJSON);
174
- return request;
175
- });
176
-
177
- await client.sendEvent(EventType.click, {actionCause: 'foo'});
178
- expect(await getSendBeaconFirstCallBlobArgument()).toContain(
179
- `clickEvent=${encodeURIComponent('{"actionCause":"foo","aNewProperty":"bar"}')}`,
180
- );
181
- });
182
-
183
- it('should keep original request body if preprocessRequest returns an invalid JSON string', async () => {
184
- const client = setupClient((request) => {
185
- request.body = 'invalid JSON string';
186
- return request;
187
- });
188
-
189
- await client.sendEvent(EventType.click, {actionCause: 'bar'});
190
- expect(await getSendBeaconFirstCallBlobArgument()).toContain(
191
- `clickEvent=${encodeURIComponent(`{"actionCause":"bar"}`)}`,
192
- );
193
- });
102
+ expect(sendBeaconMock).toHaveBeenCalledWith(
103
+ `${baseUrl}/analytics/collect?visitorId=${currentVisitorId}&discardVisitInfo=true`,
104
+ expect.anything()
105
+ );
106
+ expect(await getSendBeaconFirstCallBlobArgument()).toBe(
107
+ `access_token=${encodeURIComponent('👛')}&collectEvent=${encodeURIComponent('{"value":{"subvalue":"ok"}}')}`
108
+ );
109
+ });
110
+
111
+ describe('allows to preprocessRequest', () => {
112
+ const setupClient = (preprocessRequest: PreprocessAnalyticsRequest) => {
113
+ return new AnalyticsBeaconClient({
114
+ baseUrl,
115
+ token,
116
+ visitorIdProvider: {
117
+ getCurrentVisitorId: () => {
118
+ return Promise.resolve(currentVisitorId);
119
+ },
120
+ setCurrentVisitorId: () => {},
121
+ },
122
+ preprocessRequest,
123
+ });
124
+ };
125
+
126
+ it('to modify the origin and the body of the request', async () => {
127
+ let clientOrigin: AnalyticsClientOrigin;
128
+ const processedRequest: IAnalyticsRequestOptions = {
129
+ url: 'https://www.myownanalytics.com/endpoint',
130
+ body: JSON.stringify({
131
+ test: 'custom',
132
+ }),
133
+ };
134
+ const client = setupClient((_request, type) => {
135
+ clientOrigin = type;
136
+ return processedRequest;
137
+ });
138
+
139
+ await client.sendEvent(EventType.collect, {});
140
+
141
+ expect(clientOrigin!).toBe('analyticsBeacon');
142
+ expect(sendBeaconMock).toHaveBeenCalledWith(processedRequest.url, expect.anything());
143
+ expect(await getSendBeaconFirstCallBlobArgument()).toContain('%22test%22%3A%22custom%22');
194
144
  });
195
145
 
196
- const getSendBeaconFirstCallBlobArgument = async () => {
197
- const blobArgument: Blob = sendBeaconMock.mock.calls[0][1];
198
- expect(blobArgument.size).toBeGreaterThan(0);
199
- return new Promise((resolve) => {
200
- const reader = new FileReader();
201
- reader.addEventListener('loadend', () => {
202
- resolve(reader.result);
203
- });
204
- reader.readAsText(blobArgument);
205
- });
206
- };
146
+ it('to modify the request body as a JSON string for a collect event', async () => {
147
+ const client = setupClient((request) => {
148
+ const bodyShouldBeAvailableAsJSONString = JSON.parse(request.body as string);
149
+ expect(bodyShouldBeAvailableAsJSONString).toEqual({foo: 'bar'});
150
+ bodyShouldBeAvailableAsJSONString.foo = 'baz';
151
+ request.body = JSON.stringify(bodyShouldBeAvailableAsJSONString);
152
+ return request;
153
+ });
154
+
155
+ await client.sendEvent(EventType.collect, {foo: 'bar'});
156
+ expect(await getSendBeaconFirstCallBlobArgument()).toBe(
157
+ 'access_token=%F0%9F%91%9B&collectEvent=%7B%22foo%22%3A%22baz%22%7D'
158
+ );
159
+ });
160
+
161
+ it('to modify the request body as a JSON string for a click event', async () => {
162
+ const client = setupClient((request) => {
163
+ const bodyParsedAsJSON = JSON.parse(request.body as string);
164
+ expect(bodyParsedAsJSON).toEqual({actionCause: 'foo'});
165
+ bodyParsedAsJSON.actionCause = 'bar';
166
+ request.body = JSON.stringify(bodyParsedAsJSON);
167
+ return request;
168
+ });
169
+
170
+ await client.sendEvent(EventType.click, {actionCause: 'foo'});
171
+ expect(await getSendBeaconFirstCallBlobArgument()).toContain(
172
+ `clickEvent=${encodeURIComponent('{"actionCause":"bar"}')}`
173
+ );
174
+ });
175
+
176
+ it('to augment the request body as a JSON string for a click event', async () => {
177
+ const client = setupClient((request) => {
178
+ const bodyParsedAsJSON = JSON.parse(request.body as string);
179
+ bodyParsedAsJSON.aNewProperty = 'bar';
180
+ request.body = JSON.stringify(bodyParsedAsJSON);
181
+ return request;
182
+ });
183
+
184
+ await client.sendEvent(EventType.click, {actionCause: 'foo'});
185
+ expect(await getSendBeaconFirstCallBlobArgument()).toContain(
186
+ `clickEvent=${encodeURIComponent('{"actionCause":"foo","aNewProperty":"bar"}')}`
187
+ );
188
+ });
189
+
190
+ it('should keep original request body if preprocessRequest returns an invalid JSON string', async () => {
191
+ const client = setupClient((request) => {
192
+ request.body = 'invalid JSON string';
193
+ return request;
194
+ });
195
+
196
+ await client.sendEvent(EventType.click, {actionCause: 'bar'});
197
+ expect(await getSendBeaconFirstCallBlobArgument()).toContain(
198
+ `clickEvent=${encodeURIComponent(`{"actionCause":"bar"}`)}`
199
+ );
200
+ });
201
+ });
202
+
203
+ const getSendBeaconFirstCallBlobArgument = async () => {
204
+ const blobArgument: Blob = sendBeaconMock.mock.calls[0][1];
205
+ expect(blobArgument.size).toBeGreaterThan(0);
206
+ return new Promise((resolve) => {
207
+ const reader = new FileReader();
208
+ reader.addEventListener('loadend', () => {
209
+ resolve(reader.result);
210
+ });
211
+ reader.readAsText(blobArgument);
212
+ });
213
+ };
207
214
  });
@@ -1,98 +1,109 @@
1
- import {AnalyticsRequestClient, IAnalyticsClientOptions, PreprocessAnalyticsRequest} from './analyticsRequestClient';
1
+ import {
2
+ AnalyticsRequestClient,
3
+ IAnalyticsClientOptions,
4
+ PreprocessAnalyticsRequest,
5
+ } from './analyticsRequestClient';
2
6
  import {EventType, IRequestPayload} from '../events';
3
7
 
4
8
  export class AnalyticsBeaconClient implements AnalyticsRequestClient {
5
- constructor(private opts: IAnalyticsClientOptions) {}
9
+ constructor(private opts: IAnalyticsClientOptions) {}
6
10
 
7
- public async sendEvent(eventType: EventType, originalPayload: IRequestPayload): Promise<void> {
8
- if (!this.isAvailable()) {
9
- throw new Error(
10
- `navigator.sendBeacon is not supported in this browser. Consider adding a polyfill like "sendbeacon-polyfill".`,
11
- );
12
- }
13
-
14
- const {baseUrl, preprocessRequest} = this.opts;
15
-
16
- const paramsFragments = await this.getQueryParamsForEventType(eventType);
17
-
18
- const {url, payload} = await this.preProcessRequestAsPotentialJSONString(
19
- `${baseUrl}/analytics/${eventType}?${paramsFragments}`,
20
- originalPayload,
21
- preprocessRequest,
22
- );
23
-
24
- const parsedRequestData = this.encodeForEventType(eventType, payload);
25
- const body = new Blob([parsedRequestData], {
26
- type: 'application/x-www-form-urlencoded',
27
- });
28
-
29
- navigator.sendBeacon(url, body as any); // https://github.com/microsoft/TypeScript/issues/38715
30
- return;
31
- }
32
-
33
- public isAvailable() {
34
- return 'sendBeacon' in navigator;
35
- }
36
-
37
- public deleteHttpCookieVisitorId() {
38
- return Promise.resolve();
39
- }
40
-
41
- private async preProcessRequestAsPotentialJSONString(
42
- originalURL: string,
43
- originalPayload: IRequestPayload,
44
- preprocessRequest?: PreprocessAnalyticsRequest,
45
- ): Promise<{url: string; payload: IRequestPayload}> {
46
- let returnedUrl = originalURL;
47
- let returnedPayload = originalPayload;
48
-
49
- if (preprocessRequest) {
50
- const processedRequest = await preprocessRequest(
51
- {url: originalURL, body: JSON.stringify(originalPayload)},
52
- 'analyticsBeacon',
53
- );
54
- const {url: processedURL, body: processedBody} = processedRequest;
55
- returnedUrl = processedURL || originalURL;
56
- try {
57
- returnedPayload = JSON.parse(processedBody as string);
58
- } catch (e) {
59
- console.error('Unable to process the request body as a JSON string', e);
60
- }
61
- }
62
-
63
- return {
64
- payload: returnedPayload,
65
- url: returnedUrl,
66
- };
67
- }
68
-
69
- private encodeForEventType(eventType: EventType, payload: IRequestPayload): string {
70
- return this.isEventTypeLegacy(eventType)
71
- ? this.encodeEventToJson(eventType, payload)
72
- : this.encodeEventToJson(eventType, payload, this.opts.token);
73
- }
74
-
75
- private async getQueryParamsForEventType(eventType: EventType): Promise<string> {
76
- const {token, visitorIdProvider} = this.opts;
77
- const visitorId = await visitorIdProvider.getCurrentVisitorId();
78
- return [
79
- token && this.isEventTypeLegacy(eventType) ? `access_token=${token}` : '',
80
- visitorId ? `visitorId=${visitorId}` : '',
81
- 'discardVisitInfo=true',
82
- ]
83
- .filter((p) => !!p)
84
- .join('&');
11
+ public async sendEvent(eventType: EventType, originalPayload: IRequestPayload): Promise<void> {
12
+ if (!this.isAvailable()) {
13
+ throw new Error(
14
+ `navigator.sendBeacon is not supported in this browser. Consider adding a polyfill like "sendbeacon-polyfill".`
15
+ );
85
16
  }
86
17
 
87
- private isEventTypeLegacy(eventType: EventType) {
88
- return [EventType.click, EventType.custom, EventType.search, EventType.view].indexOf(eventType) !== -1;
18
+ const {baseUrl, preprocessRequest} = this.opts;
19
+
20
+ const paramsFragments = await this.getQueryParamsForEventType(eventType);
21
+
22
+ const {url, payload} = await this.preProcessRequestAsPotentialJSONString(
23
+ `${baseUrl}/analytics/${eventType}?${paramsFragments}`,
24
+ originalPayload,
25
+ preprocessRequest
26
+ );
27
+
28
+ const parsedRequestData = this.encodeForEventType(eventType, payload);
29
+ const body = new Blob([parsedRequestData], {
30
+ type: 'application/x-www-form-urlencoded',
31
+ });
32
+
33
+ navigator.sendBeacon(url, body as any); // https://github.com/microsoft/TypeScript/issues/38715
34
+ return;
35
+ }
36
+
37
+ public isAvailable() {
38
+ return 'sendBeacon' in navigator;
39
+ }
40
+
41
+ public deleteHttpCookieVisitorId() {
42
+ return Promise.resolve();
43
+ }
44
+
45
+ private async preProcessRequestAsPotentialJSONString(
46
+ originalURL: string,
47
+ originalPayload: IRequestPayload,
48
+ preprocessRequest?: PreprocessAnalyticsRequest
49
+ ): Promise<{url: string; payload: IRequestPayload}> {
50
+ let returnedUrl = originalURL;
51
+ let returnedPayload = originalPayload;
52
+
53
+ if (preprocessRequest) {
54
+ const processedRequest = await preprocessRequest(
55
+ {url: originalURL, body: JSON.stringify(originalPayload)},
56
+ 'analyticsBeacon'
57
+ );
58
+ const {url: processedURL, body: processedBody} = processedRequest;
59
+ returnedUrl = processedURL || originalURL;
60
+ try {
61
+ returnedPayload = JSON.parse(processedBody as string);
62
+ } catch (e) {
63
+ console.error('Unable to process the request body as a JSON string', e);
64
+ }
89
65
  }
90
66
 
91
- private encodeEventToJson(eventType: EventType, payload: IRequestPayload, access_token?: string): string {
92
- let encoded = `${eventType}Event=${encodeURIComponent(JSON.stringify(payload))}`;
93
- if (access_token) {
94
- encoded = `access_token=${encodeURIComponent(access_token)}&${encoded}`;
95
- }
96
- return encoded;
67
+ return {
68
+ payload: returnedPayload,
69
+ url: returnedUrl,
70
+ };
71
+ }
72
+
73
+ private encodeForEventType(eventType: EventType, payload: IRequestPayload): string {
74
+ return this.isEventTypeLegacy(eventType)
75
+ ? this.encodeEventToJson(eventType, payload)
76
+ : this.encodeEventToJson(eventType, payload, this.opts.token);
77
+ }
78
+
79
+ private async getQueryParamsForEventType(eventType: EventType): Promise<string> {
80
+ const {token, visitorIdProvider} = this.opts;
81
+ const visitorId = await visitorIdProvider.getCurrentVisitorId();
82
+ return [
83
+ token && this.isEventTypeLegacy(eventType) ? `access_token=${token}` : '',
84
+ visitorId ? `visitorId=${visitorId}` : '',
85
+ 'discardVisitInfo=true',
86
+ ]
87
+ .filter((p) => !!p)
88
+ .join('&');
89
+ }
90
+
91
+ private isEventTypeLegacy(eventType: EventType) {
92
+ return (
93
+ [EventType.click, EventType.custom, EventType.search, EventType.view].indexOf(eventType) !==
94
+ -1
95
+ );
96
+ }
97
+
98
+ private encodeEventToJson(
99
+ eventType: EventType,
100
+ payload: IRequestPayload,
101
+ access_token?: string
102
+ ): string {
103
+ let encoded = `${eventType}Event=${encodeURIComponent(JSON.stringify(payload))}`;
104
+ if (access_token) {
105
+ encoded = `access_token=${encodeURIComponent(access_token)}&${encoded}`;
97
106
  }
107
+ return encoded;
108
+ }
98
109
  }