@newrelic/browser-agent 0.0.9 → 0.1.229
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -207
- package/dist/cjs/cdn/lite.js +14 -0
- package/dist/cjs/cdn/polyfills/lite.js +4 -0
- package/dist/cjs/cdn/polyfills/pro.js +4 -0
- package/dist/cjs/cdn/polyfills/spa.js +4 -0
- package/dist/cjs/cdn/polyfills.js +14 -0
- package/dist/cjs/cdn/pro.js +19 -0
- package/dist/cjs/cdn/spa.js +19 -0
- package/dist/cjs/cdn/worker.js +16 -0
- package/dist/cjs/common/aggregate/aggregator.js +168 -0
- package/dist/cjs/common/browser-version/firefox-version.js +17 -0
- package/dist/cjs/common/browser-version/ios-version.js +18 -0
- package/dist/cjs/common/config/config.js +76 -0
- package/dist/cjs/common/config/state/configurable.js +32 -0
- package/dist/cjs/common/config/state/info.js +50 -0
- package/dist/cjs/common/config/state/init.js +86 -0
- package/dist/cjs/common/config/state/loader-config.js +28 -0
- package/dist/cjs/common/config/state/originals.js +9 -0
- package/dist/cjs/common/config/state/runtime.js +50 -0
- package/dist/cjs/common/constants/environment-variables.js +20 -0
- package/dist/cjs/common/context/shared-context.js +25 -0
- package/dist/cjs/common/deny-list/deny-list.js +108 -0
- package/dist/cjs/common/drain/drain.js +126 -0
- package/dist/cjs/common/event-emitter/contextual-ee.js +149 -0
- package/dist/cjs/common/event-emitter/handle.js +24 -0
- package/dist/cjs/common/event-emitter/register-handler.js +24 -0
- package/dist/cjs/common/event-listener/event-listener-opts.js +46 -0
- package/dist/cjs/common/harvest/harvest-scheduler.js +111 -0
- package/dist/cjs/common/harvest/harvest.js +236 -0
- package/dist/cjs/common/ids/id.js +30 -0
- package/dist/cjs/common/ids/unique-id.js +84 -0
- package/dist/cjs/common/metrics/framework-detection.js +72 -0
- package/dist/cjs/common/metrics/paint-metrics.js +13 -0
- package/dist/cjs/common/serialize/bel-serializer.js +89 -0
- package/dist/cjs/common/timing/nav-timing.js +77 -0
- package/dist/cjs/common/timing/now.js +15 -0
- package/dist/cjs/common/unload/eol.js +69 -0
- package/dist/cjs/common/url/clean-url.js +16 -0
- package/dist/cjs/common/url/encode.js +79 -0
- package/dist/cjs/common/url/location.js +14 -0
- package/dist/cjs/common/url/parse-url.js +66 -0
- package/dist/cjs/common/url/protocol.js +25 -0
- package/dist/cjs/common/util/console.js +17 -0
- package/dist/cjs/common/util/data-size.js +25 -0
- package/dist/cjs/common/util/feature-flags.js +42 -0
- package/dist/cjs/common/util/get-or-set.js +39 -0
- package/dist/cjs/common/util/global-scope.js +56 -0
- package/dist/cjs/common/util/map-own.js +24 -0
- package/dist/cjs/common/util/obfuscate.js +76 -0
- package/dist/cjs/common/util/reduce.js +22 -0
- package/dist/cjs/common/util/s-hash.js +19 -0
- package/dist/cjs/common/util/single.js +23 -0
- package/dist/cjs/common/util/stringify.js +47 -0
- package/dist/cjs/common/util/submit-data.js +99 -0
- package/dist/cjs/common/util/traverse.js +41 -0
- package/dist/cjs/common/util/user-agent.js +57 -0
- package/dist/cjs/common/window/load.js +19 -0
- package/dist/cjs/common/window/nreum.js +107 -0
- package/dist/cjs/common/window/page-visibility.js +28 -0
- package/dist/cjs/common/window/session-storage.js +42 -0
- package/dist/cjs/common/window/supports-performance-observer.js +15 -0
- package/dist/cjs/common/window/top-level-callers.js +23 -0
- package/dist/cjs/common/wrap/index.js +68 -0
- package/dist/cjs/common/wrap/wrap-events.js +105 -0
- package/dist/cjs/common/wrap/wrap-fetch.js +114 -0
- package/dist/cjs/common/wrap/wrap-function.js +269 -0
- package/dist/cjs/common/wrap/wrap-history.js +56 -0
- package/dist/cjs/common/wrap/wrap-jsonp.js +129 -0
- package/dist/cjs/common/wrap/wrap-mutation.js +61 -0
- package/dist/cjs/common/wrap/wrap-promise.js +160 -0
- package/dist/cjs/common/wrap/wrap-raf.js +55 -0
- package/dist/cjs/common/wrap/wrap-timer.js +70 -0
- package/dist/cjs/common/wrap/wrap-xhr.js +206 -0
- package/dist/cjs/features/ajax/aggregate/index.js +226 -0
- package/dist/cjs/features/ajax/constants.js +9 -0
- package/dist/cjs/features/ajax/index.js +12 -0
- package/dist/cjs/features/ajax/instrument/distributed-tracing.js +145 -0
- package/dist/cjs/features/ajax/instrument/index.js +338 -0
- package/dist/cjs/features/ajax/instrument/response-size.js +26 -0
- package/dist/cjs/features/jserrors/aggregate/canonical-function-name.js +18 -0
- package/dist/cjs/features/jserrors/aggregate/canonical-function-name.test.js +30 -0
- package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.js +216 -0
- package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +257 -0
- package/dist/cjs/features/jserrors/aggregate/format-stack-trace.js +36 -0
- package/dist/cjs/features/jserrors/aggregate/format-stack-trace.test.js +39 -0
- package/dist/cjs/features/jserrors/aggregate/index.js +267 -0
- package/dist/cjs/features/jserrors/aggregate/string-hash-code.js +23 -0
- package/dist/cjs/features/jserrors/aggregate/string-hash-code.test.js +26 -0
- package/dist/cjs/features/jserrors/constants.js +11 -0
- package/dist/cjs/features/jserrors/index.js +12 -0
- package/dist/cjs/features/jserrors/instrument/debug.js +40 -0
- package/dist/cjs/features/jserrors/instrument/index.js +158 -0
- package/dist/cjs/features/metrics/aggregate/index.js +136 -0
- package/dist/cjs/features/metrics/constants.js +17 -0
- package/dist/cjs/features/metrics/index.js +12 -0
- package/dist/cjs/features/metrics/instrument/index.js +20 -0
- package/dist/cjs/features/metrics/instrument/workers-helper.js +121 -0
- package/dist/cjs/features/page_action/aggregate/index.js +112 -0
- package/dist/cjs/features/page_action/constants.js +9 -0
- package/dist/cjs/features/page_action/index.js +12 -0
- package/dist/cjs/features/page_action/instrument/index.js +21 -0
- package/dist/cjs/features/page_view_event/aggregate/index.js +133 -0
- package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +39 -0
- package/dist/cjs/features/page_view_event/constants.js +15 -0
- package/dist/cjs/features/page_view_event/index.js +12 -0
- package/dist/cjs/features/page_view_event/instrument/index.js +38 -0
- package/dist/cjs/features/page_view_timing/aggregate/index.js +265 -0
- package/dist/cjs/features/page_view_timing/constants.js +9 -0
- package/dist/cjs/features/page_view_timing/first-paint.js +50 -0
- package/dist/cjs/features/page_view_timing/index.js +12 -0
- package/dist/cjs/features/page_view_timing/instrument/index.js +36 -0
- package/dist/cjs/features/page_view_timing/long-tasks.js +75 -0
- package/dist/cjs/features/session_trace/aggregate/index.js +375 -0
- package/dist/cjs/features/session_trace/constants.js +32 -0
- package/dist/cjs/features/session_trace/index.js +12 -0
- package/dist/cjs/features/session_trace/instrument/index.js +133 -0
- package/dist/cjs/features/spa/aggregate/index.js +684 -0
- package/dist/cjs/features/spa/aggregate/interaction-node.js +84 -0
- package/dist/cjs/features/spa/aggregate/interaction-node.test.js +16 -0
- package/dist/cjs/features/spa/aggregate/interaction.js +98 -0
- package/dist/cjs/features/spa/aggregate/serializer.js +147 -0
- package/dist/cjs/features/spa/constants.js +53 -0
- package/dist/cjs/features/spa/index.js +12 -0
- package/dist/cjs/features/spa/instrument/index.js +114 -0
- package/dist/cjs/features/utils/aggregate-base.js +13 -0
- package/dist/cjs/features/utils/feature-base.js +58 -0
- package/dist/cjs/features/utils/handler-cache.js +64 -0
- package/dist/cjs/features/utils/instrument-base.js +71 -0
- package/dist/cjs/features/utils/lazy-loader.js +44 -0
- package/dist/cjs/index.js +81 -58
- package/dist/cjs/loaders/agent.js +86 -0
- package/dist/cjs/loaders/api/api.js +109 -0
- package/dist/cjs/loaders/api/apiAsync.js +94 -0
- package/dist/cjs/loaders/browser-agent.js +29 -0
- package/dist/cjs/loaders/configure/configure.js +47 -0
- package/dist/cjs/loaders/features/enabled-features.js +19 -0
- package/dist/cjs/loaders/features/featureDependencies.js +32 -0
- package/dist/cjs/loaders/features/features.js +33 -0
- package/dist/cjs/loaders/micro-agent.js +93 -0
- package/dist/cjs/loaders/worker-agent.js +24 -0
- package/dist/esm/cdn/lite.js +12 -0
- package/dist/esm/cdn/polyfills/lite.js +7 -0
- package/dist/esm/cdn/polyfills/pro.js +7 -0
- package/dist/esm/cdn/polyfills/spa.js +7 -0
- package/dist/esm/cdn/polyfills.js +17 -0
- package/dist/esm/cdn/pro.js +17 -0
- package/dist/esm/cdn/spa.js +17 -0
- package/dist/esm/cdn/worker.js +14 -0
- package/dist/esm/common/aggregate/aggregator.js +161 -0
- package/dist/esm/common/browser-version/firefox-version.js +10 -0
- package/dist/esm/common/browser-version/ios-version.js +10 -0
- package/dist/esm/common/config/config.js +11 -0
- package/dist/esm/common/config/state/configurable.js +25 -0
- package/dist/esm/common/config/state/info.js +42 -0
- package/dist/esm/common/config/state/init.js +78 -0
- package/dist/esm/common/config/state/loader-config.js +21 -0
- package/dist/esm/common/config/state/originals.js +2 -0
- package/dist/esm/common/config/state/runtime.js +41 -0
- package/dist/esm/common/constants/environment-variables.js +11 -0
- package/dist/esm/common/context/shared-context.js +18 -0
- package/dist/esm/common/deny-list/deny-list.js +101 -0
- package/dist/esm/common/drain/drain.js +119 -0
- package/dist/esm/common/event-emitter/contextual-ee.js +142 -0
- package/dist/esm/common/event-emitter/handle.js +16 -0
- package/dist/esm/common/event-emitter/register-handler.js +19 -0
- package/dist/esm/common/event-listener/event-listener-opts.js +39 -0
- package/dist/esm/common/harvest/harvest-scheduler.js +105 -0
- package/dist/esm/common/harvest/harvest.js +228 -0
- package/dist/esm/common/ids/id.js +23 -0
- package/dist/esm/common/ids/unique-id.js +75 -0
- package/dist/esm/common/metrics/framework-detection.js +66 -0
- package/dist/esm/common/metrics/paint-metrics.js +6 -0
- package/dist/esm/common/serialize/bel-serializer.js +80 -0
- package/dist/esm/common/timing/nav-timing.js +67 -0
- package/dist/esm/common/timing/now.js +9 -0
- package/dist/esm/common/unload/eol.js +62 -0
- package/dist/esm/common/url/clean-url.js +10 -0
- package/dist/esm/common/url/encode.js +71 -0
- package/dist/esm/common/url/location.js +8 -0
- package/dist/esm/common/url/parse-url.js +60 -0
- package/dist/esm/common/url/protocol.js +17 -0
- package/dist/esm/common/util/console.js +11 -0
- package/dist/esm/common/util/data-size.js +19 -0
- package/dist/esm/common/util/feature-flags.js +33 -0
- package/dist/esm/common/util/get-or-set.js +33 -0
- package/dist/esm/common/util/global-scope.js +44 -0
- package/dist/esm/common/util/map-own.js +18 -0
- package/dist/esm/common/util/obfuscate.js +67 -0
- package/dist/esm/common/util/reduce.js +16 -0
- package/dist/esm/common/util/s-hash.js +13 -0
- package/dist/esm/common/util/single.js +16 -0
- package/dist/esm/common/util/stringify.js +42 -0
- package/dist/esm/common/util/submit-data.js +91 -0
- package/dist/esm/common/util/traverse.js +35 -0
- package/dist/esm/common/util/user-agent.js +48 -0
- package/dist/esm/common/window/load.js +12 -0
- package/dist/esm/common/window/nreum.js +91 -0
- package/dist/esm/common/window/page-visibility.js +23 -0
- package/dist/esm/common/window/session-storage.js +36 -0
- package/dist/esm/common/window/supports-performance-observer.js +9 -0
- package/dist/esm/common/window/top-level-callers.js +17 -0
- package/dist/esm/common/wrap/index.js +14 -0
- package/dist/esm/common/wrap/wrap-events.js +97 -0
- package/dist/esm/common/wrap/wrap-fetch.js +105 -0
- package/dist/esm/common/wrap/wrap-function.js +257 -0
- package/dist/esm/common/wrap/wrap-history.js +48 -0
- package/dist/esm/common/wrap/wrap-jsonp.js +122 -0
- package/dist/esm/common/wrap/wrap-mutation.js +54 -0
- package/dist/esm/common/wrap/wrap-promise.js +153 -0
- package/dist/esm/common/wrap/wrap-raf.js +48 -0
- package/dist/esm/common/wrap/wrap-timer.js +63 -0
- package/dist/esm/common/wrap/wrap-xhr.js +199 -0
- package/dist/esm/features/ajax/aggregate/index.js +218 -0
- package/dist/esm/features/ajax/constants.js +2 -0
- package/dist/esm/features/ajax/index.js +1 -0
- package/dist/esm/features/ajax/instrument/distributed-tracing.js +137 -0
- package/dist/esm/features/ajax/instrument/index.js +330 -0
- package/dist/esm/features/ajax/instrument/response-size.js +19 -0
- package/dist/esm/features/jserrors/aggregate/canonical-function-name.js +12 -0
- package/dist/esm/features/jserrors/aggregate/canonical-function-name.test.js +28 -0
- package/dist/esm/features/jserrors/aggregate/compute-stack-trace.js +209 -0
- package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +255 -0
- package/dist/esm/features/jserrors/aggregate/format-stack-trace.js +29 -0
- package/dist/esm/features/jserrors/aggregate/format-stack-trace.test.js +37 -0
- package/dist/esm/features/jserrors/aggregate/index.js +260 -0
- package/dist/esm/features/jserrors/aggregate/string-hash-code.js +17 -0
- package/dist/esm/features/jserrors/aggregate/string-hash-code.test.js +24 -0
- package/dist/esm/features/jserrors/constants.js +3 -0
- package/dist/esm/features/jserrors/index.js +1 -0
- package/dist/esm/features/jserrors/instrument/debug.js +38 -0
- package/dist/esm/features/jserrors/instrument/index.js +150 -0
- package/dist/esm/features/metrics/aggregate/index.js +129 -0
- package/dist/esm/features/metrics/constants.js +6 -0
- package/dist/esm/features/metrics/index.js +1 -0
- package/dist/esm/features/metrics/instrument/index.js +13 -0
- package/dist/esm/features/metrics/instrument/workers-helper.js +116 -0
- package/dist/esm/features/page_action/aggregate/index.js +105 -0
- package/dist/esm/features/page_action/constants.js +2 -0
- package/dist/esm/features/page_action/index.js +1 -0
- package/dist/esm/features/page_action/instrument/index.js +14 -0
- package/dist/esm/features/page_view_event/aggregate/index.js +124 -0
- package/dist/esm/features/page_view_event/aggregate/initialized-features.js +34 -0
- package/dist/esm/features/page_view_event/constants.js +5 -0
- package/dist/esm/features/page_view_event/index.js +1 -0
- package/dist/esm/features/page_view_event/instrument/index.js +29 -0
- package/dist/esm/features/page_view_timing/aggregate/index.js +258 -0
- package/dist/esm/features/page_view_timing/constants.js +2 -0
- package/dist/esm/features/page_view_timing/first-paint.js +43 -0
- package/dist/esm/features/page_view_timing/index.js +1 -0
- package/dist/esm/features/page_view_timing/instrument/index.js +28 -0
- package/dist/esm/features/page_view_timing/long-tasks.js +69 -0
- package/dist/esm/features/session_trace/aggregate/index.js +366 -0
- package/dist/esm/features/session_trace/constants.js +14 -0
- package/dist/esm/features/session_trace/index.js +1 -0
- package/dist/esm/features/session_trace/instrument/index.js +123 -0
- package/dist/esm/features/spa/aggregate/index.js +674 -0
- package/dist/esm/features/spa/aggregate/interaction-node.js +78 -0
- package/dist/esm/features/spa/aggregate/interaction-node.test.js +14 -0
- package/dist/esm/features/spa/aggregate/interaction.js +92 -0
- package/dist/esm/features/spa/aggregate/serializer.js +139 -0
- package/dist/esm/features/spa/constants.js +25 -0
- package/dist/esm/features/spa/index.js +1 -0
- package/dist/esm/features/spa/instrument/index.js +104 -0
- package/dist/esm/features/utils/aggregate-base.js +6 -0
- package/dist/esm/features/utils/feature-base.js +51 -0
- package/dist/esm/features/utils/handler-cache.js +57 -0
- package/dist/esm/features/utils/instrument-base.js +69 -0
- package/dist/esm/features/utils/lazy-loader.js +37 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/loaders/agent.js +77 -0
- package/dist/esm/loaders/api/api.js +104 -0
- package/dist/esm/loaders/api/apiAsync.js +88 -0
- package/dist/esm/loaders/browser-agent.js +23 -0
- package/dist/esm/loaders/configure/configure.js +41 -0
- package/dist/esm/loaders/features/enabled-features.js +13 -0
- package/dist/esm/loaders/features/featureDependencies.js +25 -0
- package/dist/esm/loaders/features/features.js +25 -0
- package/dist/esm/loaders/micro-agent.js +86 -0
- package/dist/esm/loaders/worker-agent.js +18 -0
- package/package.json +204 -71
- package/types.ts +221 -0
- package/dist/bundled/es5/index.js +0 -2
- package/dist/bundled/es5/index.js.map +0 -1
- package/dist/bundled/es6/index.js +0 -2
- package/dist/bundled/es6/index.js.map +0 -1
- package/dist/cjs/index.d.ts +0 -19
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/types.d.ts +0 -94
- package/dist/cjs/types.js +0 -28
- package/dist/cjs/types.js.map +0 -1
- package/dist/cjs/utils/api/api.d.ts +0 -10
- package/dist/cjs/utils/api/api.js +0 -40
- package/dist/cjs/utils/api/api.js.map +0 -1
- package/dist/cjs/utils/config/build-configs.d.ts +0 -6
- package/dist/cjs/utils/config/build-configs.js +0 -68
- package/dist/cjs/utils/config/build-configs.js.map +0 -1
- package/dist/cjs/utils/features/features.d.ts +0 -5
- package/dist/cjs/utils/features/features.js +0 -14
- package/dist/cjs/utils/features/features.js.map +0 -1
- package/dist/cjs/utils/features/initialize.d.ts +0 -5
- package/dist/cjs/utils/features/initialize.js +0 -51
- package/dist/cjs/utils/features/initialize.js.map +0 -1
- package/dist/es/index.d.ts +0 -19
- package/dist/es/index.js +0 -55
- package/dist/es/index.js.map +0 -1
- package/dist/es/types.d.ts +0 -94
- package/dist/es/types.js +0 -24
- package/dist/es/types.js.map +0 -1
- package/dist/es/utils/api/api.d.ts +0 -10
- package/dist/es/utils/api/api.js +0 -36
- package/dist/es/utils/api/api.js.map +0 -1
- package/dist/es/utils/config/build-configs.d.ts +0 -6
- package/dist/es/utils/config/build-configs.js +0 -64
- package/dist/es/utils/config/build-configs.js.map +0 -1
- package/dist/es/utils/features/features.d.ts +0 -5
- package/dist/es/utils/features/features.js +0 -10
- package/dist/es/utils/features/features.js.map +0 -1
- package/dist/es/utils/features/initialize.d.ts +0 -5
- package/dist/es/utils/features/initialize.js +0 -24
- package/dist/es/utils/features/initialize.js.map +0 -1
- package/dist/umd/index.d.ts +0 -19
- package/dist/umd/index.js +0 -69
- package/dist/umd/index.js.map +0 -1
- package/dist/umd/types.d.ts +0 -94
- package/dist/umd/types.js +0 -38
- package/dist/umd/types.js.map +0 -1
- package/dist/umd/utils/api/api.d.ts +0 -10
- package/dist/umd/utils/api/api.js +0 -50
- package/dist/umd/utils/api/api.js.map +0 -1
- package/dist/umd/utils/config/build-configs.d.ts +0 -6
- package/dist/umd/utils/config/build-configs.js +0 -78
- package/dist/umd/utils/config/build-configs.js.map +0 -1
- package/dist/umd/utils/features/features.d.ts +0 -5
- package/dist/umd/utils/features/features.js +0 -24
- package/dist/umd/utils/features/features.js.map +0 -1
- package/dist/umd/utils/features/initialize.d.ts +0 -5
- package/dist/umd/utils/features/initialize.js +0 -63
- package/dist/umd/utils/features/initialize.js.map +0 -1
- package/dist/webpack-analysis.html +0 -38
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
var MAX_NODES = 128;
|
|
7
|
+
var lastId = 0;
|
|
8
|
+
export function InteractionNode(interaction, parent, type, timestamp) {
|
|
9
|
+
Object.defineProperty(this, 'interaction', {
|
|
10
|
+
value: interaction,
|
|
11
|
+
writable: true // enumerable: false -- by default, which hides this prop from obj (iterations)
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
this.parent = parent;
|
|
15
|
+
this.id = ++lastId;
|
|
16
|
+
this.type = type;
|
|
17
|
+
this.children = [];
|
|
18
|
+
this.end = null;
|
|
19
|
+
this.jsEnd = this.start = timestamp;
|
|
20
|
+
this.jsTime = 0;
|
|
21
|
+
this.attrs = {};
|
|
22
|
+
this.cancelled = false;
|
|
23
|
+
}
|
|
24
|
+
var InteractionNodePrototype = InteractionNode.prototype;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} type
|
|
28
|
+
* @param {number} timestamp
|
|
29
|
+
* @param {string} name
|
|
30
|
+
* @param {bool} dontWait - When true, the interaction will not immediately start waiting
|
|
31
|
+
* for this node to complete. This is used when the creation of
|
|
32
|
+
* the node and its start happen at different times (e.g. XHR).
|
|
33
|
+
*/
|
|
34
|
+
InteractionNodePrototype.child = function child(type, timestamp, name, dontWait) {
|
|
35
|
+
var interaction = this.interaction;
|
|
36
|
+
if (interaction.end || interaction.nodes >= MAX_NODES) return null;
|
|
37
|
+
interaction.onNodeAdded(this);
|
|
38
|
+
var node = new InteractionNode(interaction, this, type, timestamp);
|
|
39
|
+
node.attrs.name = name;
|
|
40
|
+
interaction.nodes++;
|
|
41
|
+
if (!dontWait) {
|
|
42
|
+
interaction.remaining++;
|
|
43
|
+
}
|
|
44
|
+
return node;
|
|
45
|
+
};
|
|
46
|
+
InteractionNodePrototype.callback = function addCallbackTime(exclusiveTime, end) {
|
|
47
|
+
var node = this;
|
|
48
|
+
node.jsTime += exclusiveTime;
|
|
49
|
+
if (end > node.jsEnd) {
|
|
50
|
+
node.jsEnd = end;
|
|
51
|
+
node.interaction.lastCb = end;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
InteractionNodePrototype.cancel = function cancel() {
|
|
55
|
+
this.cancelled = true;
|
|
56
|
+
var interaction = this.interaction;
|
|
57
|
+
interaction.remaining--;
|
|
58
|
+
};
|
|
59
|
+
InteractionNodePrototype.finish = function finish(timestamp) {
|
|
60
|
+
var node = this;
|
|
61
|
+
if (node.end) return;
|
|
62
|
+
node.end = timestamp;
|
|
63
|
+
|
|
64
|
+
// Find the next parent node that is not cancelled
|
|
65
|
+
let parent = node.parent;
|
|
66
|
+
while (parent?.cancelled) parent = parent.parent;
|
|
67
|
+
|
|
68
|
+
// Assign the node to the non-cancelled parent node
|
|
69
|
+
if (parent) parent.children.push(node);
|
|
70
|
+
node.parent = null;
|
|
71
|
+
|
|
72
|
+
// Update the interaction remaining counter
|
|
73
|
+
var interaction = this.interaction;
|
|
74
|
+
interaction.remaining--;
|
|
75
|
+
interaction.lastFinish = timestamp;
|
|
76
|
+
// check if interaction has finished, (this is needed for older browsers for unknown reasons)
|
|
77
|
+
interaction.checkFinish();
|
|
78
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { faker } from '@faker-js/faker';
|
|
2
|
+
import { InteractionNode } from './interaction-node';
|
|
3
|
+
test('finishing node with cancelled parent should not throw an error', () => {
|
|
4
|
+
const interaction = {
|
|
5
|
+
remaining: 0,
|
|
6
|
+
onNodeAdded: jest.fn(),
|
|
7
|
+
checkFinish: jest.fn()
|
|
8
|
+
};
|
|
9
|
+
const interactionRootNode = new InteractionNode(interaction, null, 'interaction', faker.date.past().getUTCSeconds());
|
|
10
|
+
const interactionNode = interactionRootNode.child('test', faker.date.past().getUTCSeconds(), 'test', false);
|
|
11
|
+
interactionRootNode.cancel();
|
|
12
|
+
interactionNode.finish();
|
|
13
|
+
expect(interactionRootNode.children.length).toEqual(0);
|
|
14
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getInfo, getRuntime, originals } from '../../../common/config/config';
|
|
7
|
+
import { mapOwn } from '../../../common/util/map-own';
|
|
8
|
+
import { ee } from '../../../common/event-emitter/contextual-ee';
|
|
9
|
+
import { InteractionNode } from './interaction-node';
|
|
10
|
+
import { now } from '../../../common/timing/now';
|
|
11
|
+
var originalSetTimeout = originals.ST;
|
|
12
|
+
var originalClearTimeout = originals.CT;
|
|
13
|
+
var lastId = {};
|
|
14
|
+
export function Interaction(eventName, timestamp, url, routeName, onFinished, agentIdentifier) {
|
|
15
|
+
this.agentIdentifier = agentIdentifier;
|
|
16
|
+
this.ee = ee.get(agentIdentifier);
|
|
17
|
+
lastId[agentIdentifier] = 0;
|
|
18
|
+
this.id = ++lastId[agentIdentifier];
|
|
19
|
+
this.eventName = eventName;
|
|
20
|
+
this.nodes = 0;
|
|
21
|
+
this.remaining = 0;
|
|
22
|
+
this.finishTimer = null;
|
|
23
|
+
this.checkingFinish = false;
|
|
24
|
+
this.lastCb = this.lastFinish = timestamp;
|
|
25
|
+
this.handlers = [];
|
|
26
|
+
this.onFinished = onFinished;
|
|
27
|
+
this.done = false;
|
|
28
|
+
var root = this.root = new InteractionNode(this, null, 'interaction', timestamp);
|
|
29
|
+
var attrs = root.attrs;
|
|
30
|
+
attrs.trigger = eventName;
|
|
31
|
+
attrs.initialPageURL = getRuntime(agentIdentifier).origin;
|
|
32
|
+
attrs.oldRoute = routeName;
|
|
33
|
+
attrs.newURL = attrs.oldURL = url;
|
|
34
|
+
attrs.custom = {};
|
|
35
|
+
attrs.store = {};
|
|
36
|
+
}
|
|
37
|
+
var InteractionPrototype = Interaction.prototype;
|
|
38
|
+
InteractionPrototype.checkFinish = function checkFinish() {
|
|
39
|
+
var interaction = this;
|
|
40
|
+
if (interaction.remaining > 0) {
|
|
41
|
+
interaction._resetFinishCheck();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (interaction.checkingFinish) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (interaction.root.end !== null) return;
|
|
48
|
+
interaction._resetFinishCheck();
|
|
49
|
+
interaction.checkingFinish = true;
|
|
50
|
+
interaction.finishTimer = originalSetTimeout(() => {
|
|
51
|
+
interaction.checkingFinish = false;
|
|
52
|
+
interaction.finishTimer = originalSetTimeout(() => {
|
|
53
|
+
interaction.finishTimer = null;
|
|
54
|
+
if (interaction.remaining <= 0) interaction.finish();
|
|
55
|
+
}, 1);
|
|
56
|
+
}, 0);
|
|
57
|
+
};
|
|
58
|
+
InteractionPrototype.setNewURL = function setNewURL(url) {
|
|
59
|
+
this.root.attrs.newURL = url;
|
|
60
|
+
};
|
|
61
|
+
InteractionPrototype.setNewRoute = function setNewRoute(route) {
|
|
62
|
+
this.root.attrs.newRoute = route;
|
|
63
|
+
};
|
|
64
|
+
InteractionPrototype.onNodeAdded = function onNodeAdded() {
|
|
65
|
+
this._resetFinishCheck();
|
|
66
|
+
};
|
|
67
|
+
InteractionPrototype._resetFinishCheck = function _resetFinishCheck() {
|
|
68
|
+
if (this.finishTimer) {
|
|
69
|
+
originalClearTimeout(this.finishTimer);
|
|
70
|
+
this.finishTimer = null;
|
|
71
|
+
this.checkingFinish = false;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// serialize report and remove nodes from map
|
|
76
|
+
InteractionPrototype.finish = function finishInteraction() {
|
|
77
|
+
var interaction = this;
|
|
78
|
+
var root = interaction.root;
|
|
79
|
+
if (root.end !== null) return;
|
|
80
|
+
var endTimestamp = Math.max(interaction.lastCb, interaction.lastFinish);
|
|
81
|
+
var delta = now() - endTimestamp;
|
|
82
|
+
var attrs = root.attrs;
|
|
83
|
+
var customAttrs = attrs.custom;
|
|
84
|
+
if (this.onFinished) {
|
|
85
|
+
this.onFinished(this);
|
|
86
|
+
}
|
|
87
|
+
mapOwn(getInfo(interaction.agentIdentifier).jsAttributes, function (attr, value) {
|
|
88
|
+
if (!(attr in customAttrs)) customAttrs[attr] = value;
|
|
89
|
+
});
|
|
90
|
+
root.end = endTimestamp;
|
|
91
|
+
interaction.ee.emit('interaction', [this]);
|
|
92
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { cleanURL } from '../../../common/url/clean-url';
|
|
6
|
+
import { mapOwn } from '../../../common/util/map-own';
|
|
7
|
+
import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer';
|
|
8
|
+
import { SharedContext } from '../../../common/context/shared-context';
|
|
9
|
+
import { getInfo } from '../../../common/config/config';
|
|
10
|
+
export class Serializer extends SharedContext {
|
|
11
|
+
constructor(parent) {
|
|
12
|
+
super(parent);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* This variable is used to calculate an interactions ending offset when the
|
|
16
|
+
* harvest includes multiple interactions. This variable is set by the first
|
|
17
|
+
* interaction processed and used by subsequent interactions in the same harvest.
|
|
18
|
+
* See https://issues.newrelic.com/browse/NEWRELIC-5498
|
|
19
|
+
* @type {number|undefined}
|
|
20
|
+
*/
|
|
21
|
+
this.firstTimestamp = undefined;
|
|
22
|
+
}
|
|
23
|
+
serializeMultiple(interactions, offset, navTiming) {
|
|
24
|
+
const info = getInfo(this.sharedContext.agentIdentifier);
|
|
25
|
+
var addString = getAddStringContext(this.sharedContext.agentIdentifier);
|
|
26
|
+
var serialized = 'bel.7';
|
|
27
|
+
interactions.forEach(interaction => {
|
|
28
|
+
serialized += ';' + this.serializeInteraction(interaction.root, offset, navTiming, interaction.routeChange, addString, info);
|
|
29
|
+
});
|
|
30
|
+
this.firstTimestamp = undefined;
|
|
31
|
+
return serialized;
|
|
32
|
+
}
|
|
33
|
+
serializeSingle(root, offset, navTiming, isRouteChange) {
|
|
34
|
+
const info = getInfo(this.sharedContext.agentIdentifier);
|
|
35
|
+
var addString = getAddStringContext(this.sharedContext.agentIdentifier);
|
|
36
|
+
var serialized = 'bel.7;' + this.serializeInteraction(root, offset, navTiming, isRouteChange, addString, info);
|
|
37
|
+
this.firstTimestamp = undefined;
|
|
38
|
+
return serialized;
|
|
39
|
+
}
|
|
40
|
+
serializeInteraction(root, offset, navTiming, isRouteChange, addString, info) {
|
|
41
|
+
offset = offset || 0;
|
|
42
|
+
var isInitialPage = root.attrs.trigger === 'initialPageLoad';
|
|
43
|
+
var typeIdsByName = {
|
|
44
|
+
interaction: 1,
|
|
45
|
+
ajax: 2,
|
|
46
|
+
customTracer: 4
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Include the hash fragment with all SPA data
|
|
50
|
+
var includeHashFragment = true;
|
|
51
|
+
const addNode = (node, nodeList) => {
|
|
52
|
+
if (node.type === 'customEnd') return nodeList.push([3, numeric(node.end - this.firstTimestamp)]);
|
|
53
|
+
var typeName = node.type;
|
|
54
|
+
var typeId = typeIdsByName[typeName];
|
|
55
|
+
var startTimestamp = node.start;
|
|
56
|
+
var childCount = node.children.length;
|
|
57
|
+
var attrCount = 0;
|
|
58
|
+
var apmAttributes = info.atts;
|
|
59
|
+
var hasNavTiming = isInitialPage && navTiming.length && typeId === 1;
|
|
60
|
+
var children = [];
|
|
61
|
+
var attrs = node.attrs;
|
|
62
|
+
var metrics = attrs.metrics;
|
|
63
|
+
var params = attrs.params;
|
|
64
|
+
var queueTime = info.queueTime;
|
|
65
|
+
var appTime = info.applicationTime;
|
|
66
|
+
if (typeof this.firstTimestamp === 'undefined') {
|
|
67
|
+
startTimestamp += offset;
|
|
68
|
+
this.firstTimestamp = startTimestamp;
|
|
69
|
+
} else {
|
|
70
|
+
startTimestamp -= this.firstTimestamp;
|
|
71
|
+
}
|
|
72
|
+
var fields = [numeric(startTimestamp), numeric(node.end - node.start), numeric(node.jsEnd - node.end), numeric(node.jsTime)];
|
|
73
|
+
switch (typeId) {
|
|
74
|
+
case 1:
|
|
75
|
+
fields[2] = numeric(node.jsEnd - this.firstTimestamp);
|
|
76
|
+
fields.push(addString(attrs.trigger), addString(cleanURL(attrs.initialPageURL, includeHashFragment)), addString(cleanURL(attrs.oldURL, includeHashFragment)), addString(cleanURL(attrs.newURL, includeHashFragment)), addString(attrs.customName), isInitialPage ? '' : isRouteChange ? 1 : 2, nullable(isInitialPage && queueTime, numeric, true) + nullable(isInitialPage && appTime, numeric, true) + nullable(attrs.oldRoute, addString, true) + nullable(attrs.newRoute, addString, true) + addString(attrs.id), addString(node.id), nullable(attrs.firstPaint, numeric, true) + nullable(attrs.firstContentfulPaint, numeric, false));
|
|
77
|
+
var attrParts = addCustomAttributes(attrs.custom, addString);
|
|
78
|
+
children = children.concat(attrParts);
|
|
79
|
+
attrCount = attrParts.length;
|
|
80
|
+
if (apmAttributes) {
|
|
81
|
+
childCount++;
|
|
82
|
+
children.push('a,' + addString(apmAttributes));
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case 2:
|
|
86
|
+
fields.push(addString(params.method), numeric(params.status), addString(params.host), addString(params.pathname), numeric(metrics.txSize), numeric(metrics.rxSize), attrs.isFetch ? 1 : attrs.isJSONP ? 2 : '', addString(node.id), nullable(node.dt && node.dt.spanId, addString, true) + nullable(node.dt && node.dt.traceId, addString, true) + nullable(node.dt && node.dt.timestamp, numeric, false));
|
|
87
|
+
break;
|
|
88
|
+
case 4:
|
|
89
|
+
var tracedTime = attrs.tracedTime;
|
|
90
|
+
fields.push(addString(attrs.name), nullable(tracedTime, numeric, true) + addString(node.id));
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
for (var i = 0; i < node.children.length; i++) {
|
|
94
|
+
addNode(node.children[i], children);
|
|
95
|
+
}
|
|
96
|
+
fields.unshift(numeric(typeId), numeric(childCount += attrCount));
|
|
97
|
+
nodeList.push(fields);
|
|
98
|
+
if (childCount) {
|
|
99
|
+
nodeList.push(children.join(';'));
|
|
100
|
+
}
|
|
101
|
+
if (hasNavTiming) {
|
|
102
|
+
// this build up the navTiming node
|
|
103
|
+
// it for each navTiming value (pre aggregated in nav-timing.js):
|
|
104
|
+
// we initialize the seperator to ',' (seperates the nodeType id from the first value)
|
|
105
|
+
// we initialize the navTiming node to 'b' (the nodeType id)
|
|
106
|
+
// if the value is present:
|
|
107
|
+
// we add the seperator followed by the value
|
|
108
|
+
// otherwise
|
|
109
|
+
// we add null seperator ('!') to the navTimingNode
|
|
110
|
+
// we set the seperator to an empty string since we already wrote it above
|
|
111
|
+
// the reason for writing the null seperator instead of setting the seperator
|
|
112
|
+
// is to ensure we still write it if the null is the last navTiming value.
|
|
113
|
+
|
|
114
|
+
var seperator = ',';
|
|
115
|
+
var navTimingNode = 'b';
|
|
116
|
+
var prev = 0;
|
|
117
|
+
|
|
118
|
+
// get all navTiming values except navigationStart
|
|
119
|
+
// (since its the same as interaction.start)
|
|
120
|
+
// and limit to just the first 20 values we know about
|
|
121
|
+
mapOwn(navTiming.slice(1, 21), function (i, v) {
|
|
122
|
+
if (v !== void 0) {
|
|
123
|
+
navTimingNode += seperator + numeric(v - prev);
|
|
124
|
+
seperator = ',';
|
|
125
|
+
prev = v;
|
|
126
|
+
} else {
|
|
127
|
+
navTimingNode += seperator + '!';
|
|
128
|
+
seperator = '';
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
nodeList.push(navTimingNode);
|
|
132
|
+
} else if (typeId === 1) {
|
|
133
|
+
nodeList.push('');
|
|
134
|
+
}
|
|
135
|
+
return nodeList;
|
|
136
|
+
};
|
|
137
|
+
return addNode(root, []).join(';');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { originals } from '../../common/config/config';
|
|
2
|
+
import { globalScope } from '../../common/util/global-scope';
|
|
3
|
+
import { FEATURE_NAMES } from '../../loaders/features/features';
|
|
4
|
+
export const FEATURE_NAME = FEATURE_NAMES.spa;
|
|
5
|
+
export const INTERACTION_EVENTS = ['click', 'submit', 'keypress', 'keydown', 'keyup', 'change'];
|
|
6
|
+
export const MAX_TIMER_BUDGET = 999;
|
|
7
|
+
export const FN_START = 'fn-start';
|
|
8
|
+
export const FN_END = 'fn-end';
|
|
9
|
+
export const CB_START = 'cb-start';
|
|
10
|
+
export const INTERACTION_API = 'api-ixn-';
|
|
11
|
+
export const REMAINING = 'remaining';
|
|
12
|
+
export const INTERACTION = 'interaction';
|
|
13
|
+
export const SPA_NODE = 'spaNode';
|
|
14
|
+
export const JSONP_NODE = 'jsonpNode';
|
|
15
|
+
export const FETCH_START = 'fetch-start';
|
|
16
|
+
export const FETCH_DONE = 'fetch-done';
|
|
17
|
+
export const FETCH_BODY = 'fetch-body-';
|
|
18
|
+
export const JSONP_END = 'jsonp-end';
|
|
19
|
+
export const originalSetTimeout = originals.ST;
|
|
20
|
+
export const START = '-start';
|
|
21
|
+
export const END = '-end';
|
|
22
|
+
export const BODY = '-body';
|
|
23
|
+
export const CB_END = 'cb' + END;
|
|
24
|
+
export const JS_TIME = 'jsTime';
|
|
25
|
+
export const FETCH = 'fetch';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Instrument as Spa } from './instrument/index';
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { wrapMutation, wrapPromise, wrapHistory, wrapTimer, wrapFetch, wrapXhr, wrapJsonP } from '../../../common/wrap';
|
|
6
|
+
import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts';
|
|
7
|
+
import { InstrumentBase } from '../../utils/instrument-base';
|
|
8
|
+
import { getRuntime } from '../../../common/config/config';
|
|
9
|
+
import { now } from '../../../common/timing/now';
|
|
10
|
+
import * as CONSTANTS from '../constants';
|
|
11
|
+
import { isBrowserScope } from '../../../common/util/global-scope';
|
|
12
|
+
const {
|
|
13
|
+
FEATURE_NAME,
|
|
14
|
+
START,
|
|
15
|
+
END,
|
|
16
|
+
BODY,
|
|
17
|
+
CB_END,
|
|
18
|
+
JS_TIME,
|
|
19
|
+
FETCH,
|
|
20
|
+
FN_START,
|
|
21
|
+
CB_START,
|
|
22
|
+
FN_END
|
|
23
|
+
} = CONSTANTS;
|
|
24
|
+
export class Instrument extends InstrumentBase {
|
|
25
|
+
static featureName = FEATURE_NAME;
|
|
26
|
+
constructor(agentIdentifier, aggregator, auto = true) {
|
|
27
|
+
super(agentIdentifier, aggregator, FEATURE_NAME, auto);
|
|
28
|
+
if (!isBrowserScope) return; // SPA not supported outside web env
|
|
29
|
+
|
|
30
|
+
if (!getRuntime(agentIdentifier).xhrWrappable) return;
|
|
31
|
+
try {
|
|
32
|
+
this.removeOnAbort = new AbortController();
|
|
33
|
+
} catch (e) {}
|
|
34
|
+
let depth = 0;
|
|
35
|
+
let startHash;
|
|
36
|
+
const tracerEE = this.ee.get('tracer');
|
|
37
|
+
const jsonpEE = wrapJsonP(this.ee);
|
|
38
|
+
const promiseEE = wrapPromise(this.ee);
|
|
39
|
+
const timerEE = wrapTimer(this.ee);
|
|
40
|
+
const xhrEE = wrapXhr(this.ee);
|
|
41
|
+
const eventsEE = this.ee.get('events'); // wrapXhr will call wrapEvents
|
|
42
|
+
const fetchEE = wrapFetch(this.ee);
|
|
43
|
+
const historyEE = wrapHistory(this.ee);
|
|
44
|
+
const mutationEE = wrapMutation(this.ee);
|
|
45
|
+
this.ee.on(FN_START, startTimestamp);
|
|
46
|
+
promiseEE.on(CB_START, startTimestamp);
|
|
47
|
+
jsonpEE.on(CB_START, startTimestamp);
|
|
48
|
+
this.ee.on(FN_END, endTimestamp);
|
|
49
|
+
promiseEE.on(CB_END, endTimestamp);
|
|
50
|
+
jsonpEE.on(CB_END, endTimestamp);
|
|
51
|
+
this.ee.buffer([FN_START, FN_END, 'xhr-resolved'], this.featureName);
|
|
52
|
+
eventsEE.buffer([FN_START], this.featureName);
|
|
53
|
+
timerEE.buffer(['setTimeout' + END, 'clearTimeout' + START, FN_START], this.featureName);
|
|
54
|
+
xhrEE.buffer([FN_START, 'new-xhr', 'send-xhr' + START], this.featureName);
|
|
55
|
+
fetchEE.buffer([FETCH + START, FETCH + '-done', FETCH + BODY + START, FETCH + BODY + END], this.featureName);
|
|
56
|
+
historyEE.buffer(['newURL'], this.featureName);
|
|
57
|
+
mutationEE.buffer([FN_START], this.featureName);
|
|
58
|
+
promiseEE.buffer(['propagate', CB_START, CB_END, 'executor-err', 'resolve' + START], this.featureName);
|
|
59
|
+
tracerEE.buffer([FN_START, 'no-' + FN_START], this.featureName);
|
|
60
|
+
jsonpEE.buffer(['new-jsonp', 'cb-start', 'jsonp-error', 'jsonp-end'], this.featureName);
|
|
61
|
+
timestamp(fetchEE, FETCH + START);
|
|
62
|
+
timestamp(fetchEE, FETCH + '-done');
|
|
63
|
+
timestamp(jsonpEE, 'new-jsonp');
|
|
64
|
+
timestamp(jsonpEE, 'jsonp-end');
|
|
65
|
+
timestamp(jsonpEE, 'cb-start');
|
|
66
|
+
historyEE.on('pushState-end', trackURLChange);
|
|
67
|
+
historyEE.on('replaceState-end', trackURLChange);
|
|
68
|
+
window.addEventListener('hashchange', trackURLChange, eventListenerOpts(true, this.removeOnAbort?.signal));
|
|
69
|
+
window.addEventListener('load', trackURLChange, eventListenerOpts(true, this.removeOnAbort?.signal));
|
|
70
|
+
window.addEventListener('popstate', function () {
|
|
71
|
+
trackURLChange(0, depth > 1);
|
|
72
|
+
}, eventListenerOpts(true, this.removeOnAbort?.signal));
|
|
73
|
+
function trackURLChange(unusedArgs, hashChangedDuringCb) {
|
|
74
|
+
historyEE.emit('newURL', ['' + window.location, hashChangedDuringCb]);
|
|
75
|
+
}
|
|
76
|
+
function startTimestamp() {
|
|
77
|
+
depth++;
|
|
78
|
+
startHash = window.location.hash;
|
|
79
|
+
this[FN_START] = now();
|
|
80
|
+
}
|
|
81
|
+
function endTimestamp() {
|
|
82
|
+
depth--;
|
|
83
|
+
if (window.location.hash !== startHash) {
|
|
84
|
+
trackURLChange(0, true);
|
|
85
|
+
}
|
|
86
|
+
var time = now();
|
|
87
|
+
this[JS_TIME] = ~~this[JS_TIME] + time - this[FN_START];
|
|
88
|
+
this[FN_END] = time;
|
|
89
|
+
}
|
|
90
|
+
function timestamp(ee, type) {
|
|
91
|
+
ee.on(type, function () {
|
|
92
|
+
this[type] = now();
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
this.abortHandler = this.#abort;
|
|
96
|
+
this.importAggregator();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Restoration and resource release tasks to be done if SPA loader is being aborted. Unwind changes to globals and subscription to DOM events. */
|
|
100
|
+
#abort() {
|
|
101
|
+
this.removeOnAbort?.abort();
|
|
102
|
+
this.abortHandler = undefined; // weakly allow this abort op to run only once
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { getInfo, isConfigured, getRuntime } from '../../common/config/config';
|
|
2
|
+
import { ee } from '../../common/event-emitter/contextual-ee';
|
|
3
|
+
import { configure } from '../../loaders/configure/configure';
|
|
4
|
+
import { gosCDN } from '../../common/window/nreum';
|
|
5
|
+
export class FeatureBase {
|
|
6
|
+
constructor(agentIdentifier, aggregator, featureName) {
|
|
7
|
+
/** @type {string} */
|
|
8
|
+
this.agentIdentifier = agentIdentifier;
|
|
9
|
+
/** @type {Aggregator} */
|
|
10
|
+
this.aggregator = aggregator;
|
|
11
|
+
/** @type {ContextualEE} */
|
|
12
|
+
this.ee = ee.get(agentIdentifier, getRuntime(this.agentIdentifier).isolatedBacklog);
|
|
13
|
+
/** @type {string} */
|
|
14
|
+
this.featureName = featureName;
|
|
15
|
+
/**
|
|
16
|
+
* Blocked can be used to prevent aggregation and harvest after inititalization time of the feature.
|
|
17
|
+
* This can currently happen if RUM response setToken flag is 0, which is tied to ingest account entitlement info.
|
|
18
|
+
* @type {boolean}
|
|
19
|
+
*/
|
|
20
|
+
this.blocked = false;
|
|
21
|
+
this.checkConfiguration();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Checks for additional `jsAttributes` items to support backward compatibility with implementations of the agent where
|
|
26
|
+
* loader configurations may appear after the loader code is executed.
|
|
27
|
+
*/
|
|
28
|
+
checkConfiguration() {
|
|
29
|
+
// NOTE: This check has to happen at aggregator load time, but could be moved to `AggregateBase`.
|
|
30
|
+
if (!isConfigured(this.agentIdentifier)) {
|
|
31
|
+
let jsAttributes = {
|
|
32
|
+
...gosCDN().info?.jsAttributes
|
|
33
|
+
};
|
|
34
|
+
try {
|
|
35
|
+
jsAttributes = {
|
|
36
|
+
...jsAttributes,
|
|
37
|
+
...getInfo(this.agentIdentifier)?.jsAttributes
|
|
38
|
+
};
|
|
39
|
+
} catch (err) {
|
|
40
|
+
// do nothing
|
|
41
|
+
}
|
|
42
|
+
configure(this.agentIdentifier, {
|
|
43
|
+
...gosCDN(),
|
|
44
|
+
info: {
|
|
45
|
+
...gosCDN().info,
|
|
46
|
+
jsAttributes
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A class to defer callback execution until a decision can be reached
|
|
3
|
+
*/
|
|
4
|
+
export class HandlerCache {
|
|
5
|
+
/** @private @type {boolean | undefined} */
|
|
6
|
+
#decision = undefined;
|
|
7
|
+
/** @private @type {boolean} */
|
|
8
|
+
#shouldsettle = true;
|
|
9
|
+
/** @private @type {Function[]} */
|
|
10
|
+
#cache = [];
|
|
11
|
+
/** @private @type {Timeout} */
|
|
12
|
+
#settleTimer = setTimeout(() => this.#close(), 5000);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* tell the handlerCache that its ok to immediately execute the callbacks that are triggered by the ee from this moment on
|
|
16
|
+
* and execute all the storage callbacks saved up in the handlerCache
|
|
17
|
+
* @private
|
|
18
|
+
*/
|
|
19
|
+
#drain() {
|
|
20
|
+
this.#cache.forEach(h => h());
|
|
21
|
+
this.#cache = [];
|
|
22
|
+
clearTimeout(this.#settleTimer);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* tell the handlerCache not to execute any of the storage callbacks
|
|
27
|
+
* and wipe out all the storage callbacks saved up in the handlerCache
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
#close() {
|
|
31
|
+
this.#shouldsettle = false;
|
|
32
|
+
this.#cache = [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Wrap callback functions with this method to defer their execution until a decision has been reached
|
|
37
|
+
* @param {Function} handler
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
40
|
+
settle(handler) {
|
|
41
|
+
if (this.#decision === false || this.#shouldsettle === false) return;else if (this.#decision === undefined) this.#cache.push(handler);else handler();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Make a decision about what to do with the cache of callbacks.
|
|
46
|
+
* --- if true: tell the handlerCache that its ok to immediately execute the callbacks that are triggered by the ee from this moment on
|
|
47
|
+
* and execute all the storage callbacks saved up in the handlerCache ---
|
|
48
|
+
* --- if false: tell the handlerCache not to execute any of the storage callbacks
|
|
49
|
+
* and wipe out all the storage callbacks saved up in the handlerCache
|
|
50
|
+
* @param {boolean} decision
|
|
51
|
+
*/
|
|
52
|
+
decide(decision) {
|
|
53
|
+
this.#decision = decision;
|
|
54
|
+
if (decision === false) this.#close();
|
|
55
|
+
if (decision === true) this.#drain();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Defines `InstrumentBase` to be used as the super of the Instrument classes implemented by each feature.
|
|
3
|
+
* Inherits and executes the `checkConfiguration` method from [FeatureBase]{@link ./feature-base}, which also
|
|
4
|
+
* exposes the `blocked` property.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { registerDrain } from '../../common/drain/drain';
|
|
8
|
+
import { FeatureBase } from './feature-base';
|
|
9
|
+
import { onWindowLoad } from '../../common/window/load';
|
|
10
|
+
import { isWorkerScope } from '../../common/util/global-scope';
|
|
11
|
+
import { warn } from '../../common/util/console';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Base class for instrumenting a feature.
|
|
15
|
+
* @extends FeatureBase
|
|
16
|
+
*/
|
|
17
|
+
export class InstrumentBase extends FeatureBase {
|
|
18
|
+
/**
|
|
19
|
+
* Instantiate InstrumentBase.
|
|
20
|
+
* @param {string} agentIdentifier - The unique ID of the instantiated agent (relative to global scope).
|
|
21
|
+
* @param {Aggregator} aggregator - The shared Aggregator that will handle batching and reporting of data.
|
|
22
|
+
* @param {string} featureName - The name of the feature module (used to construct file path).
|
|
23
|
+
* @param {boolean} [auto=true] - Determines whether the feature should automatically register to have the draining
|
|
24
|
+
* of its pooled instrumentation data handled by the agent's centralized drain functionality, rather than draining
|
|
25
|
+
* immediately. Primarily useful for fine-grained control in tests.
|
|
26
|
+
*/
|
|
27
|
+
constructor(agentIdentifier, aggregator, featureName, auto = true) {
|
|
28
|
+
super(agentIdentifier, aggregator, featureName);
|
|
29
|
+
this.hasAggregator = false;
|
|
30
|
+
this.auto = auto;
|
|
31
|
+
|
|
32
|
+
/** @type {Function | undefined} This should be set by any derived Instrument class if it has things to do when feature fails or is killed. */
|
|
33
|
+
this.abortHandler;
|
|
34
|
+
if (auto) registerDrain(agentIdentifier, featureName);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Lazy-load the latter part of the feature: its aggregator. This method is called by the first part of the feature
|
|
39
|
+
* (the instrumentation) when instrumentation is complete.
|
|
40
|
+
*/
|
|
41
|
+
importAggregator() {
|
|
42
|
+
if (this.hasAggregator || !this.auto) return;
|
|
43
|
+
this.hasAggregator = true;
|
|
44
|
+
const importLater = async () => {
|
|
45
|
+
/**
|
|
46
|
+
* Note this try-catch differs from the one in Agent.start() in that it's placed later in a page's lifecycle and
|
|
47
|
+
* it's only responsible for aborting its one specific feature, rather than all.
|
|
48
|
+
*/
|
|
49
|
+
try {
|
|
50
|
+
const {
|
|
51
|
+
lazyLoader
|
|
52
|
+
} = await import( /* webpackChunkName: "lazy-loader" */'./lazy-loader');
|
|
53
|
+
const {
|
|
54
|
+
Aggregate
|
|
55
|
+
} = await lazyLoader(this.featureName, 'aggregate');
|
|
56
|
+
new Aggregate(this.agentIdentifier, this.aggregator);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
warn(`Downloading ${this.featureName} failed...`);
|
|
59
|
+
this.abortHandler?.(); // undo any important alterations made to the page
|
|
60
|
+
|
|
61
|
+
// not supported yet but nice to do: "abort" this agent's EE for this feature specifically
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// For regular web pages, we want to wait and lazy-load the aggregator only after all page resources are loaded.
|
|
66
|
+
// Non-browser scopes (i.e. workers) have no `window.load` event, so the aggregator can be lazy-loaded immediately.
|
|
67
|
+
if (isWorkerScope) importLater();else onWindowLoad(() => importLater(), true);
|
|
68
|
+
}
|
|
69
|
+
}
|