@newrelic/browser-agent 1.276.0 → 1.278.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/cjs/common/aggregate/event-aggregator.js +1 -1
- package/dist/cjs/common/config/init.js +1 -10
- package/dist/cjs/common/config/runtime.js +2 -1
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/harvest/harvester.js +255 -0
- package/dist/cjs/common/harvest/types.js +5 -21
- package/dist/cjs/features/ajax/aggregate/index.js +2 -11
- package/dist/cjs/features/generic_events/aggregate/index.js +17 -13
- package/dist/cjs/features/generic_events/constants.js +2 -1
- package/dist/cjs/features/jserrors/aggregate/index.js +3 -14
- package/dist/cjs/features/logging/aggregate/index.js +4 -12
- package/dist/cjs/features/metrics/aggregate/index.js +7 -15
- package/dist/cjs/features/page_view_event/aggregate/index.js +46 -48
- package/dist/cjs/features/page_view_timing/aggregate/index.js +0 -9
- package/dist/cjs/features/session_replay/aggregate/index.js +21 -43
- package/dist/cjs/features/session_replay/instrument/index.js +2 -1
- package/dist/cjs/features/session_replay/shared/recorder.js +6 -6
- package/dist/cjs/features/session_trace/aggregate/index.js +9 -24
- package/dist/cjs/features/session_trace/aggregate/trace/storage.js +8 -2
- package/dist/cjs/features/soft_navigations/aggregate/index.js +31 -21
- package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +2 -1
- package/dist/cjs/features/soft_navigations/aggregate/interaction.js +12 -12
- package/dist/cjs/features/soft_navigations/constants.js +5 -2
- package/dist/cjs/features/spa/aggregate/index.js +7 -10
- package/dist/cjs/features/utils/aggregate-base.js +66 -27
- package/dist/cjs/features/utils/event-buffer.js +0 -1
- package/dist/cjs/features/utils/event-store-manager.js +109 -0
- package/dist/cjs/features/utils/instrument-base.js +1 -10
- package/dist/cjs/loaders/api/api-methods.js +1 -1
- package/dist/cjs/loaders/api/api.js +2 -1
- package/dist/cjs/loaders/features/features.js +16 -10
- package/dist/cjs/loaders/micro-agent-base.js +10 -0
- package/dist/cjs/loaders/micro-agent.js +1 -0
- package/dist/esm/common/aggregate/event-aggregator.js +1 -1
- package/dist/esm/common/config/init.js +1 -10
- package/dist/esm/common/config/runtime.js +2 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/harvest/harvester.js +249 -0
- package/dist/esm/common/harvest/types.js +5 -21
- package/dist/esm/features/ajax/aggregate/index.js +3 -12
- package/dist/esm/features/generic_events/aggregate/index.js +18 -14
- package/dist/esm/features/generic_events/constants.js +1 -0
- package/dist/esm/features/jserrors/aggregate/index.js +4 -15
- package/dist/esm/features/logging/aggregate/index.js +4 -12
- package/dist/esm/features/metrics/aggregate/index.js +7 -15
- package/dist/esm/features/page_view_event/aggregate/index.js +46 -48
- package/dist/esm/features/page_view_timing/aggregate/index.js +1 -10
- package/dist/esm/features/session_replay/aggregate/index.js +22 -44
- package/dist/esm/features/session_replay/instrument/index.js +2 -1
- package/dist/esm/features/session_replay/shared/recorder.js +6 -6
- package/dist/esm/features/session_trace/aggregate/index.js +9 -24
- package/dist/esm/features/session_trace/aggregate/trace/storage.js +8 -2
- package/dist/esm/features/soft_navigations/aggregate/index.js +33 -23
- package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +2 -1
- package/dist/esm/features/soft_navigations/aggregate/interaction.js +13 -13
- package/dist/esm/features/soft_navigations/constants.js +4 -1
- package/dist/esm/features/spa/aggregate/index.js +8 -11
- package/dist/esm/features/utils/aggregate-base.js +66 -27
- package/dist/esm/features/utils/event-buffer.js +0 -1
- package/dist/esm/features/utils/event-store-manager.js +103 -0
- package/dist/esm/features/utils/instrument-base.js +1 -10
- package/dist/esm/loaders/api/api-methods.js +1 -1
- package/dist/esm/loaders/api/api.js +2 -1
- package/dist/esm/loaders/features/features.js +15 -9
- package/dist/esm/loaders/micro-agent-base.js +10 -0
- package/dist/esm/loaders/micro-agent.js +1 -0
- package/dist/types/common/aggregate/event-aggregator.d.ts +1 -1
- package/dist/types/common/aggregate/event-aggregator.d.ts.map +1 -1
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/config/runtime.d.ts.map +1 -1
- package/dist/types/common/harvest/harvester.d.ts +16 -0
- package/dist/types/common/harvest/harvester.d.ts.map +1 -0
- package/dist/types/common/harvest/types.d.ts +8 -45
- package/dist/types/common/harvest/types.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/aggregate/index.d.ts +1 -3
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/constants.d.ts +1 -0
- package/dist/types/features/generic_events/constants.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/logging/aggregate/index.d.ts +0 -3
- package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts +6 -2
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +12 -15
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +0 -5
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +8 -5
- package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts +1 -0
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +3 -3
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/constants.d.ts +1 -0
- package/dist/types/features/soft_navigations/constants.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts +0 -1
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/aggregate-base.d.ts +12 -7
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/features/utils/event-buffer.d.ts +1 -2
- package/dist/types/features/utils/event-buffer.d.ts.map +1 -1
- package/dist/types/features/utils/event-store-manager.d.ts +43 -0
- package/dist/types/features/utils/event-store-manager.d.ts.map +1 -0
- package/dist/types/features/utils/instrument-base.d.ts +0 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts +1 -0
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/features/features.d.ts +15 -12
- package/dist/types/loaders/features/features.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent-base.d.ts +7 -0
- package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/common/aggregate/event-aggregator.js +1 -1
- package/src/common/config/init.js +9 -10
- package/src/common/config/runtime.js +2 -1
- package/src/common/harvest/__mocks__/harvester.js +6 -0
- package/src/common/harvest/harvester.js +230 -0
- package/src/common/harvest/types.js +5 -21
- package/src/features/ajax/aggregate/index.js +3 -14
- package/src/features/generic_events/aggregate/index.js +20 -17
- package/src/features/generic_events/constants.js +2 -0
- package/src/features/jserrors/aggregate/index.js +4 -11
- package/src/features/logging/aggregate/index.js +4 -12
- package/src/features/metrics/aggregate/index.js +5 -12
- package/src/features/page_view_event/aggregate/index.js +38 -38
- package/src/features/page_view_timing/aggregate/index.js +1 -12
- package/src/features/session_replay/aggregate/index.js +19 -42
- package/src/features/session_replay/instrument/index.js +1 -1
- package/src/features/session_replay/shared/recorder.js +6 -6
- package/src/features/session_trace/aggregate/index.js +8 -25
- package/src/features/session_trace/aggregate/trace/storage.js +5 -2
- package/src/features/soft_navigations/aggregate/index.js +26 -23
- package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +2 -1
- package/src/features/soft_navigations/aggregate/interaction.js +13 -12
- package/src/features/soft_navigations/constants.js +3 -1
- package/src/features/spa/aggregate/index.js +8 -11
- package/src/features/utils/aggregate-base.js +59 -27
- package/src/features/utils/event-buffer.js +0 -1
- package/src/features/utils/event-store-manager.js +101 -0
- package/src/features/utils/instrument-base.js +2 -8
- package/src/loaders/api/api-methods.js +1 -1
- package/src/loaders/api/api.js +3 -1
- package/src/loaders/features/features.js +16 -9
- package/src/loaders/micro-agent-base.js +10 -0
- package/src/loaders/micro-agent.js +1 -0
- package/dist/cjs/common/harvest/harvest-scheduler.js +0 -168
- package/dist/cjs/common/harvest/harvest.js +0 -295
- package/dist/esm/common/harvest/harvest-scheduler.js +0 -160
- package/dist/esm/common/harvest/harvest.js +0 -286
- package/dist/types/common/harvest/harvest-scheduler.d.ts +0 -50
- package/dist/types/common/harvest/harvest-scheduler.d.ts.map +0 -1
- package/dist/types/common/harvest/harvest.d.ts +0 -65
- package/dist/types/common/harvest/harvest.d.ts.map +0 -1
- package/src/common/harvest/__mocks__/harvest.js +0 -13
- package/src/common/harvest/harvest-scheduler.js +0 -166
- package/src/common/harvest/harvest.js +0 -282
|
@@ -10,11 +10,10 @@ import { navTimingValues as navTiming } from '../../../common/timing/nav-timing'
|
|
|
10
10
|
import { generateUuid } from '../../../common/ids/unique-id'
|
|
11
11
|
import { Interaction } from './interaction'
|
|
12
12
|
import { eventListenerOpts } from '../../../common/event-listener/event-listener-opts'
|
|
13
|
-
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler'
|
|
14
13
|
import { Serializer } from './serializer'
|
|
15
14
|
import { ee } from '../../../common/event-emitter/contextual-ee'
|
|
16
15
|
import * as CONSTANTS from '../constants'
|
|
17
|
-
import { FEATURE_NAMES
|
|
16
|
+
import { FEATURE_NAMES } from '../../../loaders/features/features'
|
|
18
17
|
import { AggregateBase } from '../../utils/aggregate-base'
|
|
19
18
|
import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint'
|
|
20
19
|
import { firstPaint } from '../../../common/vitals/first-paint'
|
|
@@ -46,14 +45,12 @@ export class Aggregate extends AggregateBase {
|
|
|
46
45
|
pageLoaded: false,
|
|
47
46
|
childTime: 0,
|
|
48
47
|
depth: 0,
|
|
49
|
-
harvestTimeSeconds: agentRef.init.spa.harvestTimeSeconds || 10,
|
|
50
48
|
// The below feature flag is used to disable the SPA ajax fix for specific customers, see https://new-relic.atlassian.net/browse/NR-172169
|
|
51
49
|
disableSpaFix: (agentRef.init.feature_flags || []).indexOf('disable-spa-fix') > -1
|
|
52
50
|
}
|
|
53
51
|
this.spaSerializerClass = new Serializer(this)
|
|
54
52
|
|
|
55
53
|
const classThis = this
|
|
56
|
-
let scheduler
|
|
57
54
|
|
|
58
55
|
const baseEE = ee.get(agentRef.agentIdentifier) // <-- parent baseEE
|
|
59
56
|
const mutationEE = baseEE.get('mutation')
|
|
@@ -98,13 +95,10 @@ export class Aggregate extends AggregateBase {
|
|
|
98
95
|
// | click ending: | 65 | 50 | | | |
|
|
99
96
|
// click fn-end | 70 | 0 | 0 | 70 | 20 |
|
|
100
97
|
|
|
98
|
+
let harvester
|
|
101
99
|
this.waitForFlags((['spa'])).then(([spaFlag]) => {
|
|
102
100
|
if (spaFlag) {
|
|
103
|
-
|
|
104
|
-
onFinished: (result) => this.postHarvestCleanup(result.sent && result.retry),
|
|
105
|
-
getPayload: (options) => this.makeHarvestPayload(options.retry),
|
|
106
|
-
retryDelay: state.harvestTimeSeconds
|
|
107
|
-
}, this)
|
|
101
|
+
harvester = agentRef.runtime.harvester // since this is after RUM call, PVE would've initialized harvester by now
|
|
108
102
|
this.drain()
|
|
109
103
|
} else {
|
|
110
104
|
this.blocked = true
|
|
@@ -725,8 +719,11 @@ export class Aggregate extends AggregateBase {
|
|
|
725
719
|
else smCategory = 'Custom'
|
|
726
720
|
handle(SUPPORTABILITY_METRIC_CHANNEL, [`Spa/Interaction/${smCategory}/Duration/Ms`, Math.max((interaction.root?.end || 0) - (interaction.root?.start || 0), 0)], undefined, FEATURE_NAMES.metrics, baseEE)
|
|
727
721
|
|
|
728
|
-
|
|
729
|
-
|
|
722
|
+
if (!harvester) {
|
|
723
|
+
warn(19)
|
|
724
|
+
return
|
|
725
|
+
}
|
|
726
|
+
harvester.triggerHarvestFor(classThis)
|
|
730
727
|
}
|
|
731
728
|
}
|
|
732
729
|
|
|
@@ -5,19 +5,34 @@ import { gosCDN } from '../../common/window/nreum'
|
|
|
5
5
|
import { drain } from '../../common/drain/drain'
|
|
6
6
|
import { activatedFeatures } from '../../common/util/feature-flags'
|
|
7
7
|
import { Obfuscator } from '../../common/util/obfuscate'
|
|
8
|
-
import { EventBuffer } from './event-buffer'
|
|
9
8
|
import { FEATURE_NAMES } from '../../loaders/features/features'
|
|
9
|
+
import { EventStoreManager } from './event-store-manager'
|
|
10
|
+
import { Harvester } from '../../common/harvest/harvester'
|
|
10
11
|
|
|
11
12
|
export class AggregateBase extends FeatureBase {
|
|
12
13
|
constructor (agentRef, featureName) {
|
|
13
14
|
super(agentRef.agentIdentifier, featureName)
|
|
14
15
|
this.agentRef = agentRef
|
|
15
|
-
// Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
|
|
16
|
-
if ([FEATURE_NAMES.jserrors, FEATURE_NAMES.metrics].includes(this.featureName)) this.events = agentRef.sharedAggregator
|
|
17
|
-
// PVE has no need for eventBuffer, and SessionTrace has its own storage mechanism.
|
|
18
|
-
else if (![FEATURE_NAMES.pageViewEvent, FEATURE_NAMES.sessionTrace].includes(this.featureName)) this.events = new EventBuffer()
|
|
19
16
|
this.checkConfiguration(agentRef)
|
|
20
|
-
this.
|
|
17
|
+
this.doOnceForAllAggregate(agentRef)
|
|
18
|
+
|
|
19
|
+
// This switch needs to be after doOnceForAllAggregate which may new sharedAggregator and reset mainAppKey.
|
|
20
|
+
switch (this.featureName) {
|
|
21
|
+
// PVE has no need for eventBuffer, and SessionTrace + Replay have their own storage mechanisms.
|
|
22
|
+
case FEATURE_NAMES.pageViewEvent:
|
|
23
|
+
case FEATURE_NAMES.sessionTrace:
|
|
24
|
+
case FEATURE_NAMES.sessionReplay:
|
|
25
|
+
break
|
|
26
|
+
// Jserror and Metric features uses a singleton EventAggregator instead of a regular EventBuffer.
|
|
27
|
+
case FEATURE_NAMES.jserrors:
|
|
28
|
+
case FEATURE_NAMES.metrics:
|
|
29
|
+
this.events = agentRef.sharedAggregator
|
|
30
|
+
break
|
|
31
|
+
default:
|
|
32
|
+
this.events = new EventStoreManager(agentRef.mainAppKey, 1)
|
|
33
|
+
break
|
|
34
|
+
}
|
|
35
|
+
this.harvestOpts = {} // features aggregate classes can define custom opts for when their harvest is called
|
|
21
36
|
}
|
|
22
37
|
|
|
23
38
|
/**
|
|
@@ -56,34 +71,40 @@ export class AggregateBase extends FeatureBase {
|
|
|
56
71
|
/**
|
|
57
72
|
* Return harvest payload. A "serializer" function can be defined on a derived class to format the payload.
|
|
58
73
|
* @param {Boolean} shouldRetryOnFail - harvester flag to backup payload for retry later if harvest request fails; this should be moved to harvester logic
|
|
59
|
-
* @
|
|
74
|
+
* @param {object|undefined} target - the target app passed onto the event store manager to determine which app's data to return; if none provided, all apps data will be returned
|
|
75
|
+
* @returns {Array} Final payload tagged with their targeting browser app. The value of `payload` can be undefined if there are no pending events for an app. This should be a minimum length of 1.
|
|
60
76
|
*/
|
|
61
|
-
makeHarvestPayload (shouldRetryOnFail = false,
|
|
62
|
-
if (this.events.isEmpty(
|
|
77
|
+
makeHarvestPayload (shouldRetryOnFail = false, target) {
|
|
78
|
+
if (this.events.isEmpty(this.harvestOpts, target)) return
|
|
63
79
|
// Other conditions and things to do when preparing harvest that is required.
|
|
64
80
|
if (this.preHarvestChecks && !this.preHarvestChecks()) return
|
|
65
81
|
|
|
66
|
-
if (shouldRetryOnFail) this.events.save(
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this.events.clear(opts)
|
|
82
|
+
if (shouldRetryOnFail) this.events.save(this.harvestOpts, target)
|
|
83
|
+
const returnedDataArr = this.events.get(this.harvestOpts, target)
|
|
84
|
+
if (!returnedDataArr.length) throw new Error('Unexpected problem encountered. There should be at least one app for harvest!')
|
|
85
|
+
this.events.clear(this.harvestOpts, target)
|
|
71
86
|
|
|
72
|
-
|
|
73
|
-
body
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
87
|
+
return returnedDataArr.map(({ targetApp, data }) => {
|
|
88
|
+
// A serializer or formatter assists in creating the payload `body` from stored events on harvest when defined by derived feature class.
|
|
89
|
+
const body = this.serializer ? this.serializer(data) : data
|
|
90
|
+
const payload = {
|
|
91
|
+
body
|
|
92
|
+
}
|
|
93
|
+
// Constructs the payload `qs` for relevant features on harvest.
|
|
94
|
+
if (this.queryStringsBuilder) payload.qs = this.queryStringsBuilder(data)
|
|
95
|
+
|
|
96
|
+
return { targetApp, payload }
|
|
97
|
+
})
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
/**
|
|
81
101
|
* Cleanup task after a harvest.
|
|
82
|
-
* @param {
|
|
102
|
+
* @param {object} result - the cbResult object from the harvester's send method
|
|
83
103
|
*/
|
|
84
|
-
postHarvestCleanup (
|
|
85
|
-
|
|
86
|
-
this.events.
|
|
104
|
+
postHarvestCleanup (result = {}) {
|
|
105
|
+
const harvestFailed = result.sent && result.retry
|
|
106
|
+
if (harvestFailed) this.events.reloadSave(this.harvestOpts, result.targetApp)
|
|
107
|
+
this.events.clearSave(this.harvestOpts, result.targetApp)
|
|
87
108
|
}
|
|
88
109
|
|
|
89
110
|
/**
|
|
@@ -112,9 +133,20 @@ export class AggregateBase extends FeatureBase {
|
|
|
112
133
|
runtime: existingAgent.runtime
|
|
113
134
|
})
|
|
114
135
|
}
|
|
136
|
+
}
|
|
115
137
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
138
|
+
/**
|
|
139
|
+
* These are actions related to shared resources that should be initialized once by whichever feature Aggregate subclass loads first.
|
|
140
|
+
* This method should run after checkConfiguration, which may reset the agent's info/runtime object that is used here.
|
|
141
|
+
*/
|
|
142
|
+
doOnceForAllAggregate (agentRef) {
|
|
143
|
+
if (!agentRef.runtime.obfuscator) agentRef.runtime.obfuscator = new Obfuscator(this.agentIdentifier)
|
|
144
|
+
this.obfuscator = agentRef.runtime.obfuscator
|
|
145
|
+
|
|
146
|
+
if (!agentRef.mainAppKey) agentRef.mainAppKey = { licenseKey: agentRef.info.licenseKey, appId: agentRef.info.applicationID }
|
|
147
|
+
// Create a single Aggregator for this agent if DNE yet; to be used by jserror endpoint features.
|
|
148
|
+
if (!agentRef.sharedAggregator) agentRef.sharedAggregator = new EventStoreManager(agentRef.mainAppKey, 2)
|
|
149
|
+
|
|
150
|
+
if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new Harvester(agentRef)
|
|
119
151
|
}
|
|
120
152
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { EventAggregator } from '../../common/aggregate/event-aggregator'
|
|
2
|
+
import { EventBuffer } from './event-buffer'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This layer allows multiple browser entity apps, or "target", to each have their own segregated storage instance.
|
|
6
|
+
* The purpose is so the harvester can send data to different apps within the same agent. Each feature should have a manager if it needs this capability.
|
|
7
|
+
*/
|
|
8
|
+
export class EventStoreManager {
|
|
9
|
+
/**
|
|
10
|
+
* @param {object} defaultTarget - should contain licenseKey and appId of the main app from NREUM.info at startup
|
|
11
|
+
* @param {1|2} storageChoice - the type of storage to use in this manager; 'EventBuffer' (1), 'EventAggregator' (2)
|
|
12
|
+
*/
|
|
13
|
+
constructor (defaultTarget, storageChoice) {
|
|
14
|
+
this.mainApp = defaultTarget
|
|
15
|
+
this.StorageClass = storageChoice === 1 ? EventBuffer : EventAggregator
|
|
16
|
+
this.appStorageMap = new Map()
|
|
17
|
+
this.appStorageMap.set(defaultTarget, new this.StorageClass())
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// This class must contain an union of all methods from all supported storage classes and conceptualize away the target app argument.
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {object} optsIfPresent - exists if called during harvest interval, @see AggregateBase.makeHarvestPayload
|
|
24
|
+
* @param {object} target - specific app's storage to check; if not provided, this method takes into account all apps recorded by this manager
|
|
25
|
+
* @returns {boolean} True if the target's storage is empty, or target does not exist in map (defaults to all storages)
|
|
26
|
+
*/
|
|
27
|
+
isEmpty (optsIfPresent, target) {
|
|
28
|
+
if (target) {
|
|
29
|
+
if (!this.appStorageMap.has(target)) return true
|
|
30
|
+
else return this.appStorageMap.get(target).isEmpty(optsIfPresent)
|
|
31
|
+
}
|
|
32
|
+
for (const eventStore of this.appStorageMap.values()) {
|
|
33
|
+
if (!eventStore.isEmpty(optsIfPresent)) return false
|
|
34
|
+
}
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} event - the event element to store
|
|
40
|
+
* @param {object} target - the app to store event under; if not provided, this method adds to the main app from NREUM.info
|
|
41
|
+
* @returns {boolean} True if the event was successfully added
|
|
42
|
+
*/
|
|
43
|
+
add (event, target) {
|
|
44
|
+
if (target && !this.appStorageMap.has(target)) this.appStorageMap.set(target, new this.StorageClass())
|
|
45
|
+
return this.appStorageMap.get(target || this.mainApp).add(event)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** This is only used by the Metrics feature which has no need to add metric under a different app atm. */
|
|
49
|
+
addMetric (type, name, params, value) {
|
|
50
|
+
return this.appStorageMap.get(this.mainApp).addMetric(type, name, params, value)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {object} optsIfPresent - exists if called during harvest interval, @see AggregateBase.makeHarvestPayload
|
|
55
|
+
* @param {object} target - specific app to fetch; if not provided, this method fetches from all apps
|
|
56
|
+
* @returns {Array} Objects of `data` labeled with their respective `target` app to be sent to
|
|
57
|
+
*/
|
|
58
|
+
get (optsIfPresent, target) {
|
|
59
|
+
if (target) return [{ targetApp: target, data: this.appStorageMap.get(target)?.get(optsIfPresent) }]
|
|
60
|
+
const allPayloads = []
|
|
61
|
+
this.appStorageMap.forEach((eventStore, recordedTarget) => {
|
|
62
|
+
allPayloads.push({ targetApp: recordedTarget, data: eventStore.get(optsIfPresent) })
|
|
63
|
+
})
|
|
64
|
+
return allPayloads
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
byteSize (target) {
|
|
68
|
+
return this.appStorageMap.get(target || this.mainApp).byteSize()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
wouldExceedMaxSize (incomingSize, target) {
|
|
72
|
+
return this.appStorageMap.get(target || this.mainApp).wouldExceedMaxSize(incomingSize)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
save (optsIfPresent, target) {
|
|
76
|
+
if (target) return this.appStorageMap.get(target)?.save(optsIfPresent)
|
|
77
|
+
this.appStorageMap.forEach((eventStore) => eventStore.save(optsIfPresent))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
clear (optsIfPresent, target) {
|
|
81
|
+
if (target) return this.appStorageMap.get(target)?.clear(optsIfPresent)
|
|
82
|
+
this.appStorageMap.forEach((eventStore) => eventStore.clear(optsIfPresent))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Unlike the methods above, the following will have a target as they are called by AggregateBase.postHarvestCleanup callback on harvest finish after getting & sending the data.
|
|
86
|
+
reloadSave (optsIfPresent, target) {
|
|
87
|
+
if (!target) { // -- remove this block once the old harvest.js & harvest-schedule.js are deleted!
|
|
88
|
+
this.appStorageMap.forEach((eventStore) => eventStore.reloadSave(optsIfPresent))
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
return this.appStorageMap.get(target)?.reloadSave(optsIfPresent)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
clearSave (optsIfPresent, target) {
|
|
95
|
+
if (!target) { // -- remove this block once the old harvest.js & harvest-schedule.js are deleted!
|
|
96
|
+
this.appStorageMap.forEach((eventStore) => eventStore.clearSave(optsIfPresent))
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
return this.appStorageMap.get(target)?.clearSave(optsIfPresent)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -43,7 +43,6 @@ export class InstrumentBase extends FeatureBase {
|
|
|
43
43
|
/**
|
|
44
44
|
* @type {Promise} Assigned immediately after @see importAggregator runs. Serves as a signal for when the inner async fn finishes execution. Useful for features to await
|
|
45
45
|
* one another if there are inter-features dependencies.
|
|
46
|
-
* TODO: This is only used for the SPA feature component tests and should be refactored out.
|
|
47
46
|
*/
|
|
48
47
|
this.onAggregateImported = undefined
|
|
49
48
|
|
|
@@ -95,13 +94,6 @@ export class InstrumentBase extends FeatureBase {
|
|
|
95
94
|
* it's only responsible for aborting its one specific feature, rather than all.
|
|
96
95
|
*/
|
|
97
96
|
try {
|
|
98
|
-
// Create a single Aggregator for this agent if DNE yet; to be used by jserror endpoint features.
|
|
99
|
-
if (!agentRef.sharedAggregator) {
|
|
100
|
-
agentRef.sharedAggregator = import(/* webpackChunkName: "shared-aggregator" */ '../../common/aggregate/event-aggregator')
|
|
101
|
-
const { EventAggregator } = await agentRef.sharedAggregator
|
|
102
|
-
agentRef.sharedAggregator = new EventAggregator()
|
|
103
|
-
} else await agentRef.sharedAggregator // if another feature is already importing the aggregator, wait for it to finish
|
|
104
|
-
|
|
105
97
|
if (!this.#shouldImportAgg(this.featureName, session)) {
|
|
106
98
|
drain(this.agentIdentifier, this.featureName)
|
|
107
99
|
loadedSuccessfully(false) // aggregate module isn't loaded at all
|
|
@@ -110,6 +102,8 @@ export class InstrumentBase extends FeatureBase {
|
|
|
110
102
|
const { lazyFeatureLoader } = await import(/* webpackChunkName: "lazy-feature-loader" */ './lazy-feature-loader')
|
|
111
103
|
const { Aggregate } = await lazyFeatureLoader(this.featureName, 'aggregate')
|
|
112
104
|
this.featAggregate = new Aggregate(agentRef, argsObjFromInstrument)
|
|
105
|
+
|
|
106
|
+
agentRef.runtime.harvester.initializedAggregates.push(this.featAggregate) // "subscribe" the feature to future harvest intervals (PVE will start the timer)
|
|
113
107
|
loadedSuccessfully(true)
|
|
114
108
|
} catch (e) {
|
|
115
109
|
warn(34, e)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SR_EVENT_EMITTER_TYPES } from '../../features/session_replay/constants'
|
|
2
2
|
|
|
3
3
|
export const apiMethods = [
|
|
4
|
-
'setErrorHandler', 'finished', 'addToTrace', 'addRelease',
|
|
4
|
+
'setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'recordCustomEvent',
|
|
5
5
|
'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute',
|
|
6
6
|
'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start',
|
|
7
7
|
SR_EVENT_EMITTER_TYPES.RECORD, SR_EVENT_EMITTER_TYPES.PAUSE, 'log', 'wrapLogger'
|
package/src/loaders/api/api.js
CHANGED
|
@@ -72,6 +72,8 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
|
|
|
72
72
|
|
|
73
73
|
apiInterface.addPageAction = apiCall(prefix, 'addPageAction', true, FEATURE_NAMES.genericEvents)
|
|
74
74
|
|
|
75
|
+
apiInterface.recordCustomEvent = apiCall(prefix, 'recordCustomEvent', true, FEATURE_NAMES.genericEvents)
|
|
76
|
+
|
|
75
77
|
apiInterface.setPageViewName = function (name, host) {
|
|
76
78
|
if (typeof name !== 'string') return
|
|
77
79
|
if (name.charAt(0) !== '/') name = '/' + name
|
|
@@ -192,7 +194,7 @@ export function setAPI (agentIdentifier, forceDrain, runSoftNavOverSpa = false)
|
|
|
192
194
|
function apiCall (prefix, name, notSpa, bufferGroup) {
|
|
193
195
|
return function () {
|
|
194
196
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['API/' + name + '/called'], undefined, FEATURE_NAMES.metrics, instanceEE)
|
|
195
|
-
if (bufferGroup) handle(prefix + name, [now(), ...arguments], notSpa ? null : this, bufferGroup, instanceEE) // no bufferGroup means only the SM is emitted
|
|
197
|
+
if (bufferGroup) handle(prefix + name, [notSpa ? now() : performance.now(), ...arguments], notSpa ? null : this, bufferGroup, instanceEE) // no bufferGroup means only the SM is emitted
|
|
196
198
|
return notSpa ? undefined : this // returns the InteractionHandle which allows these methods to be chained
|
|
197
199
|
}
|
|
198
200
|
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
// To reduce build size a bit:
|
|
2
|
+
export const EVENTS = 'events'
|
|
3
|
+
export const JSERRORS = 'jserrors'
|
|
4
|
+
const BLOBS = 'browser/blobs'
|
|
5
|
+
export const RUM = 'rum'
|
|
6
|
+
|
|
1
7
|
export const FEATURE_NAMES = {
|
|
2
8
|
ajax: 'ajax',
|
|
3
9
|
genericEvents: 'generic_events',
|
|
4
|
-
jserrors:
|
|
10
|
+
jserrors: JSERRORS,
|
|
5
11
|
logging: 'logging',
|
|
6
12
|
metrics: 'metrics',
|
|
7
13
|
/**
|
|
@@ -35,14 +41,15 @@ export const featurePriority = {
|
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
export const FEATURE_TO_ENDPOINT = {
|
|
38
|
-
[FEATURE_NAMES.
|
|
39
|
-
[FEATURE_NAMES.
|
|
40
|
-
[FEATURE_NAMES.
|
|
41
|
-
[FEATURE_NAMES.
|
|
42
|
-
[FEATURE_NAMES.
|
|
43
|
-
[FEATURE_NAMES.
|
|
44
|
-
[FEATURE_NAMES.
|
|
45
|
-
[FEATURE_NAMES.
|
|
44
|
+
[FEATURE_NAMES.pageViewEvent]: RUM,
|
|
45
|
+
[FEATURE_NAMES.pageViewTiming]: EVENTS,
|
|
46
|
+
[FEATURE_NAMES.ajax]: EVENTS,
|
|
47
|
+
[FEATURE_NAMES.spa]: EVENTS,
|
|
48
|
+
[FEATURE_NAMES.softNav]: EVENTS,
|
|
49
|
+
[FEATURE_NAMES.metrics]: JSERRORS,
|
|
50
|
+
[FEATURE_NAMES.jserrors]: JSERRORS,
|
|
51
|
+
[FEATURE_NAMES.sessionTrace]: BLOBS,
|
|
52
|
+
[FEATURE_NAMES.sessionReplay]: BLOBS,
|
|
46
53
|
[FEATURE_NAMES.logging]: 'browser/logs',
|
|
47
54
|
[FEATURE_NAMES.genericEvents]: 'ins'
|
|
48
55
|
}
|
|
@@ -30,6 +30,16 @@ export class MicroAgentBase {
|
|
|
30
30
|
return this.#callMethod('addPageAction', name, attributes)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Records a custom event with a specified eventType and attributes.
|
|
35
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordCustomEvent/}
|
|
36
|
+
* @param {string} eventType The eventType to store the event as.
|
|
37
|
+
* @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}.
|
|
38
|
+
*/
|
|
39
|
+
recordCustomEvent (eventType, attributes) {
|
|
40
|
+
return this.#callMethod('recordCustomEvent', eventType, attributes)
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
/**
|
|
34
44
|
* Groups page views to help URL structure or to capture the URL's routing information.
|
|
35
45
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setpageviewname/}
|
|
@@ -64,6 +64,7 @@ export class MicroAgent extends MicroAgentBase {
|
|
|
64
64
|
return lazyFeatureLoader(f, 'aggregate')
|
|
65
65
|
}).then(({ Aggregate }) => {
|
|
66
66
|
this.features[f] = new Aggregate(this)
|
|
67
|
+
this.runtime.harvester.initializedAggregates.push(this.features[f]) // so that harvester will poll this feature agg on interval
|
|
67
68
|
}).catch(err => warn(25, err))
|
|
68
69
|
}
|
|
69
70
|
})
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.HarvestScheduler = void 0;
|
|
7
|
-
var submitData = _interopRequireWildcard(require("../util/submit-data"));
|
|
8
|
-
var _sharedContext = require("../context/shared-context");
|
|
9
|
-
var _harvest = require("./harvest");
|
|
10
|
-
var _eol = require("../unload/eol");
|
|
11
|
-
var _constants = require("../session/constants");
|
|
12
|
-
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
13
|
-
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
14
|
-
/*
|
|
15
|
-
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
16
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Periodically invokes harvest calls and handles retries
|
|
21
|
-
*/
|
|
22
|
-
class HarvestScheduler extends _sharedContext.SharedContext {
|
|
23
|
-
/**
|
|
24
|
-
* Create a HarvestScheduler
|
|
25
|
-
* @param {string} endpoint - The base BAM endpoint name -- ex. 'events'
|
|
26
|
-
* @param {object} opts - The options used to configure the HarvestScheduler
|
|
27
|
-
* @param {Function} opts.onFinished - The callback to be fired when a harvest has finished
|
|
28
|
-
* @param {Function} opts.getPayload - A callback which can be triggered to return a payload for harvesting
|
|
29
|
-
* @param {number} opts.retryDelay - The number of seconds to wait before retrying after a network failure
|
|
30
|
-
* @param {boolean} opts.raw - Use a prefabricated payload shape as the harvest payload without the need for formatting
|
|
31
|
-
* @param {string} opts.customUrl - A custom url that falls outside of the shape of the standard BAM harvester url pattern. Will use directly instead of concatenating various pieces
|
|
32
|
-
* @param {*} parent - The parent object, whose state can be passed into SharedContext
|
|
33
|
-
*/
|
|
34
|
-
constructor(endpoint, opts, parent) {
|
|
35
|
-
super(parent); // gets any allowed properties from the parent and stores them in `sharedContext`
|
|
36
|
-
this.endpoint = endpoint;
|
|
37
|
-
this.opts = opts || {};
|
|
38
|
-
this.started = false;
|
|
39
|
-
this.timeoutHandle = null;
|
|
40
|
-
this.aborted = false; // this controls the per-interval and final harvests for the scheduler (currently per feature specific!)
|
|
41
|
-
this.harvesting = false;
|
|
42
|
-
this.harvest = new _harvest.Harvest(this.sharedContext);
|
|
43
|
-
|
|
44
|
-
// If a feature specifies stuff to be done on page unload, those are frontrunned (via capture phase) before ANY feature final harvests.
|
|
45
|
-
if (typeof this.opts.onUnload === 'function') (0, _eol.subscribeToEOL)(this.opts.onUnload, true);
|
|
46
|
-
(0, _eol.subscribeToEOL)(this.unload.bind(this)); // this should consist only of sending final harvest
|
|
47
|
-
|
|
48
|
-
/* Flush all buffered data if session resets and give up retries. This should be synchronous to ensure that the correct `session` value is sent.
|
|
49
|
-
Since session-reset generates a new session ID and the ID is grabbed at send-time, any delays or retries would cause the payload to be sent under
|
|
50
|
-
the wrong session ID. */
|
|
51
|
-
this.sharedContext?.ee.on(_constants.SESSION_EVENTS.RESET, () => this.runHarvest({
|
|
52
|
-
forceNoRetry: true
|
|
53
|
-
}));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* This function is only meant for the last outgoing harvest cycle of a page. It trickles down to using sendBeacon, which should not be used
|
|
58
|
-
* to send payloads while the page is still active, due to limitations on how much data can be buffered in the API at any one time.
|
|
59
|
-
*/
|
|
60
|
-
unload() {
|
|
61
|
-
if (this.aborted) return;
|
|
62
|
-
this.runHarvest({
|
|
63
|
-
unload: true
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
startTimer(interval, initialDelay) {
|
|
67
|
-
this.interval = interval;
|
|
68
|
-
this.started = true;
|
|
69
|
-
this.scheduleHarvest(initialDelay != null ? initialDelay : this.interval);
|
|
70
|
-
}
|
|
71
|
-
stopTimer(permanently = false) {
|
|
72
|
-
this.aborted = permanently; // stopping permanently is same as aborting, but this function also cleans up the setTimeout loop
|
|
73
|
-
this.started = false;
|
|
74
|
-
if (this.timeoutHandle) {
|
|
75
|
-
clearTimeout(this.timeoutHandle);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
scheduleHarvest(delay, opts) {
|
|
79
|
-
if (this.timeoutHandle) return;
|
|
80
|
-
if (delay == null) {
|
|
81
|
-
delay = this.interval;
|
|
82
|
-
}
|
|
83
|
-
this.timeoutHandle = setTimeout(() => {
|
|
84
|
-
this.timeoutHandle = null;
|
|
85
|
-
this.runHarvest(opts);
|
|
86
|
-
}, delay * 1000);
|
|
87
|
-
}
|
|
88
|
-
runHarvest(opts) {
|
|
89
|
-
if (this.aborted) return;
|
|
90
|
-
this.harvesting = true;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* This is executed immediately after harvest sends the data via XHR, or if there's nothing to send. Note that this excludes on unloading / sendBeacon.
|
|
94
|
-
* @param {Object} result
|
|
95
|
-
*/
|
|
96
|
-
const cbRanAfterSend = result => {
|
|
97
|
-
this.harvesting = false;
|
|
98
|
-
if (opts?.forceNoRetry) result.retry = false; // discard unsent data rather than re-queuing for next harvest attempt
|
|
99
|
-
this.onHarvestFinished(opts, result);
|
|
100
|
-
};
|
|
101
|
-
let harvests = [];
|
|
102
|
-
let submitMethod;
|
|
103
|
-
let payload;
|
|
104
|
-
if (this.opts.getPayload) {
|
|
105
|
-
// Ajax, PVT, Softnav, Logging, SR & ST features provide a single callback function to get data for harvesting
|
|
106
|
-
submitMethod = submitData.getSubmitMethod({
|
|
107
|
-
isFinalHarvest: opts?.unload
|
|
108
|
-
});
|
|
109
|
-
if (!submitMethod) return false;
|
|
110
|
-
const retry = !opts?.unload && submitMethod === submitData.xhr;
|
|
111
|
-
payload = this.opts.getPayload({
|
|
112
|
-
retry,
|
|
113
|
-
...opts
|
|
114
|
-
});
|
|
115
|
-
if (!payload) {
|
|
116
|
-
if (this.started) {
|
|
117
|
-
this.scheduleHarvest();
|
|
118
|
-
}
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
payload = Object.prototype.toString.call(payload) === '[object Array]' ? payload : [payload];
|
|
122
|
-
harvests.push(...payload);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** sendX is used for features that do not supply a preformatted payload via "getPayload" */
|
|
126
|
-
let send = args => this.harvest.sendX(args);
|
|
127
|
-
if (harvests.length) {
|
|
128
|
-
/** _send is the underlying method for sending in the harvest, if sending raw we can bypass the other helpers completely which format the payloads */
|
|
129
|
-
if (this.opts.raw) send = args => this.harvest._send(args);
|
|
130
|
-
/** send is used to formated the payloads from "getPayload" and obfuscate before sending */else send = args => this.harvest.send(args);
|
|
131
|
-
} else {
|
|
132
|
-
// force it to run at least once in sendX mode
|
|
133
|
-
harvests.push(undefined);
|
|
134
|
-
}
|
|
135
|
-
harvests.forEach(payload => {
|
|
136
|
-
send({
|
|
137
|
-
endpoint: this.endpoint,
|
|
138
|
-
payload,
|
|
139
|
-
opts,
|
|
140
|
-
submitMethod,
|
|
141
|
-
cbFinished: cbRanAfterSend,
|
|
142
|
-
customUrl: this.opts.customUrl,
|
|
143
|
-
raw: this.opts.raw
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
if (this.started) {
|
|
147
|
-
this.scheduleHarvest();
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
onHarvestFinished(opts, result) {
|
|
151
|
-
if (this.opts.onFinished) {
|
|
152
|
-
this.opts.onFinished(result);
|
|
153
|
-
}
|
|
154
|
-
if (result.sent && result.retry) {
|
|
155
|
-
const delay = result.delay || this.opts.retryDelay;
|
|
156
|
-
// reschedule next harvest if should be delayed longer
|
|
157
|
-
if (this.started && delay) {
|
|
158
|
-
clearTimeout(this.timeoutHandle);
|
|
159
|
-
this.timeoutHandle = null;
|
|
160
|
-
this.scheduleHarvest(delay, opts);
|
|
161
|
-
} else if (!this.started && delay) {
|
|
162
|
-
// if not running on a timer, schedule a single retry
|
|
163
|
-
this.scheduleHarvest(delay, opts);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
exports.HarvestScheduler = HarvestScheduler;
|