@crimson-education/browser-logger 5.0.1 → 5.0.2

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 (64) hide show
  1. package/README.md +72 -94
  2. package/lib/index.d.ts +1 -1
  3. package/lib/index.d.ts.map +1 -1
  4. package/lib/index.js +23 -46
  5. package/lib/index.js.map +1 -1
  6. package/lib/logger/consoleTransport.d.ts +1 -1
  7. package/lib/logger/consoleTransport.d.ts.map +1 -1
  8. package/lib/logger/consoleTransport.js +16 -22
  9. package/lib/logger/consoleTransport.js.map +1 -1
  10. package/lib/logger/datadogTransport.d.ts +1 -1
  11. package/lib/logger/datadogTransport.d.ts.map +1 -1
  12. package/lib/logger/datadogTransport.js +4 -8
  13. package/lib/logger/datadogTransport.js.map +1 -1
  14. package/lib/logger/index.js +29 -49
  15. package/lib/logger/index.js.map +1 -1
  16. package/lib/logger/index.test.js +16 -18
  17. package/lib/logger/index.test.js.map +1 -1
  18. package/lib/logger/utils.js +4 -9
  19. package/lib/logger/utils.js.map +1 -1
  20. package/lib/reporters/datadogReporter.d.ts +13 -1
  21. package/lib/reporters/datadogReporter.d.ts.map +1 -1
  22. package/lib/reporters/datadogReporter.js +122 -49
  23. package/lib/reporters/datadogReporter.js.map +1 -1
  24. package/lib/reporters/gtmReporter.d.ts +1 -1
  25. package/lib/reporters/gtmReporter.d.ts.map +1 -1
  26. package/lib/reporters/gtmReporter.js +1 -5
  27. package/lib/reporters/gtmReporter.js.map +1 -1
  28. package/lib/reporters/index.js +46 -71
  29. package/lib/reporters/index.js.map +1 -1
  30. package/lib/reporters/logReporter.js +24 -34
  31. package/lib/reporters/logReporter.js.map +1 -1
  32. package/lib/reporters/posthogReporter.d.ts +1 -1
  33. package/lib/reporters/posthogReporter.d.ts.map +1 -1
  34. package/lib/reporters/posthogReporter.js +41 -55
  35. package/lib/reporters/posthogReporter.js.map +1 -1
  36. package/lib/reporters/posthogReporter.test.js +15 -22
  37. package/lib/reporters/posthogReporter.test.js.map +1 -1
  38. package/lib/types/index.js +2 -18
  39. package/lib/types/index.js.map +1 -1
  40. package/lib/types/logger.d.ts +3 -3
  41. package/lib/types/logger.d.ts.map +1 -1
  42. package/lib/types/logger.js +2 -5
  43. package/lib/types/logger.js.map +1 -1
  44. package/lib/types/reporter.d.ts +6 -12
  45. package/lib/types/reporter.d.ts.map +1 -1
  46. package/lib/types/reporter.js +1 -2
  47. package/lib/utils.js +1 -5
  48. package/lib/utils.js.map +1 -1
  49. package/lib/utils.test.js +2 -4
  50. package/lib/utils.test.js.map +1 -1
  51. package/package.json +16 -16
  52. package/src/index.ts +0 -6
  53. package/src/reporters/datadogReporter.ts +133 -22
  54. package/src/types/reporter.ts +0 -7
  55. package/lib/reporters/amplifyReporter.d.ts +0 -86
  56. package/lib/reporters/amplifyReporter.d.ts.map +0 -1
  57. package/lib/reporters/amplifyReporter.js +0 -225
  58. package/lib/reporters/amplifyReporter.js.map +0 -1
  59. package/lib/reporters/amplifyReporter.test.d.ts +0 -2
  60. package/lib/reporters/amplifyReporter.test.d.ts.map +0 -1
  61. package/lib/reporters/amplifyReporter.test.js +0 -51
  62. package/lib/reporters/amplifyReporter.test.js.map +0 -1
  63. package/src/reporters/amplifyReporter.test.ts +0 -61
  64. package/src/reporters/amplifyReporter.ts +0 -344
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crimson-education/browser-logger",
3
- "version": "5.0.1",
3
+ "version": "5.0.2",
4
4
  "description": "An abstract logger and reporting utility for browser environments",
5
5
  "scripts": {
6
6
  "prepack": "npm run build",
@@ -28,25 +28,25 @@
28
28
  },
29
29
  "homepage": "https://github.com/crimson-education/browser-logger#readme",
30
30
  "dependencies": {
31
- "@datadog/browser-logs": "^4.40.0",
32
- "@datadog/browser-rum": "^4.40.0",
31
+ "@datadog/browser-logs": "^6.13.0",
32
+ "@datadog/browser-rum": "^6.13.0",
33
33
  "posthog-js": "^1.396.5"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@crimson-education/eslint-config": "^2.1.0",
37
- "@types/jest": "^27.0.3",
38
- "eslint": "^7.20.0",
39
- "eslint-config-prettier": "^8.3.0",
40
- "eslint-plugin-jest": "^25.3.2",
41
- "jest": "^27.4.3",
42
- "rimraf": "^3.0.2",
43
- "ts-jest": "^27.1.1",
44
- "typescript": "^4.1.2"
45
- },
46
- "peerDependencies": {
47
- "@aws-amplify/analytics": ">=5.2.22",
48
- "@aws-amplify/auth": ">=4.6.8",
49
- "@aws-sdk/client-pinpoint": ">=3.370.0"
37
+ "@types/jest": "^29.5.12",
38
+ "@typescript-eslint/eslint-plugin": "^5.15.0",
39
+ "eslint": "^8.57.0",
40
+ "eslint-config-prettier": "^9.1.0",
41
+ "eslint-plugin-jest": "^28.9.0",
42
+ "eslint-plugin-jsx-a11y": "^6.10.2",
43
+ "eslint-plugin-prettier": "^5.5.6",
44
+ "eslint-plugin-react": "^7.37.5",
45
+ "eslint-plugin-react-hooks": "^7.1.1",
46
+ "jest": "^29.7.0",
47
+ "rimraf": "^5.0.5",
48
+ "ts-jest": "^29.1.2",
49
+ "typescript": "5.1.6"
50
50
  },
51
51
  "files": [
52
52
  "lib",
package/src/index.ts CHANGED
@@ -2,7 +2,6 @@ import { ReporterConfigurations, ServiceInfo } from './types';
2
2
  import { setLogLevel } from './logger';
3
3
  import { addMetadata, reporters, setEventLevel } from './reporters';
4
4
  import { gtmReporter } from './reporters/gtmReporter';
5
- import { amplifyReporter } from './reporters/amplifyReporter';
6
5
  import { datadogReporter } from './reporters/datadogReporter';
7
6
  import { logReporter } from './reporters/logReporter';
8
7
  import { posthogReporter } from './reporters/posthogReporter';
@@ -36,14 +35,9 @@ export function init(config: LoggerConfig) {
36
35
  reporters['datadog'] = datadogReporter(config, config.reporters.datadog);
37
36
  }
38
37
 
39
- if (config.reporters?.amplify) {
40
- reporters['amplify'] = amplifyReporter(config, config.reporters.amplify);
41
- }
42
-
43
38
  if (config.reporters?.posthog) {
44
39
  reporters['posthog'] = posthogReporter(config, config.reporters.posthog);
45
40
  }
46
-
47
41
  if (config.reporters?.gtm) {
48
42
  reporters['gtm'] = gtmReporter(config, typeof config.reporters.gtm === 'boolean' ? {} : config.reporters.gtm);
49
43
  }
@@ -15,6 +15,95 @@ import { datadogRum, DefaultPrivacyLevel, RumInitConfiguration } from '@datadog/
15
15
  import { logTransports } from '../logger';
16
16
  import { DatadogLogTransportConfig, datadogTransport } from '../logger/datadogTransport';
17
17
 
18
+ // User frustration detection for Datadog Gen2
19
+ class DatadogFrustrationDetector {
20
+ private config: DatadogReporterConfig;
21
+ private frustrationEvents: Array<{ type: string; timestamp: number }> = [];
22
+ private readonly FRUSTRATION_THRESHOLD = 3; // Number of events to trigger frustration
23
+ private readonly FRUSTRATION_WINDOW = 5000; // Time window in ms
24
+
25
+ constructor(config: DatadogReporterConfig) {
26
+ this.config = config;
27
+ if (config.trackFrustrations) {
28
+ this.setupFrustrationDetection();
29
+ }
30
+ }
31
+
32
+ private setupFrustrationDetection() {
33
+ // Track rapid clicks (potential frustration)
34
+ let clickCount = 0;
35
+ let lastClickTime = 0;
36
+
37
+ document.addEventListener('click', () => {
38
+ const now = Date.now();
39
+ if (now - lastClickTime < 1000) {
40
+ // Rapid clicks within 1 second
41
+ clickCount++;
42
+ if (clickCount >= 3) {
43
+ this.recordFrustrationEvent('rapid_clicks');
44
+ clickCount = 0;
45
+ }
46
+ } else {
47
+ clickCount = 1;
48
+ }
49
+ lastClickTime = now;
50
+ });
51
+
52
+ // Track form errors (potential frustration)
53
+ document.addEventListener('invalid', () => {
54
+ this.recordFrustrationEvent('form_validation_error');
55
+ });
56
+
57
+ // Track 404 errors (potential frustration)
58
+ window.addEventListener('error', (event) => {
59
+ if (event.message.includes('404') || event.message.includes('Not Found')) {
60
+ this.recordFrustrationEvent('page_not_found');
61
+ }
62
+ });
63
+
64
+ // Track network errors (potential frustration)
65
+ window.addEventListener('unhandledrejection', (event) => {
66
+ if (event.reason && typeof event.reason === 'object' && 'status' in event.reason) {
67
+ const status = (event.reason as any).status;
68
+ if (status >= 400) {
69
+ this.recordFrustrationEvent('network_error');
70
+ }
71
+ }
72
+ });
73
+ }
74
+
75
+ private recordFrustrationEvent(type: string) {
76
+ const now = Date.now();
77
+ this.frustrationEvents.push({ type, timestamp: now });
78
+
79
+ // Clean old events outside the window
80
+ this.frustrationEvents = this.frustrationEvents.filter((event) => now - event.timestamp < this.FRUSTRATION_WINDOW);
81
+
82
+ // Check if we have enough events to trigger frustration
83
+ if (this.frustrationEvents.length >= this.FRUSTRATION_THRESHOLD) {
84
+ this.triggerFrustrationDetection();
85
+ }
86
+ }
87
+
88
+ private triggerFrustrationDetection() {
89
+ const frustrationData = {
90
+ frustrationType: 'user_frustration_detected',
91
+ frustrationEvents: this.frustrationEvents.map((e) => e.type),
92
+ frustrationCount: this.frustrationEvents.length,
93
+ pageUrl: window.location.href,
94
+ timestamp: new Date().toISOString(),
95
+ };
96
+
97
+ // Send frustration event to Datadog
98
+ if (typeof datadogRum !== 'undefined') {
99
+ datadogRum.addAction('User Frustration Detected', frustrationData);
100
+ }
101
+
102
+ // Clear events after reporting
103
+ this.frustrationEvents = [];
104
+ }
105
+ }
106
+
18
107
  export interface DatadogReporterConfig extends ReporterConfigBase {
19
108
  /** The RUM application ID. */
20
109
  applicationId: string;
@@ -36,11 +125,17 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
36
125
  * @deprecated Prefer using the dedicated `logSampleRate`, `rumSampleRate`, and `replaySampleRate` options.
37
126
  */
38
127
  sampleRate?: number;
128
+
129
+ sessionSampleRate?: number;
130
+ sessionReplaySampleRate?: number;
131
+
39
132
  /**
40
133
  * Sampling rate for browser log collection. Defaults to 100, meaning every log message is sent to Datadog.
41
134
  */
42
135
  logSampleRate?: number;
136
+
43
137
  /**
138
+ * @deprecated Prefer using `sessionSampleRate`.
44
139
  * Sampling rate for RUM session collection. Defaults to 100, meaning every browser session is tracked.
45
140
  *
46
141
  * RUM can be used to track things like core web vitals.
@@ -48,7 +143,9 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
48
143
  * @see https://www.datadoghq.com/product/real-user-monitoring/
49
144
  */
50
145
  rumSampleRate?: number;
146
+
51
147
  /**
148
+ * @deprecated Prefer using `sessionReplaySampleRate`.
52
149
  * Applied after the RUM sample rate, and controls the percentage of sessions tracked as Browser RUM & Session Replay.
53
150
  * It defaults to 100, so every session is tracked as Browser RUM & Session Replay by default.
54
151
  *
@@ -60,11 +157,21 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
60
157
  * Use a secure session cookie. This disables RUM events sent on insecure (non-HTTPS) connections.
61
158
  */
62
159
  useSecureSessionCookie?: boolean;
160
+
63
161
  /**
162
+ * @deprecated Prefer using `usePartitionedCrossSiteSessionCookie`.
64
163
  * Use a secure cross-site session cookie.
65
164
  * This allows the RUM Browser SDK to run when the site is loaded from another one (iframe). Implies `useSecureSessionCookie`
66
165
  */
67
166
  useCrossSiteSessionCookie?: boolean;
167
+
168
+ /**
169
+ * Use a partitioned secure cross-site session cookie. This allows the RUM Browser SDK to run when the site is loaded from another one (iframe). Implies `useSecureSessionCookie`.
170
+ * Important: If you are using the RUM and Logs Browser SDKs, this option must be configured with identical values
171
+ * @default false
172
+ */
173
+ usePartitionedCrossSiteSessionCookie?: boolean;
174
+
68
175
  /**
69
176
  * Preserve the session across subdomains for the same site.
70
177
  */
@@ -117,7 +224,8 @@ export interface DatadogReporterConfig extends ReporterConfigBase {
117
224
  /**
118
225
  * A list of request origins used to inject tracing headers, to be able to connect RUM and backend tracing.
119
226
  */
120
- allowedTracingOrigins?: (string | RegExp)[];
227
+ allowedTrackingOrigins?: (string | RegExp)[];
228
+ allowedTracingUrls?: (string | RegExp)[];
121
229
  /**
122
230
  * Enables action tracking for user interactions. This enables the Heatmap tab within Datadog
123
231
  */
@@ -140,23 +248,23 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
140
248
  const enableLogTransport = config.logTransport !== false;
141
249
 
142
250
  // Only init datadog logs if something is using it.
143
- if (forwardErrorsToLogs !== true && enableLogTransport !== true) {
251
+ if (forwardErrorsToLogs === true || enableLogTransport === true) {
144
252
  datadogLogs.init({
145
- site: config.site,
146
- proxyUrl: config.proxyUrl,
253
+ site: config.site as any,
147
254
  clientToken: config.clientToken,
148
255
  service: info.service,
149
256
  env: info.environment,
150
257
  version: config.version ?? info.version,
151
258
 
152
- sampleRate: config.logSampleRate ?? config.sampleRate ?? 100,
153
259
  beforeSend: config.beforeLogsSend,
154
260
 
261
+ usePartitionedCrossSiteSessionCookie: config.usePartitionedCrossSiteSessionCookie,
155
262
  useSecureSessionCookie: config.useSecureSessionCookie ?? !isLocalhost,
156
- useCrossSiteSessionCookie: config.useCrossSiteSessionCookie ?? false,
263
+ // Note: useCrossSiteSessionCookie is not available in newer Datadog versions
157
264
  trackSessionAcrossSubdomains: config.trackSessionAcrossSubdomains,
158
265
 
159
266
  forwardErrorsToLogs,
267
+ allowedTrackingOrigins: config.allowedTrackingOrigins,
160
268
  });
161
269
  datadogLogs.logger.setHandler(HandlerType.http);
162
270
  }
@@ -168,34 +276,37 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
168
276
 
169
277
  datadogRum.init({
170
278
  enableExperimentalFeatures: ['feature_flags'],
171
- site: config.site,
172
- proxyUrl: config.proxyUrl,
279
+ site: config.site as any,
173
280
  clientToken: config.clientToken,
174
281
  applicationId: config.applicationId,
175
282
  service: info.service,
176
283
  env: info.environment,
177
284
  version: config.version ?? info.version,
178
285
 
179
- sampleRate: config.rumSampleRate ?? config.sampleRate ?? 100,
180
- replaySampleRate: config.replaySampleRate ?? 100,
286
+ usePartitionedCrossSiteSessionCookie: config.usePartitionedCrossSiteSessionCookie,
287
+ // Use correct Datadog v6 SDK options
288
+ sessionSampleRate: config.sessionSampleRate ?? config.rumSampleRate ?? config.sampleRate ?? 100,
289
+ sessionReplaySampleRate: config.sessionReplaySampleRate?? config.replaySampleRate ?? 100,
290
+
291
+ // Track interactions (Note: trackFrustrations is not available in Datadog v6)
292
+ trackUserInteractions: config.trackUserInteractions ?? config.trackInteractions ?? false,
181
293
 
182
294
  useSecureSessionCookie: config.useSecureSessionCookie ?? !isLocalhost,
183
- useCrossSiteSessionCookie: config.useCrossSiteSessionCookie ?? false,
295
+ // Note: useCrossSiteSessionCookie is not available in newer Datadog versions
184
296
  trackSessionAcrossSubdomains: config.trackSessionAcrossSubdomains,
185
-
186
- trackInteractions: config.trackInteractions ?? false,
187
- trackFrustrations: config.trackFrustrations ?? false,
188
297
  trackViewsManually: config.trackViewsManually ?? false,
189
298
  actionNameAttribute: config.actionNameAttribute ?? 'data-analytics-name',
190
299
  beforeSend: config.beforeSend,
191
300
 
192
301
  defaultPrivacyLevel: config.defaultPrivacyLevel ?? 'mask-user-input',
193
- allowedTracingOrigins: config.allowedTracingOrigins,
194
- trackUserInteractions: config.trackUserInteractions ?? false,
195
-
302
+ allowedTracingUrls: config.allowedTracingUrls ?? config.allowedTrackingOrigins,
303
+
196
304
  excludedActivityUrls: config.excludedActivityUrls,
197
305
  });
198
306
 
307
+ // Initialize frustration detector
308
+ new DatadogFrustrationDetector(config);
309
+
199
310
  const reporter: IReporter = {
200
311
  trackEvent: function (event: ReporterEvent): void {
201
312
  datadogRum.addAction(event.message, {
@@ -214,16 +325,16 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
214
325
  addMetadata: function (metadata: Metadata): void {
215
326
  for (const [key, value] of Object.entries(metadata)) {
216
327
  if (value !== null) {
217
- datadogRum.addRumGlobalContext(key, value);
328
+ datadogRum.setGlobalContextProperty(key, value);
218
329
 
219
330
  // Note, this will add duplicate context data in logs.
220
331
  // But this is valuable for logs ingested outside of the browser logger.
221
- datadogLogs.addLoggerGlobalContext(key, value);
332
+ datadogLogs.setGlobalContextProperty(key, value);
222
333
  } else {
223
- datadogRum.removeRumGlobalContext(key);
334
+ datadogRum.removeGlobalContextProperty(key);
224
335
 
225
336
  // But this is valuable for logs ingested outside of the browser logger.
226
- datadogLogs.removeLoggerGlobalContext(key);
337
+ datadogLogs.removeGlobalContextProperty(key);
227
338
  }
228
339
  }
229
340
  },
@@ -236,7 +347,7 @@ export function datadogReporter(info: ServiceInfo, config: DatadogReporterConfig
236
347
  name: user.name ?? user.email,
237
348
  });
238
349
  } else {
239
- datadogRum.removeUser();
350
+ datadogRum.setUser({});
240
351
  }
241
352
  },
242
353
  setRouteName: function (routeName: string): void {
@@ -1,4 +1,3 @@
1
- import { AmplifyReporterConfig } from '../reporters/amplifyReporter';
2
1
  import { DatadogReporterConfig } from '../reporters/datadogReporter';
3
2
  import { GTMReporterConfig } from '../reporters/gtmReporter';
4
3
  import { LogReporterConfig } from '../reporters/logReporter';
@@ -18,12 +17,6 @@ export type ReporterConfigurations = {
18
17
  */
19
18
  datadog?: DatadogReporterConfig;
20
19
 
21
- /**
22
- * Amplify/Pinpoint reporter configuration.
23
- * If not set, the amplify reporter will not be used.
24
- */
25
- amplify?: AmplifyReporterConfig;
26
-
27
20
  /**
28
21
  * PostHog reporter configuration.
29
22
  * If not set, the PostHog reporter will not be used.
@@ -1,86 +0,0 @@
1
- import { IReporter, Metadata, ReporterConfigBase, ServiceInfo } from '../types';
2
- declare type AttributeMap = Record<string, string[] | string | null>;
3
- declare type AmplifyAutoTrackSource = 'pageView' | 'event' | 'session';
4
- export interface AmplifyReporterConfig extends ReporterConfigBase {
5
- /**
6
- * AWS Region for Amplify.
7
- */
8
- region: string;
9
- /**
10
- * The Identity Pool Id to use for reporting, if set to false, Auth.configure is not called.
11
- * This must be called manually for the reporter to work.
12
- */
13
- identityPoolId: string | false;
14
- /**
15
- * The Pinpoint App Id to report to.
16
- */
17
- analyticsAppId: string;
18
- /** Optional proxy URL */
19
- proxyUrl?: string;
20
- /**
21
- * The Cognito User Pool to configure in Auth.configure.
22
- * If you are using Cognito, it is better to set identityPoolId to false and configure Auth manually.
23
- */
24
- userPoolId?: string;
25
- /**
26
- * The Cognito Web Client Id to configure in Auth.configure.
27
- * If you are using Cognito, it is better to set identityPoolId to false and configure Auth manually.
28
- */
29
- userPoolWebClientId?: string;
30
- /**
31
- * If you want to track which page/url in your webapp is the most frequently viewed one, you can use this feature.
32
- * It will automatically send events containing url information when the page is visited.
33
- */
34
- autoTrackPageViews?: boolean;
35
- /**
36
- * If you want to track user interactions with elements on the page, you can use this feature.
37
- * All you need to do is attach the specified selectors to your dom element and turn on the auto tracking.
38
- */
39
- autoTrackEvents?: boolean;
40
- /**
41
- * A web session can be defined in different ways.
42
- * To keep it simple we define that the web session is active when the page is not hidden and inactive when the page is hidden.
43
- */
44
- autoTrackSessions?: boolean;
45
- /**
46
- * Optional function to run before autotracked analytics events are sent out.
47
- * The returned metadata is attached to the event.
48
- */
49
- beforeAutoTrack?(source: AmplifyAutoTrackSource): Metadata | undefined;
50
- /**
51
- * The data tag prefix to use for attributing HTML elements. Defaults to data-analytics-
52
- */
53
- selectorPrefix?: string;
54
- /**
55
- * Modify how the reporter sends events to Amplify.
56
- */
57
- buffering?: AmplifyReporterBufferingConfig;
58
- }
59
- /**
60
- * Configuration options for the buffering behavior of Pinpoint's event tracker.
61
- *
62
- * @see https://docs.amplify.aws/lib/analytics/getting-started/q/platform/js/#set-up-existing-analytics-backend
63
- */
64
- declare type AmplifyReporterBufferingConfig = {
65
- /** Number of items to buffer for sending. */
66
- bufferSize?: number;
67
- /** Number of events sent each time Pinpoint flushes. */
68
- flushSize?: number;
69
- /** Interval Pinpoint flushes analytics events. Measured in milliseconds. */
70
- flushInterval?: number;
71
- /** The maximum number of times Pinpoint will retry to send an event. */
72
- resendLimit?: number;
73
- };
74
- export declare function amplifyReporter(info: ServiceInfo, config: AmplifyReporterConfig): IReporter;
75
- /**
76
- * Pinpoint has strict attribute name and value length limits
77
- */
78
- export declare function asAttributeMap(values: Record<string, unknown>, groupValues?: boolean): AttributeMap;
79
- /**
80
- * Pinpoint expects `endpoint.attributes` and `endpoint.userAttributes` to have
81
- * values which are string arrays. This function takes in an object and ensures
82
- * all of its values are of type `string[]` to appease Pinpoint.
83
- */
84
- export declare function buildAttributeMap(values: Record<string, any>, parentKey?: string | undefined, groupValues?: boolean): AttributeMap;
85
- export {};
86
- //# sourceMappingURL=amplifyReporter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"amplifyReporter.d.ts","sourceRoot":"","sources":["../../src/reporters/amplifyReporter.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,SAAS,EACT,QAAQ,EAER,kBAAkB,EAGlB,WAAW,EACZ,MAAM,UAAU,CAAC;AAIlB,aAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC;AAE7D,aAAK,sBAAsB,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAE/D,MAAM,WAAW,qBAAsB,SAAQ,kBAAkB;IAC/D;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,KAAK,CAAC;IAC/B;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;OAGG;IACH,eAAe,CAAC,CAAC,MAAM,EAAE,sBAAsB,GAAG,QAAQ,GAAG,SAAS,CAAC;IAEvE;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,SAAS,CAAC,EAAE,8BAA8B,CAAC;CAC5C;AAED;;;;GAIG;AACH,aAAK,8BAA8B,GAAG;IACpC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,qBAAqB,GAAG,SAAS,CA8H3F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,UAAO,GAAG,YAAY,CAqBhG;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,SAAS,GAAE,MAAM,GAAG,SAAqB,EACzC,WAAW,UAAO,GACjB,YAAY,CAsBd"}
@@ -1,225 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildAttributeMap = exports.asAttributeMap = exports.amplifyReporter = void 0;
4
- const auth_1 = require("@aws-amplify/auth");
5
- const analytics_1 = require("@aws-amplify/analytics");
6
- const protocol_http_1 = require("@aws-sdk/protocol-http");
7
- const logger_1 = require("../logger");
8
- function amplifyReporter(info, config) {
9
- var _a;
10
- if (config.identityPoolId !== false) {
11
- auth_1.Auth.configure({
12
- region: config.region,
13
- identityPoolId: config.identityPoolId,
14
- userPoolId: config.userPoolId,
15
- userPoolWebClientId: config.userPoolWebClientId,
16
- });
17
- }
18
- const wrapAutoTrackMiddleware = (source) => {
19
- const { beforeAutoTrack } = config;
20
- return typeof beforeAutoTrack === 'function' ? () => { var _a; return (_a = beforeAutoTrack(source)) !== null && _a !== void 0 ? _a : {}; } : undefined;
21
- };
22
- const allMetadata = asAttributeMap({
23
- appName: info.service,
24
- service: info.service,
25
- domain: window.location.host,
26
- environment: info.environment,
27
- version: info.version,
28
- });
29
- analytics_1.Analytics.configure({
30
- region: config.region,
31
- appId: config.analyticsAppId,
32
- endpoint: {
33
- attributes: allMetadata,
34
- },
35
- ...config.buffering,
36
- });
37
- // Session autotracking is enabled by default for backwards compatibility reasons, so we _must_
38
- // call this unconditionally to ensure we opt out of session tracking when `autoTrackSessions` isn't set
39
- // See: https://docs.amplify.aws/lib/analytics/autotrack/q/platform/js/#session-tracking
40
- analytics_1.Analytics.autoTrack('session', {
41
- enable: config.autoTrackSessions === true,
42
- attributes: wrapAutoTrackMiddleware('session'),
43
- });
44
- if (config.autoTrackPageViews) {
45
- analytics_1.Analytics.autoTrack('pageView', {
46
- enable: true,
47
- eventName: 'pageView',
48
- type: 'SPA',
49
- provider: 'AWSPinpoint',
50
- attributes: wrapAutoTrackMiddleware('pageView'),
51
- });
52
- }
53
- if (config.autoTrackEvents) {
54
- analytics_1.Analytics.autoTrack('event', {
55
- enable: true,
56
- selectorPrefix: (_a = config.selectorPrefix) !== null && _a !== void 0 ? _a : 'data-analytics-',
57
- attributes: wrapAutoTrackMiddleware('event'),
58
- });
59
- }
60
- if (config.proxyUrl) {
61
- installPinpointProxy(new URL(config.proxyUrl));
62
- }
63
- const reporter = {
64
- trackEvent: function (event) {
65
- analytics_1.Analytics.record({
66
- name: event.message,
67
- attributes: asAttributeMap({
68
- ...event.metadata,
69
- ...event.tags,
70
- }, false),
71
- metrics: event.metrics,
72
- }).catch(() => {
73
- // Swallow; see: https://crimsonhq.slack.com/archives/G4UN6Q4KF/p1648599302847539
74
- });
75
- },
76
- addBreadcrumb: function (breadcrumb) {
77
- reporter.trackEvent({
78
- message: breadcrumb.message,
79
- metadata: {
80
- category: breadcrumb.category,
81
- ...breadcrumb.metadata,
82
- },
83
- });
84
- },
85
- addMetadata: function (metadata) {
86
- Object.assign(allMetadata, asAttributeMap(metadata, true));
87
- analytics_1.Analytics.updateEndpoint({
88
- attributes: allMetadata,
89
- }).catch(() => {
90
- // Swallow; see: https://crimsonhq.slack.com/archives/G4UN6Q4KF/p1648599302847539
91
- });
92
- },
93
- setUser: function (user) {
94
- var _a, _b;
95
- const userMetadata = user
96
- ? asAttributeMap({
97
- userId: user.id,
98
- })
99
- : {};
100
- Object.assign(allMetadata, asAttributeMap(userMetadata, true));
101
- analytics_1.Analytics.updateEndpoint({
102
- userId: (_a = user === null || user === void 0 ? void 0 : user.id) !== null && _a !== void 0 ? _a : '',
103
- attributes: allMetadata,
104
- userAttributes: user
105
- ? asAttributeMap({
106
- id: user.id,
107
- email: user.email,
108
- name: (_b = user.name) !== null && _b !== void 0 ? _b : user.email,
109
- username: user.username,
110
- })
111
- : {},
112
- }).catch(() => {
113
- // Swallow; see: https://crimsonhq.slack.com/archives/G4UN6Q4KF/p1648599302847539
114
- });
115
- },
116
- setRouteName: function (routeName) {
117
- reporter.addMetadata({ routeName });
118
- },
119
- setPageName: function (pageName) {
120
- reporter.addMetadata({ pageName });
121
- },
122
- };
123
- return reporter;
124
- }
125
- exports.amplifyReporter = amplifyReporter;
126
- /**
127
- * Pinpoint has strict attribute name and value length limits
128
- */
129
- function asAttributeMap(values, groupValues = true) {
130
- const attributeMap = buildAttributeMap(values, undefined, groupValues);
131
- const checkedEntries = Object.entries(attributeMap).map(([key, value]) => {
132
- var _a, _b;
133
- const truncatedKey = key.length > 50 ? `___${key.slice(-47)}` : key;
134
- const truncatedValue = Array.isArray(value)
135
- ? (_a = value === null || value === void 0 ? void 0 : value.map((val) => val.slice(0, 100))) !== null && _a !== void 0 ? _a : null
136
- : (_b = value === null || value === void 0 ? void 0 : value.slice(0, 100)) !== null && _b !== void 0 ? _b : null;
137
- return [truncatedKey, truncatedValue];
138
- });
139
- // Pinpoint only accepts 40 attributes
140
- if (checkedEntries.length > 40) {
141
- logger_1.logger.error(`Amplify only allows 40 attributes per event, truncating to 40 attributes`, {
142
- attributes: checkedEntries,
143
- });
144
- checkedEntries.length = 40;
145
- }
146
- return Object.fromEntries(checkedEntries);
147
- }
148
- exports.asAttributeMap = asAttributeMap;
149
- /**
150
- * Pinpoint expects `endpoint.attributes` and `endpoint.userAttributes` to have
151
- * values which are string arrays. This function takes in an object and ensures
152
- * all of its values are of type `string[]` to appease Pinpoint.
153
- */
154
- function buildAttributeMap(values, parentKey = undefined, groupValues = true) {
155
- const valuesWithStringArrays = {};
156
- Object.entries(values).forEach(([key, value]) => {
157
- const combinedKey = parentKey ? [parentKey, key].join('.') : key;
158
- if (!value) {
159
- valuesWithStringArrays[combinedKey] = null;
160
- }
161
- else if (groupValues && Array.isArray(value)) {
162
- valuesWithStringArrays[combinedKey] = value.map((element) => typeof element === 'string' ? element : JSON.stringify(element));
163
- }
164
- else if (typeof value === 'object') {
165
- const flattenedAttribute = buildAttributeMap(value, combinedKey, groupValues);
166
- Object.assign(valuesWithStringArrays, flattenedAttribute);
167
- }
168
- else {
169
- const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
170
- valuesWithStringArrays[combinedKey] = groupValues ? [stringValue] : stringValue;
171
- }
172
- });
173
- return valuesWithStringArrays;
174
- }
175
- exports.buildAttributeMap = buildAttributeMap;
176
- function installPinpointProxy(proxyUrl) {
177
- // No public API for overriding where the Pinpoint client sends events to... 🤮
178
- // In theory you can pass in an `endpoint` to the Pinpoint client's constructor like any other AWS
179
- // client, but Amplify's analytics doesn't expose anything we can use to get an endpoint threaded
180
- // down to the Pinpoint client's constructor.
181
- //
182
- // The Pinpoint client _also_ isn't available synchronously because it is instantiated when events
183
- // get sent out, and then reconfigured whenever the API credentials change. We need to hook `_initClients`
184
- // to ensure that the Pinpoint client being used is always patched with our custom endpoint.
185
- const provider = analytics_1.Analytics.getPluggable('AWSPinpoint');
186
- if (!provider || typeof provider._initClients !== 'function') {
187
- logger_1.logger.error('Installation of the Pinpoint proxy failed. This likely means the internals of the @aws-amplify/analytics package have changed.');
188
- return;
189
- }
190
- const originalInitClients = provider._initClients;
191
- const requestMiddleware = (next) => async (args) => {
192
- const pinpointClient = provider.pinpointClient;
193
- if (pinpointClient && proxyUrl.pathname !== '/' && protocol_http_1.HttpRequest.isInstance(args.request)) {
194
- // Add proxyUrl.pathname to final request url if it was provided
195
- const shouldStripSlash = proxyUrl.pathname.endsWith('/');
196
- args.request.path = `${proxyUrl.pathname}${args.request.path.slice(shouldStripSlash ? 1 : 0)}`;
197
- // Wrap request body so the proxy has signing info
198
- if (typeof args.request.body === 'string') {
199
- const credentials = await pinpointClient.config.credentials();
200
- args.request.body = JSON.stringify({
201
- credentials,
202
- data: JSON.parse(args.request.body),
203
- });
204
- }
205
- }
206
- return next(args);
207
- };
208
- provider._initClients = async (credentials) => {
209
- const result = await originalInitClients.call(provider, credentials);
210
- const pinpointClient = provider.pinpointClient;
211
- if (pinpointClient) {
212
- pinpointClient.config.endpoint = () => Promise.resolve({
213
- hostname: proxyUrl.hostname,
214
- // Passing proxyUrl.pathname here doesn't work; it gets overridden
215
- path: '/',
216
- port: undefined,
217
- protocol: proxyUrl.protocol,
218
- });
219
- pinpointClient.middlewareStack.remove(requestMiddleware);
220
- pinpointClient.middlewareStack.add(requestMiddleware, { step: 'finalizeRequest' });
221
- }
222
- return result;
223
- };
224
- }
225
- //# sourceMappingURL=amplifyReporter.js.map