@amplitude/plugin-network-capture-browser 1.4.5 → 1.5.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.
@@ -1,3 +1,4 @@
1
1
  export declare const PLUGIN_NAME = "@amplitude/plugin-network-capture-browser";
2
2
  export declare const AMPLITUDE_NETWORK_REQUEST_EVENT = "[Amplitude] Network Request";
3
+ export declare const IS_HEADER_CAPTURE_EXPERIMENTAL = true;
3
4
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,8CAA8C,CAAC;AAEvE,eAAO,MAAM,+BAA+B,gCAAgC,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,8CAA8C,CAAC;AAEvE,eAAO,MAAM,+BAA+B,gCAAgC,CAAC;AAI7E,eAAO,MAAM,8BAA8B,OAAO,CAAC"}
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AMPLITUDE_NETWORK_REQUEST_EVENT = exports.PLUGIN_NAME = void 0;
3
+ exports.IS_HEADER_CAPTURE_EXPERIMENTAL = exports.AMPLITUDE_NETWORK_REQUEST_EVENT = exports.PLUGIN_NAME = void 0;
4
4
  exports.PLUGIN_NAME = '@amplitude/plugin-network-capture-browser';
5
5
  exports.AMPLITUDE_NETWORK_REQUEST_EVENT = '[Amplitude] Network Request';
6
+ // TODO: when @experimental is removed from responseHeaders and requestHeaders,
7
+ // remove this constant or set it to false
8
+ exports.IS_HEADER_CAPTURE_EXPERIMENTAL = true;
6
9
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,WAAW,GAAG,2CAA2C,CAAC;AAE1D,QAAA,+BAA+B,GAAG,6BAA6B,CAAC","sourcesContent":["export const PLUGIN_NAME = '@amplitude/plugin-network-capture-browser';\n\nexport const AMPLITUDE_NETWORK_REQUEST_EVENT = '[Amplitude] Network Request';\n"]}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,WAAW,GAAG,2CAA2C,CAAC;AAE1D,QAAA,+BAA+B,GAAG,6BAA6B,CAAC;AAE7E,+EAA+E;AAC/E,0CAA0C;AAC7B,QAAA,8BAA8B,GAAG,IAAI,CAAC","sourcesContent":["export const PLUGIN_NAME = '@amplitude/plugin-network-capture-browser';\n\nexport const AMPLITUDE_NETWORK_REQUEST_EVENT = '[Amplitude] Network Request';\n\n// TODO: when @experimental is removed from responseHeaders and requestHeaders,\n// remove this constant or set it to false\nexport const IS_HEADER_CAPTURE_EXPERIMENTAL = true;\n"]}
@@ -1,5 +1,13 @@
1
- import { BrowserClient, NetworkRequestEvent, NetworkTrackingOptions } from '@amplitude/analytics-core';
1
+ import { BrowserClient, NetworkRequestEvent, NetworkTrackingOptions, JsonObject } from '@amplitude/analytics-core';
2
2
  import { AllWindowObservables } from './network-capture-plugin';
3
+ /**
4
+ * Takes a user provided header capture rule and returns a
5
+ * HeaderCaptureRule object that sets proper default values.
6
+ *
7
+ * @param rule - The header capture rule to parse.
8
+ * @returns A HeaderCaptureRule object.
9
+ */
10
+ export declare function parseHeaderCaptureRule(rule: string[] | boolean | undefined | null): string[] | undefined;
3
11
  export declare function shouldTrackNetworkEvent(networkEvent: NetworkRequestEvent, options?: NetworkTrackingOptions): boolean;
4
12
  export type NetworkAnalyticsEvent = {
5
13
  ['[Amplitude] URL']: string;
@@ -11,9 +19,14 @@ export type NetworkAnalyticsEvent = {
11
19
  ['[Amplitude] Completion Time']?: number;
12
20
  ['[Amplitude] Duration']?: number;
13
21
  ['[Amplitude] Request Body Size']?: number;
22
+ ['[Amplitude] Request Headers']?: Record<string, string>;
23
+ ['[Amplitude] Request Body']?: JsonObject;
14
24
  ['[Amplitude] Response Body Size']?: number;
25
+ ['[Amplitude] Response Headers']?: Record<string, string>;
26
+ ['[Amplitude] Response Body']?: JsonObject;
15
27
  ['[Amplitude] Request Type']?: 'xhr' | 'fetch';
16
28
  };
29
+ export declare function logNetworkAnalyticsEvent(networkAnalyticsEvent: NetworkAnalyticsEvent, request: NetworkRequestEvent, amplitude: BrowserClient): Promise<void>;
17
30
  export declare function trackNetworkEvents({ allObservables, networkTrackingOptions, amplitude, }: {
18
31
  allObservables: AllWindowObservables;
19
32
  networkTrackingOptions: NetworkTrackingOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"track-network-event.d.ts","sourceRoot":"","sources":["../../src/track-network-event.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,mBAAmB,EAEnB,sBAAsB,EAEvB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,oBAAoB,EAAoB,MAAM,0BAA0B,CAAC;AAwFlF,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,mBAAmB,EAAE,OAAO,GAAE,sBAA2B,WAuD9G;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC5B,CAAC,uBAAuB,CAAC,CAAC,EAAE,MAAM,CAAC;IACnC,CAAC,0BAA0B,CAAC,CAAC,EAAE,MAAM,CAAC;IACtC,CAAC,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACvC,CAAC,yBAAyB,CAAC,CAAC,EAAE,MAAM,CAAC;IACrC,CAAC,wBAAwB,CAAC,CAAC,EAAE,MAAM,CAAC;IACpC,CAAC,6BAA6B,CAAC,CAAC,EAAE,MAAM,CAAC;IACzC,CAAC,sBAAsB,CAAC,CAAC,EAAE,MAAM,CAAC;IAClC,CAAC,+BAA+B,CAAC,CAAC,EAAE,MAAM,CAAC;IAC3C,CAAC,gCAAgC,CAAC,CAAC,EAAE,MAAM,CAAC;IAC5C,CAAC,0BAA0B,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CAChD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EACjC,cAAc,EACd,sBAAsB,EACtB,SAAS,GACV,EAAE;IACD,cAAc,EAAE,oBAAoB,CAAC;IACrC,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,SAAS,EAAE,aAAa,CAAC;CAC1B,+BA6CA"}
1
+ {"version":3,"file":"track-network-event.d.ts","sourceRoot":"","sources":["../../src/track-network-event.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,mBAAmB,EAEnB,sBAAsB,EAEtB,UAAU,EAGX,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,oBAAoB,EAAoB,MAAM,0BAA0B,CAAC;AA+GlF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,EAAE,GAAG,SAAS,CAmBxG;AAOD,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,mBAAmB,EAAE,OAAO,GAAE,sBAA2B,WA2F9G;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC5B,CAAC,uBAAuB,CAAC,CAAC,EAAE,MAAM,CAAC;IACnC,CAAC,0BAA0B,CAAC,CAAC,EAAE,MAAM,CAAC;IACtC,CAAC,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACvC,CAAC,yBAAyB,CAAC,CAAC,EAAE,MAAM,CAAC;IACrC,CAAC,wBAAwB,CAAC,CAAC,EAAE,MAAM,CAAC;IACpC,CAAC,6BAA6B,CAAC,CAAC,EAAE,MAAM,CAAC;IACzC,CAAC,sBAAsB,CAAC,CAAC,EAAE,MAAM,CAAC;IAClC,CAAC,+BAA+B,CAAC,CAAC,EAAE,MAAM,CAAC;IAC3C,CAAC,6BAA6B,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,CAAC,0BAA0B,CAAC,CAAC,EAAE,UAAU,CAAC;IAC1C,CAAC,gCAAgC,CAAC,CAAC,EAAE,MAAM,CAAC;IAC5C,CAAC,8BAA8B,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1D,CAAC,2BAA2B,CAAC,CAAC,EAAE,UAAU,CAAC;IAC3C,CAAC,0BAA0B,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CAChD,CAAC;AAEF,wBAAsB,wBAAwB,CAC5C,qBAAqB,EAAE,qBAAqB,EAC5C,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,aAAa,iBAazB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,cAAc,EACd,sBAAsB,EACtB,SAAS,GACV,EAAE;IACD,cAAc,EAAE,oBAAoB,CAAC;IACrC,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,SAAS,EAAE,aAAa,CAAC;CAC1B,+BA+CA"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.trackNetworkEvents = exports.shouldTrackNetworkEvent = void 0;
3
+ exports.trackNetworkEvents = exports.logNetworkAnalyticsEvent = exports.shouldTrackNetworkEvent = exports.parseHeaderCaptureRule = void 0;
4
4
  var tslib_1 = require("tslib");
5
5
  var analytics_core_1 = require("@amplitude/analytics-core");
6
6
  var rxjs_1 = require("rxjs");
@@ -38,11 +38,21 @@ function isStatusCodeInRange(statusCode, range) {
38
38
  }
39
39
  return false;
40
40
  }
41
- function isCaptureRuleMatch(rule, hostname, status) {
41
+ function isCaptureRuleMatch(rule, hostname, status, url, method) {
42
42
  // check if the host is in the allowed hosts
43
43
  if (rule.hosts && !rule.hosts.find(function (host) { return wildcardMatch(hostname, host); })) {
44
44
  return;
45
45
  }
46
+ // check if the URL is in the allowed URL patterns
47
+ if (url && rule.urls && !(0, analytics_core_1.isUrlMatchAllowlist)(url, rule.urls)) {
48
+ return;
49
+ }
50
+ // check if the method is in the allowed methods
51
+ if (method &&
52
+ rule.methods &&
53
+ !rule.methods.find(function (allowedMethod) { return method.toLowerCase() === allowedMethod.toLowerCase() || allowedMethod === '*'; })) {
54
+ return;
55
+ }
46
56
  // check if the status code is in the allowed range
47
57
  if (status || status === 0) {
48
58
  var statusCodeRange = rule.statusCodeRange || DEFAULT_STATUS_CODE_RANGE;
@@ -95,6 +105,38 @@ function isAmplitudeNetworkRequestEvent(host, requestWrapper) {
95
105
  }
96
106
  return false;
97
107
  }
108
+ /**
109
+ * Takes a user provided header capture rule and returns a
110
+ * HeaderCaptureRule object that sets proper default values.
111
+ *
112
+ * @param rule - The header capture rule to parse.
113
+ * @returns A HeaderCaptureRule object.
114
+ */
115
+ function parseHeaderCaptureRule(rule) {
116
+ if (typeof rule !== 'object' || rule === null) {
117
+ // if rule is truthy or undefined, return SAFE_HEADERS
118
+ if (rule) {
119
+ return tslib_1.__spreadArray([], tslib_1.__read(analytics_core_1.SAFE_HEADERS), false);
120
+ }
121
+ else if (rule === undefined) {
122
+ /* istanbul ignore next */
123
+ var res = constants_1.IS_HEADER_CAPTURE_EXPERIMENTAL ? undefined : tslib_1.__spreadArray([], tslib_1.__read(analytics_core_1.SAFE_HEADERS), false);
124
+ return res;
125
+ }
126
+ return;
127
+ }
128
+ // if the rule is defined but empty, return undefined
129
+ if (rule.length === 0) {
130
+ return;
131
+ }
132
+ return rule;
133
+ }
134
+ exports.parseHeaderCaptureRule = parseHeaderCaptureRule;
135
+ function isBodyCaptureRuleEmpty(rule) {
136
+ var _a, _b;
137
+ /* istanbul ignore next */
138
+ return !((_a = rule === null || rule === void 0 ? void 0 : rule.allowlist) === null || _a === void 0 ? void 0 : _a.length) && !((_b = rule === null || rule === void 0 ? void 0 : rule.blocklist) === null || _b === void 0 ? void 0 : _b.length);
139
+ }
98
140
  function shouldTrackNetworkEvent(networkEvent, options) {
99
141
  var _a;
100
142
  if (options === void 0) { options = {}; }
@@ -127,7 +169,32 @@ function shouldTrackNetworkEvent(networkEvent, options) {
127
169
  // that is a match (true) or a miss (false)
128
170
  var isMatch_1;
129
171
  tslib_1.__spreadArray([], tslib_1.__read(options.captureRules), false).reverse().find(function (rule) {
130
- isMatch_1 = isCaptureRuleMatch(rule, host, networkEvent.status);
172
+ isMatch_1 = isCaptureRuleMatch(rule, host, networkEvent.status, networkEvent.url, networkEvent.method);
173
+ if (isMatch_1) {
174
+ var responseHeadersRule = parseHeaderCaptureRule(rule.responseHeaders);
175
+ if (networkEvent.responseWrapper && responseHeadersRule) {
176
+ var responseHeaders = networkEvent.responseWrapper.headers(responseHeadersRule);
177
+ if (responseHeaders) {
178
+ networkEvent.responseHeaders = responseHeaders;
179
+ }
180
+ }
181
+ // if requestHeaders rule is specified, enrich the event with the request headers
182
+ var requestHeadersRule = parseHeaderCaptureRule(rule.requestHeaders);
183
+ if (networkEvent.requestWrapper && requestHeadersRule) {
184
+ var requestHeaders = networkEvent.requestWrapper.headers(requestHeadersRule);
185
+ if (requestHeaders) {
186
+ networkEvent.requestHeaders = requestHeaders;
187
+ }
188
+ }
189
+ // if responseBody rule is specified, enrich the event with the response body
190
+ if (networkEvent.responseWrapper && rule.responseBody && !isBodyCaptureRuleEmpty(rule.responseBody)) {
191
+ networkEvent.responseBodyJson = networkEvent.responseWrapper.json(rule.responseBody.allowlist, rule.responseBody.blocklist);
192
+ }
193
+ // if requestBody rule is specified, enrich the event with the request body
194
+ if (networkEvent.requestWrapper && rule.requestBody && !isBodyCaptureRuleEmpty(rule.requestBody)) {
195
+ networkEvent.requestBodyJson = networkEvent.requestWrapper.json(rule.requestBody.allowlist, rule.requestBody.blocklist);
196
+ }
197
+ }
131
198
  return isMatch_1 !== undefined;
132
199
  });
133
200
  // if we found a miss (false) or no match (undefined),
@@ -143,6 +210,32 @@ function shouldTrackNetworkEvent(networkEvent, options) {
143
210
  return true;
144
211
  }
145
212
  exports.shouldTrackNetworkEvent = shouldTrackNetworkEvent;
213
+ function logNetworkAnalyticsEvent(networkAnalyticsEvent, request, amplitude) {
214
+ return tslib_1.__awaiter(this, void 0, void 0, function () {
215
+ var _a, requestBody, responseBody;
216
+ return tslib_1.__generator(this, function (_b) {
217
+ switch (_b.label) {
218
+ case 0:
219
+ if (!(request.requestBodyJson || request.responseBodyJson)) return [3 /*break*/, 2];
220
+ return [4 /*yield*/, Promise.all([request.requestBodyJson, request.responseBodyJson])];
221
+ case 1:
222
+ _a = tslib_1.__read.apply(void 0, [_b.sent(), 2]), requestBody = _a[0], responseBody = _a[1];
223
+ if (requestBody) {
224
+ networkAnalyticsEvent['[Amplitude] Request Body'] = requestBody;
225
+ }
226
+ if (responseBody) {
227
+ networkAnalyticsEvent['[Amplitude] Response Body'] = responseBody;
228
+ }
229
+ _b.label = 2;
230
+ case 2:
231
+ /* istanbul ignore next */
232
+ amplitude === null || amplitude === void 0 ? void 0 : amplitude.track(constants_1.AMPLITUDE_NETWORK_REQUEST_EVENT, networkAnalyticsEvent);
233
+ return [2 /*return*/];
234
+ }
235
+ });
236
+ });
237
+ }
238
+ exports.logNetworkAnalyticsEvent = logNetworkAnalyticsEvent;
146
239
  function trackNetworkEvents(_a) {
147
240
  var allObservables = _a.allObservables, networkTrackingOptions = _a.networkTrackingOptions, amplitude = _a.amplitude;
148
241
  var networkObservable = allObservables.networkObservable;
@@ -179,9 +272,11 @@ function trackNetworkEvents(_a) {
179
272
  _a['[Amplitude] Request Body Size'] = requestBodySize,
180
273
  _a['[Amplitude] Response Body Size'] = responseBodySize,
181
274
  _a['[Amplitude] Request Type'] = request.type,
275
+ _a['[Amplitude] Request Headers'] = request.requestHeaders,
276
+ _a['[Amplitude] Response Headers'] = request.responseHeaders,
182
277
  _a);
183
- /* istanbul ignore next */
184
- amplitude === null || amplitude === void 0 ? void 0 : amplitude.track(constants_1.AMPLITUDE_NETWORK_REQUEST_EVENT, networkAnalyticsEvent);
278
+ // fire-and-forget promise that tracks the event
279
+ void logNetworkAnalyticsEvent(networkAnalyticsEvent, request, amplitude);
185
280
  });
186
281
  }
187
282
  exports.trackNetworkEvents = trackNetworkEvents;
@@ -1 +1 @@
1
- {"version":3,"file":"track-network-event.js","sourceRoot":"","sources":["../../src/track-network-event.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BrowserClient,\n NetworkRequestEvent,\n NetworkCaptureRule,\n NetworkTrackingOptions,\n getGlobalScope,\n} from '@amplitude/analytics-core';\nimport { filter } from 'rxjs';\nimport { AllWindowObservables, TimestampedEvent } from './network-capture-plugin';\nimport { AMPLITUDE_NETWORK_REQUEST_EVENT } from './constants';\nimport { IRequestWrapper } from '@amplitude/analytics-core';\n\nconst DEFAULT_STATUS_CODE_RANGE = '500-599';\n\nfunction wildcardMatch(str: string, pattern: string) {\n // Escape all regex special characters except for *\n const escapedPattern = pattern.replace(/[-[\\]{}()+?.,\\\\^$|#\\s]/g, '\\\\$&');\n // Replace * with .*\n const regexPattern = '^' + escapedPattern.replace(/\\*/g, '.*') + '$';\n const regex = new RegExp(regexPattern);\n return regex.test(str);\n}\n\nfunction isStatusCodeInRange(statusCode: number, range: string) {\n const ranges = range.split(',');\n for (const r of ranges) {\n const [start, end] = r.split('-').map(Number);\n if (statusCode === start && end === undefined) {\n return true;\n }\n if (statusCode >= start && statusCode <= end) {\n return true;\n }\n }\n return false;\n}\n\nfunction isCaptureRuleMatch(rule: NetworkCaptureRule, hostname: string, status?: number) {\n // check if the host is in the allowed hosts\n if (rule.hosts && !rule.hosts.find((host: string) => wildcardMatch(hostname, host))) {\n return;\n }\n\n // check if the status code is in the allowed range\n if (status || status === 0) {\n const statusCodeRange = rule.statusCodeRange || DEFAULT_STATUS_CODE_RANGE;\n if (!isStatusCodeInRange(status, statusCodeRange)) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction parseUrl(url: string | undefined) {\n if (!url) {\n return;\n }\n try {\n /* istanbul ignore next */\n const currentHref = getGlobalScope()?.location.href;\n const urlObj = new URL(url, currentHref);\n const query = urlObj.searchParams.toString();\n const fragment = urlObj.hash.replace('#', '');\n const href = urlObj.href;\n const host = urlObj.host;\n urlObj.hash = '';\n urlObj.search = '';\n const hrefWithoutQueryOrHash = urlObj.href;\n return { query, fragment, href, hrefWithoutQueryOrHash, host };\n } catch (e) {\n /* istanbul ignore next */\n return;\n }\n}\n\nfunction isAmplitudeNetworkRequestEvent(host: string, requestWrapper: IRequestWrapper): boolean {\n if (host.includes('amplitude.com')) {\n try {\n const body = requestWrapper.body;\n if (typeof body !== 'string') {\n return false;\n }\n const bodyObj = JSON.parse(body) as { events: any[] };\n const { events } = bodyObj;\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */\n if (events.find((event: any) => event.event_type === AMPLITUDE_NETWORK_REQUEST_EVENT)) {\n return true;\n }\n } catch (e) {\n // do nothing\n }\n }\n return false;\n}\n\nexport function shouldTrackNetworkEvent(networkEvent: NetworkRequestEvent, options: NetworkTrackingOptions = {}) {\n const urlObj = parseUrl(networkEvent.url);\n /* istanbul ignore if */\n if (!urlObj) {\n // if the URL failed to parse, do not track the event\n // this is a probably impossible case that would only happen if the URL is malformed\n /* istanbul ignore next */\n return false;\n }\n const { host } = urlObj;\n\n // false if is amplitude request and not configured to track amplitude requests\n if (\n options.ignoreAmplitudeRequests !== false &&\n (wildcardMatch(host, '*.amplitude.com') || wildcardMatch(host, 'amplitude.com'))\n ) {\n return false;\n }\n\n // false if the host is in the ignore list\n if (options.ignoreHosts?.find((ignoreHost: string) => wildcardMatch(host, ignoreHost))) {\n return false;\n }\n\n // false if the status code is not 500-599 and there are no captureRules\n if (\n !options.captureRules &&\n networkEvent.status !== undefined &&\n !isStatusCodeInRange(networkEvent.status, DEFAULT_STATUS_CODE_RANGE)\n ) {\n return false;\n }\n\n if (options.captureRules) {\n // find the first capture rule, in reverse-order,\n // that is a match (true) or a miss (false)\n let isMatch: boolean | undefined;\n [...options.captureRules].reverse().find((rule) => {\n isMatch = isCaptureRuleMatch(rule, host, networkEvent.status);\n return isMatch !== undefined;\n });\n\n // if we found a miss (false) or no match (undefined),\n // then do not track the event\n if (!isMatch) {\n return false;\n }\n }\n\n // skip Amplitude network requests to \"[Amplitude] Network Request\" to avoid infinite loop\n if (networkEvent.requestWrapper && isAmplitudeNetworkRequestEvent(host, networkEvent.requestWrapper)) {\n return false;\n }\n\n return true;\n}\n\nexport type NetworkAnalyticsEvent = {\n ['[Amplitude] URL']: string;\n ['[Amplitude] URL Query']?: string;\n ['[Amplitude] URL Fragment']?: string;\n ['[Amplitude] Request Method']: string;\n ['[Amplitude] Status Code']?: number;\n ['[Amplitude] Start Time']?: number; // unix timestamp\n ['[Amplitude] Completion Time']?: number; // unix timestamp\n ['[Amplitude] Duration']?: number; // completionTime - startTime (millis)\n ['[Amplitude] Request Body Size']?: number;\n ['[Amplitude] Response Body Size']?: number;\n ['[Amplitude] Request Type']?: 'xhr' | 'fetch';\n};\n\nexport function trackNetworkEvents({\n allObservables,\n networkTrackingOptions,\n amplitude,\n}: {\n allObservables: AllWindowObservables;\n networkTrackingOptions: NetworkTrackingOptions;\n amplitude: BrowserClient;\n}) {\n const { networkObservable } = allObservables;\n\n const filteredNetworkObservable = networkObservable.pipe(\n filter((event: TimestampedEvent<NetworkRequestEvent>) => {\n // Only track network events that should be tracked,\n return shouldTrackNetworkEvent(event.event as NetworkRequestEvent, networkTrackingOptions);\n }),\n );\n\n return filteredNetworkObservable.subscribe((networkEvent) => {\n const request = networkEvent.event;\n\n // convert to NetworkAnalyticsEvent\n const urlObj = parseUrl(request.url);\n /* istanbul ignore if */\n if (!urlObj) {\n // if the URL failed to parse, do not track the event\n // this is a very unlikely case, because URL() shouldn't throw an exception\n // when the URL is a valid URL\n /* istanbul ignore next */\n return;\n }\n\n const responseBodySize = request.responseWrapper?.bodySize;\n /* istanbul ignore next */\n const requestBodySize = request.requestWrapper?.bodySize;\n\n const networkAnalyticsEvent: NetworkAnalyticsEvent = {\n ['[Amplitude] URL']: urlObj.hrefWithoutQueryOrHash,\n ['[Amplitude] URL Query']: urlObj.query,\n ['[Amplitude] URL Fragment']: urlObj.fragment,\n ['[Amplitude] Request Method']: request.method,\n ['[Amplitude] Status Code']: request.status,\n ['[Amplitude] Start Time']: request.startTime,\n ['[Amplitude] Completion Time']: request.endTime,\n ['[Amplitude] Duration']: request.duration,\n ['[Amplitude] Request Body Size']: requestBodySize,\n ['[Amplitude] Response Body Size']: responseBodySize,\n ['[Amplitude] Request Type']: request.type,\n };\n\n /* istanbul ignore next */\n amplitude?.track(AMPLITUDE_NETWORK_REQUEST_EVENT, networkAnalyticsEvent);\n });\n}\n"]}
1
+ {"version":3,"file":"track-network-event.js","sourceRoot":"","sources":["../../src/track-network-event.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BrowserClient,\n NetworkRequestEvent,\n NetworkCaptureRule,\n NetworkTrackingOptions,\n getGlobalScope,\n JsonObject,\n isUrlMatchAllowlist,\n SAFE_HEADERS,\n} from '@amplitude/analytics-core';\nimport { filter } from 'rxjs';\nimport { AllWindowObservables, TimestampedEvent } from './network-capture-plugin';\nimport { AMPLITUDE_NETWORK_REQUEST_EVENT, IS_HEADER_CAPTURE_EXPERIMENTAL } from './constants';\nimport { IRequestWrapper } from '@amplitude/analytics-core';\nimport { BodyCaptureRule } from '@amplitude/analytics-core/lib/esm/types/network-tracking';\n\nconst DEFAULT_STATUS_CODE_RANGE = '500-599';\n\nfunction wildcardMatch(str: string, pattern: string) {\n // Escape all regex special characters except for *\n const escapedPattern = pattern.replace(/[-[\\]{}()+?.,\\\\^$|#\\s]/g, '\\\\$&');\n // Replace * with .*\n const regexPattern = '^' + escapedPattern.replace(/\\*/g, '.*') + '$';\n const regex = new RegExp(regexPattern);\n return regex.test(str);\n}\n\nfunction isStatusCodeInRange(statusCode: number, range: string) {\n const ranges = range.split(',');\n for (const r of ranges) {\n const [start, end] = r.split('-').map(Number);\n if (statusCode === start && end === undefined) {\n return true;\n }\n if (statusCode >= start && statusCode <= end) {\n return true;\n }\n }\n return false;\n}\n\nfunction isCaptureRuleMatch(\n rule: NetworkCaptureRule,\n hostname: string,\n status?: number,\n url?: string,\n method?: string,\n) {\n // check if the host is in the allowed hosts\n if (rule.hosts && !rule.hosts.find((host: string) => wildcardMatch(hostname, host))) {\n return;\n }\n\n // check if the URL is in the allowed URL patterns\n if (url && rule.urls && !isUrlMatchAllowlist(url, rule.urls)) {\n return;\n }\n\n // check if the method is in the allowed methods\n if (\n method &&\n rule.methods &&\n !rule.methods.find(\n (allowedMethod: string) => method.toLowerCase() === allowedMethod.toLowerCase() || allowedMethod === '*',\n )\n ) {\n return;\n }\n\n // check if the status code is in the allowed range\n if (status || status === 0) {\n const statusCodeRange = rule.statusCodeRange || DEFAULT_STATUS_CODE_RANGE;\n if (!isStatusCodeInRange(status, statusCodeRange)) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction parseUrl(url: string | undefined) {\n if (!url) {\n return;\n }\n try {\n /* istanbul ignore next */\n const currentHref = getGlobalScope()?.location.href;\n const urlObj = new URL(url, currentHref);\n const query = urlObj.searchParams.toString();\n const fragment = urlObj.hash.replace('#', '');\n const href = urlObj.href;\n const host = urlObj.host;\n urlObj.hash = '';\n urlObj.search = '';\n const hrefWithoutQueryOrHash = urlObj.href;\n return { query, fragment, href, hrefWithoutQueryOrHash, host };\n } catch (e) {\n /* istanbul ignore next */\n return;\n }\n}\n\nfunction isAmplitudeNetworkRequestEvent(host: string, requestWrapper: IRequestWrapper): boolean {\n if (host.includes('amplitude.com')) {\n try {\n const body = requestWrapper.body;\n if (typeof body !== 'string') {\n return false;\n }\n const bodyObj = JSON.parse(body) as { events: any[] };\n const { events } = bodyObj;\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */\n if (events.find((event: any) => event.event_type === AMPLITUDE_NETWORK_REQUEST_EVENT)) {\n return true;\n }\n } catch (e) {\n // do nothing\n }\n }\n return false;\n}\n\n/**\n * Takes a user provided header capture rule and returns a\n * HeaderCaptureRule object that sets proper default values.\n *\n * @param rule - The header capture rule to parse.\n * @returns A HeaderCaptureRule object.\n */\nexport function parseHeaderCaptureRule(rule: string[] | boolean | undefined | null): string[] | undefined {\n if (typeof rule !== 'object' || rule === null) {\n // if rule is truthy or undefined, return SAFE_HEADERS\n if (rule) {\n return [...SAFE_HEADERS];\n } else if (rule === undefined) {\n /* istanbul ignore next */\n const res = IS_HEADER_CAPTURE_EXPERIMENTAL ? undefined : [...SAFE_HEADERS];\n return res;\n }\n return;\n }\n\n // if the rule is defined but empty, return undefined\n if (rule.length === 0) {\n return;\n }\n\n return rule;\n}\n\nfunction isBodyCaptureRuleEmpty(rule: BodyCaptureRule) {\n /* istanbul ignore next */\n return !rule?.allowlist?.length && !rule?.blocklist?.length;\n}\n\nexport function shouldTrackNetworkEvent(networkEvent: NetworkRequestEvent, options: NetworkTrackingOptions = {}) {\n const urlObj = parseUrl(networkEvent.url);\n /* istanbul ignore if */\n if (!urlObj) {\n // if the URL failed to parse, do not track the event\n // this is a probably impossible case that would only happen if the URL is malformed\n /* istanbul ignore next */\n return false;\n }\n const { host } = urlObj;\n\n // false if is amplitude request and not configured to track amplitude requests\n if (\n options.ignoreAmplitudeRequests !== false &&\n (wildcardMatch(host, '*.amplitude.com') || wildcardMatch(host, 'amplitude.com'))\n ) {\n return false;\n }\n\n // false if the host is in the ignore list\n if (options.ignoreHosts?.find((ignoreHost: string) => wildcardMatch(host, ignoreHost))) {\n return false;\n }\n\n // false if the status code is not 500-599 and there are no captureRules\n if (\n !options.captureRules &&\n networkEvent.status !== undefined &&\n !isStatusCodeInRange(networkEvent.status, DEFAULT_STATUS_CODE_RANGE)\n ) {\n return false;\n }\n\n if (options.captureRules) {\n // find the first capture rule, in reverse-order,\n // that is a match (true) or a miss (false)\n let isMatch: boolean | undefined;\n [...options.captureRules].reverse().find((rule) => {\n isMatch = isCaptureRuleMatch(rule, host, networkEvent.status, networkEvent.url, networkEvent.method);\n\n if (isMatch) {\n const responseHeadersRule = parseHeaderCaptureRule(rule.responseHeaders);\n if (networkEvent.responseWrapper && responseHeadersRule) {\n const responseHeaders = networkEvent.responseWrapper.headers(responseHeadersRule);\n if (responseHeaders) {\n networkEvent.responseHeaders = responseHeaders;\n }\n }\n\n // if requestHeaders rule is specified, enrich the event with the request headers\n const requestHeadersRule = parseHeaderCaptureRule(rule.requestHeaders);\n if (networkEvent.requestWrapper && requestHeadersRule) {\n const requestHeaders = networkEvent.requestWrapper.headers(requestHeadersRule);\n if (requestHeaders) {\n networkEvent.requestHeaders = requestHeaders;\n }\n }\n\n // if responseBody rule is specified, enrich the event with the response body\n if (networkEvent.responseWrapper && rule.responseBody && !isBodyCaptureRuleEmpty(rule.responseBody)) {\n networkEvent.responseBodyJson = networkEvent.responseWrapper.json(\n rule.responseBody.allowlist,\n rule.responseBody.blocklist,\n );\n }\n\n // if requestBody rule is specified, enrich the event with the request body\n if (networkEvent.requestWrapper && rule.requestBody && !isBodyCaptureRuleEmpty(rule.requestBody)) {\n networkEvent.requestBodyJson = networkEvent.requestWrapper.json(\n rule.requestBody.allowlist,\n rule.requestBody.blocklist,\n );\n }\n }\n\n return isMatch !== undefined;\n });\n\n // if we found a miss (false) or no match (undefined),\n // then do not track the event\n if (!isMatch) {\n return false;\n }\n }\n\n // skip Amplitude network requests to \"[Amplitude] Network Request\" to avoid infinite loop\n if (networkEvent.requestWrapper && isAmplitudeNetworkRequestEvent(host, networkEvent.requestWrapper)) {\n return false;\n }\n\n return true;\n}\n\nexport type NetworkAnalyticsEvent = {\n ['[Amplitude] URL']: string;\n ['[Amplitude] URL Query']?: string;\n ['[Amplitude] URL Fragment']?: string;\n ['[Amplitude] Request Method']: string;\n ['[Amplitude] Status Code']?: number;\n ['[Amplitude] Start Time']?: number; // unix timestamp\n ['[Amplitude] Completion Time']?: number; // unix timestamp\n ['[Amplitude] Duration']?: number; // completionTime - startTime (millis)\n ['[Amplitude] Request Body Size']?: number;\n ['[Amplitude] Request Headers']?: Record<string, string>;\n ['[Amplitude] Request Body']?: JsonObject;\n ['[Amplitude] Response Body Size']?: number;\n ['[Amplitude] Response Headers']?: Record<string, string>;\n ['[Amplitude] Response Body']?: JsonObject;\n ['[Amplitude] Request Type']?: 'xhr' | 'fetch';\n};\n\nexport async function logNetworkAnalyticsEvent(\n networkAnalyticsEvent: NetworkAnalyticsEvent,\n request: NetworkRequestEvent,\n amplitude: BrowserClient,\n) {\n if (request.requestBodyJson || request.responseBodyJson) {\n const [requestBody, responseBody] = await Promise.all([request.requestBodyJson, request.responseBodyJson]);\n if (requestBody) {\n networkAnalyticsEvent['[Amplitude] Request Body'] = requestBody;\n }\n if (responseBody) {\n networkAnalyticsEvent['[Amplitude] Response Body'] = responseBody;\n }\n }\n /* istanbul ignore next */\n amplitude?.track(AMPLITUDE_NETWORK_REQUEST_EVENT, networkAnalyticsEvent);\n}\n\nexport function trackNetworkEvents({\n allObservables,\n networkTrackingOptions,\n amplitude,\n}: {\n allObservables: AllWindowObservables;\n networkTrackingOptions: NetworkTrackingOptions;\n amplitude: BrowserClient;\n}) {\n const { networkObservable } = allObservables;\n\n const filteredNetworkObservable = networkObservable.pipe(\n filter((event: TimestampedEvent<NetworkRequestEvent>) => {\n // Only track network events that should be tracked,\n return shouldTrackNetworkEvent(event.event as NetworkRequestEvent, networkTrackingOptions);\n }),\n );\n\n return filteredNetworkObservable.subscribe((networkEvent) => {\n const request = networkEvent.event;\n\n // convert to NetworkAnalyticsEvent\n const urlObj = parseUrl(request.url);\n /* istanbul ignore if */\n if (!urlObj) {\n // if the URL failed to parse, do not track the event\n // this is a very unlikely case, because URL() shouldn't throw an exception\n // when the URL is a valid URL\n /* istanbul ignore next */\n return;\n }\n\n const responseBodySize = request.responseWrapper?.bodySize;\n /* istanbul ignore next */\n const requestBodySize = request.requestWrapper?.bodySize;\n\n const networkAnalyticsEvent: NetworkAnalyticsEvent = {\n ['[Amplitude] URL']: urlObj.hrefWithoutQueryOrHash,\n ['[Amplitude] URL Query']: urlObj.query,\n ['[Amplitude] URL Fragment']: urlObj.fragment,\n ['[Amplitude] Request Method']: request.method,\n ['[Amplitude] Status Code']: request.status,\n ['[Amplitude] Start Time']: request.startTime,\n ['[Amplitude] Completion Time']: request.endTime,\n ['[Amplitude] Duration']: request.duration,\n ['[Amplitude] Request Body Size']: requestBodySize,\n ['[Amplitude] Response Body Size']: responseBodySize,\n ['[Amplitude] Request Type']: request.type,\n ['[Amplitude] Request Headers']: request.requestHeaders,\n ['[Amplitude] Response Headers']: request.responseHeaders,\n };\n\n // fire-and-forget promise that tracks the event\n void logNetworkAnalyticsEvent(networkAnalyticsEvent, request, amplitude);\n });\n}\n"]}
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "1.4.5";
1
+ export declare const VERSION = "1.5.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = '1.4.5';
4
+ exports.VERSION = '1.5.0';
5
5
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":";;;AAAa,QAAA,OAAO,GAAG,OAAO,CAAC","sourcesContent":["export const VERSION = '1.4.5';\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":";;;AAAa,QAAA,OAAO,GAAG,OAAO,CAAC","sourcesContent":["export const VERSION = '1.5.0';\n"]}
@@ -1,3 +1,4 @@
1
1
  export declare const PLUGIN_NAME = "@amplitude/plugin-network-capture-browser";
2
2
  export declare const AMPLITUDE_NETWORK_REQUEST_EVENT = "[Amplitude] Network Request";
3
+ export declare const IS_HEADER_CAPTURE_EXPERIMENTAL = true;
3
4
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,8CAA8C,CAAC;AAEvE,eAAO,MAAM,+BAA+B,gCAAgC,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,8CAA8C,CAAC;AAEvE,eAAO,MAAM,+BAA+B,gCAAgC,CAAC;AAI7E,eAAO,MAAM,8BAA8B,OAAO,CAAC"}
@@ -1,3 +1,6 @@
1
1
  export var PLUGIN_NAME = '@amplitude/plugin-network-capture-browser';
2
2
  export var AMPLITUDE_NETWORK_REQUEST_EVENT = '[Amplitude] Network Request';
3
+ // TODO: when @experimental is removed from responseHeaders and requestHeaders,
4
+ // remove this constant or set it to false
5
+ export var IS_HEADER_CAPTURE_EXPERIMENTAL = true;
3
6
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,IAAM,WAAW,GAAG,2CAA2C,CAAC;AAEvE,MAAM,CAAC,IAAM,+BAA+B,GAAG,6BAA6B,CAAC","sourcesContent":["export const PLUGIN_NAME = '@amplitude/plugin-network-capture-browser';\n\nexport const AMPLITUDE_NETWORK_REQUEST_EVENT = '[Amplitude] Network Request';\n"]}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,IAAM,WAAW,GAAG,2CAA2C,CAAC;AAEvE,MAAM,CAAC,IAAM,+BAA+B,GAAG,6BAA6B,CAAC;AAE7E,+EAA+E;AAC/E,0CAA0C;AAC1C,MAAM,CAAC,IAAM,8BAA8B,GAAG,IAAI,CAAC","sourcesContent":["export const PLUGIN_NAME = '@amplitude/plugin-network-capture-browser';\n\nexport const AMPLITUDE_NETWORK_REQUEST_EVENT = '[Amplitude] Network Request';\n\n// TODO: when @experimental is removed from responseHeaders and requestHeaders,\n// remove this constant or set it to false\nexport const IS_HEADER_CAPTURE_EXPERIMENTAL = true;\n"]}
@@ -1,5 +1,13 @@
1
- import { BrowserClient, NetworkRequestEvent, NetworkTrackingOptions } from '@amplitude/analytics-core';
1
+ import { BrowserClient, NetworkRequestEvent, NetworkTrackingOptions, JsonObject } from '@amplitude/analytics-core';
2
2
  import { AllWindowObservables } from './network-capture-plugin';
3
+ /**
4
+ * Takes a user provided header capture rule and returns a
5
+ * HeaderCaptureRule object that sets proper default values.
6
+ *
7
+ * @param rule - The header capture rule to parse.
8
+ * @returns A HeaderCaptureRule object.
9
+ */
10
+ export declare function parseHeaderCaptureRule(rule: string[] | boolean | undefined | null): string[] | undefined;
3
11
  export declare function shouldTrackNetworkEvent(networkEvent: NetworkRequestEvent, options?: NetworkTrackingOptions): boolean;
4
12
  export type NetworkAnalyticsEvent = {
5
13
  ['[Amplitude] URL']: string;
@@ -11,9 +19,14 @@ export type NetworkAnalyticsEvent = {
11
19
  ['[Amplitude] Completion Time']?: number;
12
20
  ['[Amplitude] Duration']?: number;
13
21
  ['[Amplitude] Request Body Size']?: number;
22
+ ['[Amplitude] Request Headers']?: Record<string, string>;
23
+ ['[Amplitude] Request Body']?: JsonObject;
14
24
  ['[Amplitude] Response Body Size']?: number;
25
+ ['[Amplitude] Response Headers']?: Record<string, string>;
26
+ ['[Amplitude] Response Body']?: JsonObject;
15
27
  ['[Amplitude] Request Type']?: 'xhr' | 'fetch';
16
28
  };
29
+ export declare function logNetworkAnalyticsEvent(networkAnalyticsEvent: NetworkAnalyticsEvent, request: NetworkRequestEvent, amplitude: BrowserClient): Promise<void>;
17
30
  export declare function trackNetworkEvents({ allObservables, networkTrackingOptions, amplitude, }: {
18
31
  allObservables: AllWindowObservables;
19
32
  networkTrackingOptions: NetworkTrackingOptions;
@@ -1 +1 @@
1
- {"version":3,"file":"track-network-event.d.ts","sourceRoot":"","sources":["../../src/track-network-event.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,mBAAmB,EAEnB,sBAAsB,EAEvB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,oBAAoB,EAAoB,MAAM,0BAA0B,CAAC;AAwFlF,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,mBAAmB,EAAE,OAAO,GAAE,sBAA2B,WAuD9G;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC5B,CAAC,uBAAuB,CAAC,CAAC,EAAE,MAAM,CAAC;IACnC,CAAC,0BAA0B,CAAC,CAAC,EAAE,MAAM,CAAC;IACtC,CAAC,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACvC,CAAC,yBAAyB,CAAC,CAAC,EAAE,MAAM,CAAC;IACrC,CAAC,wBAAwB,CAAC,CAAC,EAAE,MAAM,CAAC;IACpC,CAAC,6BAA6B,CAAC,CAAC,EAAE,MAAM,CAAC;IACzC,CAAC,sBAAsB,CAAC,CAAC,EAAE,MAAM,CAAC;IAClC,CAAC,+BAA+B,CAAC,CAAC,EAAE,MAAM,CAAC;IAC3C,CAAC,gCAAgC,CAAC,CAAC,EAAE,MAAM,CAAC;IAC5C,CAAC,0BAA0B,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CAChD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EACjC,cAAc,EACd,sBAAsB,EACtB,SAAS,GACV,EAAE;IACD,cAAc,EAAE,oBAAoB,CAAC;IACrC,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,SAAS,EAAE,aAAa,CAAC;CAC1B,+BA6CA"}
1
+ {"version":3,"file":"track-network-event.d.ts","sourceRoot":"","sources":["../../src/track-network-event.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,mBAAmB,EAEnB,sBAAsB,EAEtB,UAAU,EAGX,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,oBAAoB,EAAoB,MAAM,0BAA0B,CAAC;AA+GlF;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,EAAE,GAAG,SAAS,CAmBxG;AAOD,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,mBAAmB,EAAE,OAAO,GAAE,sBAA2B,WA2F9G;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC5B,CAAC,uBAAuB,CAAC,CAAC,EAAE,MAAM,CAAC;IACnC,CAAC,0BAA0B,CAAC,CAAC,EAAE,MAAM,CAAC;IACtC,CAAC,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACvC,CAAC,yBAAyB,CAAC,CAAC,EAAE,MAAM,CAAC;IACrC,CAAC,wBAAwB,CAAC,CAAC,EAAE,MAAM,CAAC;IACpC,CAAC,6BAA6B,CAAC,CAAC,EAAE,MAAM,CAAC;IACzC,CAAC,sBAAsB,CAAC,CAAC,EAAE,MAAM,CAAC;IAClC,CAAC,+BAA+B,CAAC,CAAC,EAAE,MAAM,CAAC;IAC3C,CAAC,6BAA6B,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,CAAC,0BAA0B,CAAC,CAAC,EAAE,UAAU,CAAC;IAC1C,CAAC,gCAAgC,CAAC,CAAC,EAAE,MAAM,CAAC;IAC5C,CAAC,8BAA8B,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1D,CAAC,2BAA2B,CAAC,CAAC,EAAE,UAAU,CAAC;IAC3C,CAAC,0BAA0B,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CAChD,CAAC;AAEF,wBAAsB,wBAAwB,CAC5C,qBAAqB,EAAE,qBAAqB,EAC5C,OAAO,EAAE,mBAAmB,EAC5B,SAAS,EAAE,aAAa,iBAazB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,cAAc,EACd,sBAAsB,EACtB,SAAS,GACV,EAAE;IACD,cAAc,EAAE,oBAAoB,CAAC;IACrC,sBAAsB,EAAE,sBAAsB,CAAC;IAC/C,SAAS,EAAE,aAAa,CAAC;CAC1B,+BA+CA"}
@@ -1,7 +1,7 @@
1
- import { __read, __spreadArray, __values } from "tslib";
2
- import { getGlobalScope, } from '@amplitude/analytics-core';
1
+ import { __awaiter, __generator, __read, __spreadArray, __values } from "tslib";
2
+ import { getGlobalScope, isUrlMatchAllowlist, SAFE_HEADERS, } from '@amplitude/analytics-core';
3
3
  import { filter } from 'rxjs';
4
- import { AMPLITUDE_NETWORK_REQUEST_EVENT } from './constants';
4
+ import { AMPLITUDE_NETWORK_REQUEST_EVENT, IS_HEADER_CAPTURE_EXPERIMENTAL } from './constants';
5
5
  var DEFAULT_STATUS_CODE_RANGE = '500-599';
6
6
  function wildcardMatch(str, pattern) {
7
7
  // Escape all regex special characters except for *
@@ -35,11 +35,21 @@ function isStatusCodeInRange(statusCode, range) {
35
35
  }
36
36
  return false;
37
37
  }
38
- function isCaptureRuleMatch(rule, hostname, status) {
38
+ function isCaptureRuleMatch(rule, hostname, status, url, method) {
39
39
  // check if the host is in the allowed hosts
40
40
  if (rule.hosts && !rule.hosts.find(function (host) { return wildcardMatch(hostname, host); })) {
41
41
  return;
42
42
  }
43
+ // check if the URL is in the allowed URL patterns
44
+ if (url && rule.urls && !isUrlMatchAllowlist(url, rule.urls)) {
45
+ return;
46
+ }
47
+ // check if the method is in the allowed methods
48
+ if (method &&
49
+ rule.methods &&
50
+ !rule.methods.find(function (allowedMethod) { return method.toLowerCase() === allowedMethod.toLowerCase() || allowedMethod === '*'; })) {
51
+ return;
52
+ }
43
53
  // check if the status code is in the allowed range
44
54
  if (status || status === 0) {
45
55
  var statusCodeRange = rule.statusCodeRange || DEFAULT_STATUS_CODE_RANGE;
@@ -92,6 +102,37 @@ function isAmplitudeNetworkRequestEvent(host, requestWrapper) {
92
102
  }
93
103
  return false;
94
104
  }
105
+ /**
106
+ * Takes a user provided header capture rule and returns a
107
+ * HeaderCaptureRule object that sets proper default values.
108
+ *
109
+ * @param rule - The header capture rule to parse.
110
+ * @returns A HeaderCaptureRule object.
111
+ */
112
+ export function parseHeaderCaptureRule(rule) {
113
+ if (typeof rule !== 'object' || rule === null) {
114
+ // if rule is truthy or undefined, return SAFE_HEADERS
115
+ if (rule) {
116
+ return __spreadArray([], __read(SAFE_HEADERS), false);
117
+ }
118
+ else if (rule === undefined) {
119
+ /* istanbul ignore next */
120
+ var res = IS_HEADER_CAPTURE_EXPERIMENTAL ? undefined : __spreadArray([], __read(SAFE_HEADERS), false);
121
+ return res;
122
+ }
123
+ return;
124
+ }
125
+ // if the rule is defined but empty, return undefined
126
+ if (rule.length === 0) {
127
+ return;
128
+ }
129
+ return rule;
130
+ }
131
+ function isBodyCaptureRuleEmpty(rule) {
132
+ var _a, _b;
133
+ /* istanbul ignore next */
134
+ return !((_a = rule === null || rule === void 0 ? void 0 : rule.allowlist) === null || _a === void 0 ? void 0 : _a.length) && !((_b = rule === null || rule === void 0 ? void 0 : rule.blocklist) === null || _b === void 0 ? void 0 : _b.length);
135
+ }
95
136
  export function shouldTrackNetworkEvent(networkEvent, options) {
96
137
  var _a;
97
138
  if (options === void 0) { options = {}; }
@@ -124,7 +165,32 @@ export function shouldTrackNetworkEvent(networkEvent, options) {
124
165
  // that is a match (true) or a miss (false)
125
166
  var isMatch_1;
126
167
  __spreadArray([], __read(options.captureRules), false).reverse().find(function (rule) {
127
- isMatch_1 = isCaptureRuleMatch(rule, host, networkEvent.status);
168
+ isMatch_1 = isCaptureRuleMatch(rule, host, networkEvent.status, networkEvent.url, networkEvent.method);
169
+ if (isMatch_1) {
170
+ var responseHeadersRule = parseHeaderCaptureRule(rule.responseHeaders);
171
+ if (networkEvent.responseWrapper && responseHeadersRule) {
172
+ var responseHeaders = networkEvent.responseWrapper.headers(responseHeadersRule);
173
+ if (responseHeaders) {
174
+ networkEvent.responseHeaders = responseHeaders;
175
+ }
176
+ }
177
+ // if requestHeaders rule is specified, enrich the event with the request headers
178
+ var requestHeadersRule = parseHeaderCaptureRule(rule.requestHeaders);
179
+ if (networkEvent.requestWrapper && requestHeadersRule) {
180
+ var requestHeaders = networkEvent.requestWrapper.headers(requestHeadersRule);
181
+ if (requestHeaders) {
182
+ networkEvent.requestHeaders = requestHeaders;
183
+ }
184
+ }
185
+ // if responseBody rule is specified, enrich the event with the response body
186
+ if (networkEvent.responseWrapper && rule.responseBody && !isBodyCaptureRuleEmpty(rule.responseBody)) {
187
+ networkEvent.responseBodyJson = networkEvent.responseWrapper.json(rule.responseBody.allowlist, rule.responseBody.blocklist);
188
+ }
189
+ // if requestBody rule is specified, enrich the event with the request body
190
+ if (networkEvent.requestWrapper && rule.requestBody && !isBodyCaptureRuleEmpty(rule.requestBody)) {
191
+ networkEvent.requestBodyJson = networkEvent.requestWrapper.json(rule.requestBody.allowlist, rule.requestBody.blocklist);
192
+ }
193
+ }
128
194
  return isMatch_1 !== undefined;
129
195
  });
130
196
  // if we found a miss (false) or no match (undefined),
@@ -139,6 +205,31 @@ export function shouldTrackNetworkEvent(networkEvent, options) {
139
205
  }
140
206
  return true;
141
207
  }
208
+ export function logNetworkAnalyticsEvent(networkAnalyticsEvent, request, amplitude) {
209
+ return __awaiter(this, void 0, void 0, function () {
210
+ var _a, requestBody, responseBody;
211
+ return __generator(this, function (_b) {
212
+ switch (_b.label) {
213
+ case 0:
214
+ if (!(request.requestBodyJson || request.responseBodyJson)) return [3 /*break*/, 2];
215
+ return [4 /*yield*/, Promise.all([request.requestBodyJson, request.responseBodyJson])];
216
+ case 1:
217
+ _a = __read.apply(void 0, [_b.sent(), 2]), requestBody = _a[0], responseBody = _a[1];
218
+ if (requestBody) {
219
+ networkAnalyticsEvent['[Amplitude] Request Body'] = requestBody;
220
+ }
221
+ if (responseBody) {
222
+ networkAnalyticsEvent['[Amplitude] Response Body'] = responseBody;
223
+ }
224
+ _b.label = 2;
225
+ case 2:
226
+ /* istanbul ignore next */
227
+ amplitude === null || amplitude === void 0 ? void 0 : amplitude.track(AMPLITUDE_NETWORK_REQUEST_EVENT, networkAnalyticsEvent);
228
+ return [2 /*return*/];
229
+ }
230
+ });
231
+ });
232
+ }
142
233
  export function trackNetworkEvents(_a) {
143
234
  var allObservables = _a.allObservables, networkTrackingOptions = _a.networkTrackingOptions, amplitude = _a.amplitude;
144
235
  var networkObservable = allObservables.networkObservable;
@@ -175,9 +266,11 @@ export function trackNetworkEvents(_a) {
175
266
  _a['[Amplitude] Request Body Size'] = requestBodySize,
176
267
  _a['[Amplitude] Response Body Size'] = responseBodySize,
177
268
  _a['[Amplitude] Request Type'] = request.type,
269
+ _a['[Amplitude] Request Headers'] = request.requestHeaders,
270
+ _a['[Amplitude] Response Headers'] = request.responseHeaders,
178
271
  _a);
179
- /* istanbul ignore next */
180
- amplitude === null || amplitude === void 0 ? void 0 : amplitude.track(AMPLITUDE_NETWORK_REQUEST_EVENT, networkAnalyticsEvent);
272
+ // fire-and-forget promise that tracks the event
273
+ void logNetworkAnalyticsEvent(networkAnalyticsEvent, request, amplitude);
181
274
  });
182
275
  }
183
276
  //# sourceMappingURL=track-network-event.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"track-network-event.js","sourceRoot":"","sources":["../../src/track-network-event.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BrowserClient,\n NetworkRequestEvent,\n NetworkCaptureRule,\n NetworkTrackingOptions,\n getGlobalScope,\n} from '@amplitude/analytics-core';\nimport { filter } from 'rxjs';\nimport { AllWindowObservables, TimestampedEvent } from './network-capture-plugin';\nimport { AMPLITUDE_NETWORK_REQUEST_EVENT } from './constants';\nimport { IRequestWrapper } from '@amplitude/analytics-core';\n\nconst DEFAULT_STATUS_CODE_RANGE = '500-599';\n\nfunction wildcardMatch(str: string, pattern: string) {\n // Escape all regex special characters except for *\n const escapedPattern = pattern.replace(/[-[\\]{}()+?.,\\\\^$|#\\s]/g, '\\\\$&');\n // Replace * with .*\n const regexPattern = '^' + escapedPattern.replace(/\\*/g, '.*') + '$';\n const regex = new RegExp(regexPattern);\n return regex.test(str);\n}\n\nfunction isStatusCodeInRange(statusCode: number, range: string) {\n const ranges = range.split(',');\n for (const r of ranges) {\n const [start, end] = r.split('-').map(Number);\n if (statusCode === start && end === undefined) {\n return true;\n }\n if (statusCode >= start && statusCode <= end) {\n return true;\n }\n }\n return false;\n}\n\nfunction isCaptureRuleMatch(rule: NetworkCaptureRule, hostname: string, status?: number) {\n // check if the host is in the allowed hosts\n if (rule.hosts && !rule.hosts.find((host: string) => wildcardMatch(hostname, host))) {\n return;\n }\n\n // check if the status code is in the allowed range\n if (status || status === 0) {\n const statusCodeRange = rule.statusCodeRange || DEFAULT_STATUS_CODE_RANGE;\n if (!isStatusCodeInRange(status, statusCodeRange)) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction parseUrl(url: string | undefined) {\n if (!url) {\n return;\n }\n try {\n /* istanbul ignore next */\n const currentHref = getGlobalScope()?.location.href;\n const urlObj = new URL(url, currentHref);\n const query = urlObj.searchParams.toString();\n const fragment = urlObj.hash.replace('#', '');\n const href = urlObj.href;\n const host = urlObj.host;\n urlObj.hash = '';\n urlObj.search = '';\n const hrefWithoutQueryOrHash = urlObj.href;\n return { query, fragment, href, hrefWithoutQueryOrHash, host };\n } catch (e) {\n /* istanbul ignore next */\n return;\n }\n}\n\nfunction isAmplitudeNetworkRequestEvent(host: string, requestWrapper: IRequestWrapper): boolean {\n if (host.includes('amplitude.com')) {\n try {\n const body = requestWrapper.body;\n if (typeof body !== 'string') {\n return false;\n }\n const bodyObj = JSON.parse(body) as { events: any[] };\n const { events } = bodyObj;\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */\n if (events.find((event: any) => event.event_type === AMPLITUDE_NETWORK_REQUEST_EVENT)) {\n return true;\n }\n } catch (e) {\n // do nothing\n }\n }\n return false;\n}\n\nexport function shouldTrackNetworkEvent(networkEvent: NetworkRequestEvent, options: NetworkTrackingOptions = {}) {\n const urlObj = parseUrl(networkEvent.url);\n /* istanbul ignore if */\n if (!urlObj) {\n // if the URL failed to parse, do not track the event\n // this is a probably impossible case that would only happen if the URL is malformed\n /* istanbul ignore next */\n return false;\n }\n const { host } = urlObj;\n\n // false if is amplitude request and not configured to track amplitude requests\n if (\n options.ignoreAmplitudeRequests !== false &&\n (wildcardMatch(host, '*.amplitude.com') || wildcardMatch(host, 'amplitude.com'))\n ) {\n return false;\n }\n\n // false if the host is in the ignore list\n if (options.ignoreHosts?.find((ignoreHost: string) => wildcardMatch(host, ignoreHost))) {\n return false;\n }\n\n // false if the status code is not 500-599 and there are no captureRules\n if (\n !options.captureRules &&\n networkEvent.status !== undefined &&\n !isStatusCodeInRange(networkEvent.status, DEFAULT_STATUS_CODE_RANGE)\n ) {\n return false;\n }\n\n if (options.captureRules) {\n // find the first capture rule, in reverse-order,\n // that is a match (true) or a miss (false)\n let isMatch: boolean | undefined;\n [...options.captureRules].reverse().find((rule) => {\n isMatch = isCaptureRuleMatch(rule, host, networkEvent.status);\n return isMatch !== undefined;\n });\n\n // if we found a miss (false) or no match (undefined),\n // then do not track the event\n if (!isMatch) {\n return false;\n }\n }\n\n // skip Amplitude network requests to \"[Amplitude] Network Request\" to avoid infinite loop\n if (networkEvent.requestWrapper && isAmplitudeNetworkRequestEvent(host, networkEvent.requestWrapper)) {\n return false;\n }\n\n return true;\n}\n\nexport type NetworkAnalyticsEvent = {\n ['[Amplitude] URL']: string;\n ['[Amplitude] URL Query']?: string;\n ['[Amplitude] URL Fragment']?: string;\n ['[Amplitude] Request Method']: string;\n ['[Amplitude] Status Code']?: number;\n ['[Amplitude] Start Time']?: number; // unix timestamp\n ['[Amplitude] Completion Time']?: number; // unix timestamp\n ['[Amplitude] Duration']?: number; // completionTime - startTime (millis)\n ['[Amplitude] Request Body Size']?: number;\n ['[Amplitude] Response Body Size']?: number;\n ['[Amplitude] Request Type']?: 'xhr' | 'fetch';\n};\n\nexport function trackNetworkEvents({\n allObservables,\n networkTrackingOptions,\n amplitude,\n}: {\n allObservables: AllWindowObservables;\n networkTrackingOptions: NetworkTrackingOptions;\n amplitude: BrowserClient;\n}) {\n const { networkObservable } = allObservables;\n\n const filteredNetworkObservable = networkObservable.pipe(\n filter((event: TimestampedEvent<NetworkRequestEvent>) => {\n // Only track network events that should be tracked,\n return shouldTrackNetworkEvent(event.event as NetworkRequestEvent, networkTrackingOptions);\n }),\n );\n\n return filteredNetworkObservable.subscribe((networkEvent) => {\n const request = networkEvent.event;\n\n // convert to NetworkAnalyticsEvent\n const urlObj = parseUrl(request.url);\n /* istanbul ignore if */\n if (!urlObj) {\n // if the URL failed to parse, do not track the event\n // this is a very unlikely case, because URL() shouldn't throw an exception\n // when the URL is a valid URL\n /* istanbul ignore next */\n return;\n }\n\n const responseBodySize = request.responseWrapper?.bodySize;\n /* istanbul ignore next */\n const requestBodySize = request.requestWrapper?.bodySize;\n\n const networkAnalyticsEvent: NetworkAnalyticsEvent = {\n ['[Amplitude] URL']: urlObj.hrefWithoutQueryOrHash,\n ['[Amplitude] URL Query']: urlObj.query,\n ['[Amplitude] URL Fragment']: urlObj.fragment,\n ['[Amplitude] Request Method']: request.method,\n ['[Amplitude] Status Code']: request.status,\n ['[Amplitude] Start Time']: request.startTime,\n ['[Amplitude] Completion Time']: request.endTime,\n ['[Amplitude] Duration']: request.duration,\n ['[Amplitude] Request Body Size']: requestBodySize,\n ['[Amplitude] Response Body Size']: responseBodySize,\n ['[Amplitude] Request Type']: request.type,\n };\n\n /* istanbul ignore next */\n amplitude?.track(AMPLITUDE_NETWORK_REQUEST_EVENT, networkAnalyticsEvent);\n });\n}\n"]}
1
+ {"version":3,"file":"track-network-event.js","sourceRoot":"","sources":["../../src/track-network-event.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n BrowserClient,\n NetworkRequestEvent,\n NetworkCaptureRule,\n NetworkTrackingOptions,\n getGlobalScope,\n JsonObject,\n isUrlMatchAllowlist,\n SAFE_HEADERS,\n} from '@amplitude/analytics-core';\nimport { filter } from 'rxjs';\nimport { AllWindowObservables, TimestampedEvent } from './network-capture-plugin';\nimport { AMPLITUDE_NETWORK_REQUEST_EVENT, IS_HEADER_CAPTURE_EXPERIMENTAL } from './constants';\nimport { IRequestWrapper } from '@amplitude/analytics-core';\nimport { BodyCaptureRule } from '@amplitude/analytics-core/lib/esm/types/network-tracking';\n\nconst DEFAULT_STATUS_CODE_RANGE = '500-599';\n\nfunction wildcardMatch(str: string, pattern: string) {\n // Escape all regex special characters except for *\n const escapedPattern = pattern.replace(/[-[\\]{}()+?.,\\\\^$|#\\s]/g, '\\\\$&');\n // Replace * with .*\n const regexPattern = '^' + escapedPattern.replace(/\\*/g, '.*') + '$';\n const regex = new RegExp(regexPattern);\n return regex.test(str);\n}\n\nfunction isStatusCodeInRange(statusCode: number, range: string) {\n const ranges = range.split(',');\n for (const r of ranges) {\n const [start, end] = r.split('-').map(Number);\n if (statusCode === start && end === undefined) {\n return true;\n }\n if (statusCode >= start && statusCode <= end) {\n return true;\n }\n }\n return false;\n}\n\nfunction isCaptureRuleMatch(\n rule: NetworkCaptureRule,\n hostname: string,\n status?: number,\n url?: string,\n method?: string,\n) {\n // check if the host is in the allowed hosts\n if (rule.hosts && !rule.hosts.find((host: string) => wildcardMatch(hostname, host))) {\n return;\n }\n\n // check if the URL is in the allowed URL patterns\n if (url && rule.urls && !isUrlMatchAllowlist(url, rule.urls)) {\n return;\n }\n\n // check if the method is in the allowed methods\n if (\n method &&\n rule.methods &&\n !rule.methods.find(\n (allowedMethod: string) => method.toLowerCase() === allowedMethod.toLowerCase() || allowedMethod === '*',\n )\n ) {\n return;\n }\n\n // check if the status code is in the allowed range\n if (status || status === 0) {\n const statusCodeRange = rule.statusCodeRange || DEFAULT_STATUS_CODE_RANGE;\n if (!isStatusCodeInRange(status, statusCodeRange)) {\n return false;\n }\n }\n\n return true;\n}\n\nfunction parseUrl(url: string | undefined) {\n if (!url) {\n return;\n }\n try {\n /* istanbul ignore next */\n const currentHref = getGlobalScope()?.location.href;\n const urlObj = new URL(url, currentHref);\n const query = urlObj.searchParams.toString();\n const fragment = urlObj.hash.replace('#', '');\n const href = urlObj.href;\n const host = urlObj.host;\n urlObj.hash = '';\n urlObj.search = '';\n const hrefWithoutQueryOrHash = urlObj.href;\n return { query, fragment, href, hrefWithoutQueryOrHash, host };\n } catch (e) {\n /* istanbul ignore next */\n return;\n }\n}\n\nfunction isAmplitudeNetworkRequestEvent(host: string, requestWrapper: IRequestWrapper): boolean {\n if (host.includes('amplitude.com')) {\n try {\n const body = requestWrapper.body;\n if (typeof body !== 'string') {\n return false;\n }\n const bodyObj = JSON.parse(body) as { events: any[] };\n const { events } = bodyObj;\n /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */\n if (events.find((event: any) => event.event_type === AMPLITUDE_NETWORK_REQUEST_EVENT)) {\n return true;\n }\n } catch (e) {\n // do nothing\n }\n }\n return false;\n}\n\n/**\n * Takes a user provided header capture rule and returns a\n * HeaderCaptureRule object that sets proper default values.\n *\n * @param rule - The header capture rule to parse.\n * @returns A HeaderCaptureRule object.\n */\nexport function parseHeaderCaptureRule(rule: string[] | boolean | undefined | null): string[] | undefined {\n if (typeof rule !== 'object' || rule === null) {\n // if rule is truthy or undefined, return SAFE_HEADERS\n if (rule) {\n return [...SAFE_HEADERS];\n } else if (rule === undefined) {\n /* istanbul ignore next */\n const res = IS_HEADER_CAPTURE_EXPERIMENTAL ? undefined : [...SAFE_HEADERS];\n return res;\n }\n return;\n }\n\n // if the rule is defined but empty, return undefined\n if (rule.length === 0) {\n return;\n }\n\n return rule;\n}\n\nfunction isBodyCaptureRuleEmpty(rule: BodyCaptureRule) {\n /* istanbul ignore next */\n return !rule?.allowlist?.length && !rule?.blocklist?.length;\n}\n\nexport function shouldTrackNetworkEvent(networkEvent: NetworkRequestEvent, options: NetworkTrackingOptions = {}) {\n const urlObj = parseUrl(networkEvent.url);\n /* istanbul ignore if */\n if (!urlObj) {\n // if the URL failed to parse, do not track the event\n // this is a probably impossible case that would only happen if the URL is malformed\n /* istanbul ignore next */\n return false;\n }\n const { host } = urlObj;\n\n // false if is amplitude request and not configured to track amplitude requests\n if (\n options.ignoreAmplitudeRequests !== false &&\n (wildcardMatch(host, '*.amplitude.com') || wildcardMatch(host, 'amplitude.com'))\n ) {\n return false;\n }\n\n // false if the host is in the ignore list\n if (options.ignoreHosts?.find((ignoreHost: string) => wildcardMatch(host, ignoreHost))) {\n return false;\n }\n\n // false if the status code is not 500-599 and there are no captureRules\n if (\n !options.captureRules &&\n networkEvent.status !== undefined &&\n !isStatusCodeInRange(networkEvent.status, DEFAULT_STATUS_CODE_RANGE)\n ) {\n return false;\n }\n\n if (options.captureRules) {\n // find the first capture rule, in reverse-order,\n // that is a match (true) or a miss (false)\n let isMatch: boolean | undefined;\n [...options.captureRules].reverse().find((rule) => {\n isMatch = isCaptureRuleMatch(rule, host, networkEvent.status, networkEvent.url, networkEvent.method);\n\n if (isMatch) {\n const responseHeadersRule = parseHeaderCaptureRule(rule.responseHeaders);\n if (networkEvent.responseWrapper && responseHeadersRule) {\n const responseHeaders = networkEvent.responseWrapper.headers(responseHeadersRule);\n if (responseHeaders) {\n networkEvent.responseHeaders = responseHeaders;\n }\n }\n\n // if requestHeaders rule is specified, enrich the event with the request headers\n const requestHeadersRule = parseHeaderCaptureRule(rule.requestHeaders);\n if (networkEvent.requestWrapper && requestHeadersRule) {\n const requestHeaders = networkEvent.requestWrapper.headers(requestHeadersRule);\n if (requestHeaders) {\n networkEvent.requestHeaders = requestHeaders;\n }\n }\n\n // if responseBody rule is specified, enrich the event with the response body\n if (networkEvent.responseWrapper && rule.responseBody && !isBodyCaptureRuleEmpty(rule.responseBody)) {\n networkEvent.responseBodyJson = networkEvent.responseWrapper.json(\n rule.responseBody.allowlist,\n rule.responseBody.blocklist,\n );\n }\n\n // if requestBody rule is specified, enrich the event with the request body\n if (networkEvent.requestWrapper && rule.requestBody && !isBodyCaptureRuleEmpty(rule.requestBody)) {\n networkEvent.requestBodyJson = networkEvent.requestWrapper.json(\n rule.requestBody.allowlist,\n rule.requestBody.blocklist,\n );\n }\n }\n\n return isMatch !== undefined;\n });\n\n // if we found a miss (false) or no match (undefined),\n // then do not track the event\n if (!isMatch) {\n return false;\n }\n }\n\n // skip Amplitude network requests to \"[Amplitude] Network Request\" to avoid infinite loop\n if (networkEvent.requestWrapper && isAmplitudeNetworkRequestEvent(host, networkEvent.requestWrapper)) {\n return false;\n }\n\n return true;\n}\n\nexport type NetworkAnalyticsEvent = {\n ['[Amplitude] URL']: string;\n ['[Amplitude] URL Query']?: string;\n ['[Amplitude] URL Fragment']?: string;\n ['[Amplitude] Request Method']: string;\n ['[Amplitude] Status Code']?: number;\n ['[Amplitude] Start Time']?: number; // unix timestamp\n ['[Amplitude] Completion Time']?: number; // unix timestamp\n ['[Amplitude] Duration']?: number; // completionTime - startTime (millis)\n ['[Amplitude] Request Body Size']?: number;\n ['[Amplitude] Request Headers']?: Record<string, string>;\n ['[Amplitude] Request Body']?: JsonObject;\n ['[Amplitude] Response Body Size']?: number;\n ['[Amplitude] Response Headers']?: Record<string, string>;\n ['[Amplitude] Response Body']?: JsonObject;\n ['[Amplitude] Request Type']?: 'xhr' | 'fetch';\n};\n\nexport async function logNetworkAnalyticsEvent(\n networkAnalyticsEvent: NetworkAnalyticsEvent,\n request: NetworkRequestEvent,\n amplitude: BrowserClient,\n) {\n if (request.requestBodyJson || request.responseBodyJson) {\n const [requestBody, responseBody] = await Promise.all([request.requestBodyJson, request.responseBodyJson]);\n if (requestBody) {\n networkAnalyticsEvent['[Amplitude] Request Body'] = requestBody;\n }\n if (responseBody) {\n networkAnalyticsEvent['[Amplitude] Response Body'] = responseBody;\n }\n }\n /* istanbul ignore next */\n amplitude?.track(AMPLITUDE_NETWORK_REQUEST_EVENT, networkAnalyticsEvent);\n}\n\nexport function trackNetworkEvents({\n allObservables,\n networkTrackingOptions,\n amplitude,\n}: {\n allObservables: AllWindowObservables;\n networkTrackingOptions: NetworkTrackingOptions;\n amplitude: BrowserClient;\n}) {\n const { networkObservable } = allObservables;\n\n const filteredNetworkObservable = networkObservable.pipe(\n filter((event: TimestampedEvent<NetworkRequestEvent>) => {\n // Only track network events that should be tracked,\n return shouldTrackNetworkEvent(event.event as NetworkRequestEvent, networkTrackingOptions);\n }),\n );\n\n return filteredNetworkObservable.subscribe((networkEvent) => {\n const request = networkEvent.event;\n\n // convert to NetworkAnalyticsEvent\n const urlObj = parseUrl(request.url);\n /* istanbul ignore if */\n if (!urlObj) {\n // if the URL failed to parse, do not track the event\n // this is a very unlikely case, because URL() shouldn't throw an exception\n // when the URL is a valid URL\n /* istanbul ignore next */\n return;\n }\n\n const responseBodySize = request.responseWrapper?.bodySize;\n /* istanbul ignore next */\n const requestBodySize = request.requestWrapper?.bodySize;\n\n const networkAnalyticsEvent: NetworkAnalyticsEvent = {\n ['[Amplitude] URL']: urlObj.hrefWithoutQueryOrHash,\n ['[Amplitude] URL Query']: urlObj.query,\n ['[Amplitude] URL Fragment']: urlObj.fragment,\n ['[Amplitude] Request Method']: request.method,\n ['[Amplitude] Status Code']: request.status,\n ['[Amplitude] Start Time']: request.startTime,\n ['[Amplitude] Completion Time']: request.endTime,\n ['[Amplitude] Duration']: request.duration,\n ['[Amplitude] Request Body Size']: requestBodySize,\n ['[Amplitude] Response Body Size']: responseBodySize,\n ['[Amplitude] Request Type']: request.type,\n ['[Amplitude] Request Headers']: request.requestHeaders,\n ['[Amplitude] Response Headers']: request.responseHeaders,\n };\n\n // fire-and-forget promise that tracks the event\n void logNetworkAnalyticsEvent(networkAnalyticsEvent, request, amplitude);\n });\n}\n"]}
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "1.4.5";
1
+ export declare const VERSION = "1.5.0";
2
2
  //# sourceMappingURL=version.d.ts.map