@newrelic/browser-agent 1.257.0 → 1.258.1
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 +27 -0
- package/dist/cjs/common/config/state/configurable.js +8 -5
- package/dist/cjs/common/config/state/init.js +0 -2
- package/dist/cjs/common/config/state/runtime.js +10 -8
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/constants/runtime.js +8 -2
- package/dist/cjs/common/deny-list/deny-list.js +6 -8
- package/dist/cjs/common/session/constants.js +1 -0
- package/dist/cjs/common/session/session-entity.js +3 -0
- package/dist/cjs/common/timing/time-keeper.js +45 -9
- package/dist/cjs/common/vitals/time-to-first-byte.js +10 -2
- package/dist/cjs/common/vitals/vital-metric.js +1 -1
- package/dist/cjs/features/ajax/aggregate/chunk.js +50 -0
- package/dist/cjs/features/ajax/aggregate/index.js +131 -191
- package/dist/cjs/features/ajax/instrument/index.js +11 -11
- package/dist/cjs/features/jserrors/aggregate/index.js +7 -3
- package/dist/cjs/features/jserrors/instrument/index.js +4 -87
- package/dist/cjs/features/jserrors/shared/cast-error.js +66 -0
- package/dist/cjs/features/jserrors/{instrument → shared}/uncaught-error.js +4 -2
- package/dist/cjs/features/page_view_event/aggregate/index.js +3 -3
- package/dist/cjs/features/session_replay/aggregate/index.js +1 -0
- package/dist/cjs/features/session_replay/shared/utils.js +3 -3
- package/dist/cjs/features/session_trace/aggregate/index.js +9 -7
- package/dist/cjs/features/spa/aggregate/index.js +11 -1
- package/dist/cjs/features/spa/instrument/index.js +9 -2
- package/dist/cjs/features/utils/agent-session.js +1 -5
- package/dist/cjs/features/utils/instrument-base.js +1 -1
- package/dist/cjs/loaders/api/api.js +6 -14
- package/dist/cjs/loaders/api/apiAsync.js +5 -4
- package/dist/esm/common/config/state/configurable.js +8 -5
- package/dist/esm/common/config/state/init.js +0 -2
- package/dist/esm/common/config/state/runtime.js +11 -9
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/constants/runtime.js +7 -1
- package/dist/esm/common/deny-list/deny-list.js +5 -8
- package/dist/esm/common/session/constants.js +1 -0
- package/dist/esm/common/session/session-entity.js +3 -0
- package/dist/esm/common/timing/time-keeper.js +46 -9
- package/dist/esm/common/vitals/time-to-first-byte.js +11 -3
- package/dist/esm/common/vitals/vital-metric.js +1 -1
- package/dist/esm/features/ajax/aggregate/chunk.js +43 -0
- package/dist/esm/features/ajax/aggregate/index.js +130 -191
- package/dist/esm/features/ajax/instrument/index.js +12 -12
- package/dist/esm/features/jserrors/aggregate/index.js +7 -3
- package/dist/esm/features/jserrors/instrument/index.js +4 -87
- package/dist/esm/features/jserrors/shared/cast-error.js +59 -0
- package/dist/esm/features/jserrors/{instrument → shared}/uncaught-error.js +5 -2
- package/dist/esm/features/page_view_event/aggregate/index.js +4 -4
- package/dist/esm/features/session_replay/aggregate/index.js +1 -0
- package/dist/esm/features/session_replay/shared/utils.js +4 -4
- package/dist/esm/features/session_trace/aggregate/index.js +9 -7
- package/dist/esm/features/spa/aggregate/index.js +11 -1
- package/dist/esm/features/spa/instrument/index.js +9 -2
- package/dist/esm/features/utils/agent-session.js +1 -5
- package/dist/esm/features/utils/instrument-base.js +1 -1
- package/dist/esm/loaders/api/api.js +6 -14
- package/dist/esm/loaders/api/apiAsync.js +5 -4
- package/dist/types/common/config/state/configurable.d.ts.map +1 -1
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/config/state/runtime.d.ts.map +1 -1
- package/dist/types/common/constants/runtime.d.ts +6 -1
- package/dist/types/common/constants/runtime.d.ts.map +1 -1
- package/dist/types/common/deny-list/deny-list.d.ts +1 -0
- package/dist/types/common/deny-list/deny-list.d.ts.map +1 -1
- package/dist/types/common/session/constants.d.ts +1 -0
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/timing/time-keeper.d.ts +1 -1
- package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/chunk.d.ts +8 -0
- package/dist/types/features/ajax/aggregate/chunk.d.ts.map +1 -0
- package/dist/types/features/ajax/aggregate/index.d.ts +8 -6
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/ajax/instrument/index.d.ts +2 -2
- package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/shared/cast-error.d.ts +21 -0
- package/dist/types/features/jserrors/shared/cast-error.d.ts.map +1 -0
- package/dist/types/features/jserrors/{instrument → shared}/uncaught-error.d.ts +3 -2
- package/dist/types/features/jserrors/shared/uncaught-error.d.ts.map +1 -0
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts +3 -3
- package/dist/types/features/session_trace/aggregate/index.d.ts +9 -8
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/instrument/index.d.ts.map +1 -1
- package/dist/types/features/utils/agent-session.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/config/state/configurable.js +9 -8
- package/src/common/config/state/init.js +0 -1
- package/src/common/config/state/runtime.js +12 -9
- package/src/common/constants/__mocks__/runtime.js +2 -0
- package/src/common/constants/runtime.js +6 -1
- package/src/common/deny-list/deny-list.js +6 -7
- package/src/common/session/constants.js +1 -0
- package/src/common/session/session-entity.js +2 -0
- package/src/common/timing/time-keeper.js +44 -10
- package/src/common/vitals/time-to-first-byte.js +10 -3
- package/src/common/vitals/vital-metric.js +1 -1
- package/src/features/ajax/aggregate/chunk.js +51 -0
- package/src/features/ajax/aggregate/index.js +128 -200
- package/src/features/ajax/instrument/index.js +12 -13
- package/src/features/jserrors/aggregate/index.js +9 -3
- package/src/features/jserrors/instrument/index.js +4 -99
- package/src/features/jserrors/shared/cast-error.js +69 -0
- package/src/features/jserrors/{instrument → shared}/uncaught-error.js +5 -2
- package/src/features/page_view_event/aggregate/index.js +4 -4
- package/src/features/session_replay/aggregate/index.js +2 -0
- package/src/features/session_replay/shared/utils.js +4 -4
- package/src/features/session_trace/aggregate/index.js +9 -8
- package/src/features/spa/aggregate/index.js +10 -1
- package/src/features/spa/instrument/index.js +3 -3
- package/src/features/utils/agent-session.js +1 -7
- package/src/features/utils/instrument-base.js +1 -1
- package/src/loaders/api/api.js +6 -15
- package/src/loaders/api/apiAsync.js +5 -4
- package/dist/cjs/common/storage/first-party-cookies.js +0 -36
- package/dist/esm/common/storage/first-party-cookies.js +0 -29
- package/dist/types/common/storage/first-party-cookies.d.ts +0 -8
- package/dist/types/common/storage/first-party-cookies.d.ts.map +0 -1
- package/dist/types/features/jserrors/instrument/uncaught-error.d.ts.map +0 -1
- package/src/common/storage/first-party-cookies.js +0 -32
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.castError = castError;
|
|
7
|
+
exports.castErrorEvent = castErrorEvent;
|
|
8
|
+
exports.castPromiseRejectionEvent = castPromiseRejectionEvent;
|
|
9
|
+
var _uncaughtError = require("./uncaught-error");
|
|
10
|
+
/**
|
|
11
|
+
* Any value can be used with the `throw` keyword. This function ensures that the value is
|
|
12
|
+
* either a proper Error instance or attempts to convert it to an UncaughtError instance.
|
|
13
|
+
* @param {any} error The value thrown
|
|
14
|
+
* @returns {Error|UncaughtError} The converted error instance
|
|
15
|
+
*/
|
|
16
|
+
function castError(error) {
|
|
17
|
+
/** Sometimes a browser can emit an error object with no stack */
|
|
18
|
+
if (canTrustError(error)) {
|
|
19
|
+
return error;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The thrown value may contain a message property. If it does, try to treat the thrown
|
|
24
|
+
* value as an Error-like object.
|
|
25
|
+
*/
|
|
26
|
+
return new _uncaughtError.UncaughtError(error?.message !== undefined ? error.message : error, error?.filename || error?.sourceURL, error?.lineno || error?.line, error?.colno || error?.col, error?.__newrelic);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Attempts to convert a PromiseRejectionEvent object to an Error object
|
|
31
|
+
* @param {PromiseRejectionEvent} unhandledRejectionEvent The unhandled promise rejection event
|
|
32
|
+
* @returns {Error} An Error object with the message as the casted reason
|
|
33
|
+
*/
|
|
34
|
+
function castPromiseRejectionEvent(promiseRejectionEvent) {
|
|
35
|
+
let prefix = 'Unhandled Promise Rejection';
|
|
36
|
+
if (canTrustError(promiseRejectionEvent?.reason)) {
|
|
37
|
+
try {
|
|
38
|
+
promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message;
|
|
39
|
+
return castError(promiseRejectionEvent.reason);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return castError(promiseRejectionEvent.reason);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (typeof promiseRejectionEvent.reason === 'undefined') return castError(prefix);
|
|
45
|
+
const error = castError(promiseRejectionEvent.reason);
|
|
46
|
+
error.message = prefix + ': ' + error?.message;
|
|
47
|
+
return error;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Attempts to convert an ErrorEvent object to an Error object
|
|
52
|
+
* @param {ErrorEvent} errorEvent The error event
|
|
53
|
+
* @returns {Error|UncaughtError} The error event converted to an Error object
|
|
54
|
+
*/
|
|
55
|
+
function castErrorEvent(errorEvent) {
|
|
56
|
+
if (errorEvent.error instanceof SyntaxError && !/:\d+$/.test(errorEvent.error.stack?.trim())) {
|
|
57
|
+
const error = new _uncaughtError.UncaughtError(errorEvent.message, errorEvent.filename, errorEvent.lineno, errorEvent.colno, errorEvent.error.__newrelic);
|
|
58
|
+
error.name = SyntaxError.name;
|
|
59
|
+
return error;
|
|
60
|
+
}
|
|
61
|
+
if (canTrustError(errorEvent.error)) return errorEvent.error;
|
|
62
|
+
return castError(errorEvent);
|
|
63
|
+
}
|
|
64
|
+
function canTrustError(error) {
|
|
65
|
+
return error instanceof Error && !!error.stack;
|
|
66
|
+
}
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.UncaughtError = void 0;
|
|
7
|
+
var _stringify = require("../../../common/util/stringify");
|
|
7
8
|
/**
|
|
8
9
|
* Represents an uncaught non Error type error. This class does
|
|
9
10
|
* not extend the Error class to prevent an invalid stack trace
|
|
@@ -11,12 +12,13 @@ exports.UncaughtError = void 0;
|
|
|
11
12
|
* do not use the Error class (strings, etc) to an object.
|
|
12
13
|
*/
|
|
13
14
|
class UncaughtError {
|
|
14
|
-
constructor(message, filename, lineno, colno) {
|
|
15
|
+
constructor(message, filename, lineno, colno, newrelic) {
|
|
15
16
|
this.name = 'UncaughtError';
|
|
16
|
-
this.message = message;
|
|
17
|
+
this.message = typeof message === 'string' ? message : (0, _stringify.stringify)(message);
|
|
17
18
|
this.sourceURL = filename;
|
|
18
19
|
this.line = lineno;
|
|
19
20
|
this.column = colno;
|
|
21
|
+
this.__newrelic = newrelic;
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
exports.UncaughtError = UncaughtError;
|
|
@@ -103,14 +103,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
103
103
|
// Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
|
|
104
104
|
const navTimingEntry = _runtime.globalScope?.performance?.getEntriesByType('navigation')?.[0];
|
|
105
105
|
const perf = {
|
|
106
|
-
timing: (0, _navTiming.addPT)(
|
|
106
|
+
timing: (0, _navTiming.addPT)(_runtime.originTime, navTimingEntry, {}),
|
|
107
107
|
navigation: (0, _navTiming.addPN)(navTimingEntry, {})
|
|
108
108
|
};
|
|
109
109
|
queryParameters.perf = (0, _stringify.stringify)(perf);
|
|
110
110
|
} else if (typeof PerformanceTiming !== 'undefined') {
|
|
111
111
|
// Safari pre-15 did not support level 2 timing
|
|
112
112
|
const perf = {
|
|
113
|
-
timing: (0, _navTiming.addPT)(
|
|
113
|
+
timing: (0, _navTiming.addPT)(_runtime.originTime, _runtime.globalScope.performance.timing, {}, true),
|
|
114
114
|
navigation: (0, _navTiming.addPN)(_runtime.globalScope.performance.navigation, {})
|
|
115
115
|
};
|
|
116
116
|
queryParameters.perf = (0, _stringify.stringify)(perf);
|
|
@@ -142,7 +142,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
142
142
|
return;
|
|
143
143
|
}
|
|
144
144
|
try {
|
|
145
|
-
const timeKeeper = new _timeKeeper.TimeKeeper();
|
|
145
|
+
const timeKeeper = new _timeKeeper.TimeKeeper(this.agentIdentifier);
|
|
146
146
|
timeKeeper.processRumRequest(xhr, rumStartTime, rumEndTime);
|
|
147
147
|
if (!timeKeeper.ready) throw new Error('TimeKeeper not ready');
|
|
148
148
|
agentRuntime.timeKeeper = timeKeeper;
|
|
@@ -373,6 +373,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
373
373
|
...(agentMetadata.entityGuid && {
|
|
374
374
|
entityGuid: agentMetadata.entityGuid
|
|
375
375
|
}),
|
|
376
|
+
harvestId: [agentRuntime.session?.state.value, agentRuntime.ptid, agentRuntime.harvestCount].filter(x => x).join('_'),
|
|
376
377
|
'replay.firstTimestamp': firstTimestamp,
|
|
377
378
|
'replay.lastTimestamp': lastTimestamp,
|
|
378
379
|
'replay.nodes': events.length,
|
|
@@ -32,8 +32,8 @@ function buildNRMetaNode(timestamp, timeKeeper) {
|
|
|
32
32
|
originalTimestamp: timestamp,
|
|
33
33
|
correctedTimestamp,
|
|
34
34
|
timestampDiff: timestamp - correctedTimestamp,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
originTime: _runtime.originTime,
|
|
36
|
+
correctedOriginTime: timeKeeper.correctedOriginTime,
|
|
37
|
+
originTimeDiff: Math.floor(_runtime.originTime - timeKeeper.correctedOriginTime)
|
|
38
38
|
};
|
|
39
39
|
}
|
|
@@ -14,6 +14,7 @@ var _replayMode = require("../../session_replay/shared/replay-mode");
|
|
|
14
14
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
15
15
|
var _constants2 = require("../../../common/session/constants");
|
|
16
16
|
var _now = require("../../../common/timing/now");
|
|
17
|
+
var _runtime = require("../../../common/constants/runtime");
|
|
17
18
|
/*
|
|
18
19
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
19
20
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -53,14 +54,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
53
54
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME);
|
|
54
55
|
_this = this;
|
|
55
56
|
this.agentRuntime = (0, _config.getRuntime)(agentIdentifier);
|
|
56
|
-
|
|
57
|
-
// Very unlikely, but in case the existing XMLHttpRequest.prototype object on the page couldn't be wrapped.
|
|
58
|
-
if (!this.agentRuntime.xhrWrappable) return;
|
|
59
57
|
this.resourceObserver = argsObj?.resourceObserver; // undefined if observer couldn't be created
|
|
60
58
|
this.ptid = '';
|
|
61
59
|
this.trace = {};
|
|
62
60
|
this.nodeCount = 0;
|
|
63
61
|
this.sentTrace = null;
|
|
62
|
+
this.prevStoredEvents = new Set();
|
|
64
63
|
this.harvestTimeSeconds = (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.harvestTimeSeconds') || 10;
|
|
65
64
|
this.maxNodesPerHarvest = (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.maxNodesPerHarvest') || 1000;
|
|
66
65
|
/**
|
|
@@ -128,7 +127,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
128
127
|
return controlTraceOp(on);
|
|
129
128
|
}, this.featureName, this.ee);
|
|
130
129
|
} else {
|
|
131
|
-
(0, _registerHandler.registerHandler)('
|
|
130
|
+
(0, _registerHandler.registerHandler)('trace-jserror', () => {
|
|
132
131
|
seenAnError = true;
|
|
133
132
|
switchToFull();
|
|
134
133
|
}, this.featureName, this.ee);
|
|
@@ -223,7 +222,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
223
222
|
}
|
|
224
223
|
return operationalGate.settle(() => _this.storeSTN(...args));
|
|
225
224
|
}, this.featureName, this.ee);
|
|
226
|
-
(0, _registerHandler.registerHandler)('
|
|
225
|
+
(0, _registerHandler.registerHandler)('trace-jserror', function () {
|
|
227
226
|
for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
|
|
228
227
|
args[_key6] = arguments[_key6];
|
|
229
228
|
}
|
|
@@ -272,6 +271,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
272
271
|
}
|
|
273
272
|
}
|
|
274
273
|
#prepareHarvest(options) {
|
|
274
|
+
this.prevStoredEvents.clear(); // release references to past events for GC
|
|
275
275
|
if (this.isStandalone) {
|
|
276
276
|
if (this.ptid && (0, _now.now)() >= MAX_TRACE_DURATION) {
|
|
277
277
|
// Perform a final harvest once we hit or exceed the max session trace time
|
|
@@ -338,6 +338,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
338
338
|
// Tracks the events and their listener's duration on objects wrapped by wrap-events.
|
|
339
339
|
storeEvent(currentEvent, target, start, end) {
|
|
340
340
|
if (this.shouldIgnoreEvent(currentEvent, target)) return;
|
|
341
|
+
if (this.prevStoredEvents.has(currentEvent)) return; // prevent multiple listeners of an event from creating duplicate trace nodes per occurrence. Cleared every harvest. near-zero chance for re-duplication after clearing per harvest since the timestamps of the event are considered for uniqueness.
|
|
342
|
+
this.prevStoredEvents.add(currentEvent);
|
|
341
343
|
const evt = {
|
|
342
344
|
n: this.evtName(currentEvent.type),
|
|
343
345
|
s: start,
|
|
@@ -538,13 +540,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
538
540
|
}
|
|
539
541
|
return {
|
|
540
542
|
qs: {
|
|
541
|
-
st:
|
|
543
|
+
st: _runtime.originTime,
|
|
542
544
|
/** hr === "hasReplay" in NR1, standalone is always checked and processed before harvesting
|
|
543
545
|
* so a race condition between ST and SR states should not be a concern if implemented here */
|
|
544
546
|
hr: Number(!this.isStandalone),
|
|
545
547
|
/** fts === "firstTimestamp" in NR1, indicates what the earliest NODE timestamp was
|
|
546
548
|
* so that blob parsing doesn't need to happen to support UI/API functions */
|
|
547
|
-
fts:
|
|
549
|
+
fts: _runtime.originTime + earliestTimeStamp,
|
|
548
550
|
/** n === "nodeCount" in NR1, a count of nodes in the ST payload, so that blob parsing doesn't need to happen to support UI/API functions */
|
|
549
551
|
n: stns.length,
|
|
550
552
|
// node count
|
|
@@ -651,7 +651,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
651
651
|
state.interactionsSent = [];
|
|
652
652
|
}
|
|
653
653
|
}
|
|
654
|
-
baseEE.on('
|
|
654
|
+
baseEE.on('spa-jserror', function (type, name, params, metrics) {
|
|
655
655
|
if (!state.currentNode) return;
|
|
656
656
|
params._interactionId = state.currentNode.interaction.id;
|
|
657
657
|
// do not capture parentNodeId when in root node
|
|
@@ -659,6 +659,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
659
659
|
params._interactionNodeId = state.currentNode.id;
|
|
660
660
|
}
|
|
661
661
|
});
|
|
662
|
+
(0, _registerHandler.registerHandler)('function-err', function (args, obj, error) {
|
|
663
|
+
if (!state.currentNode) return;
|
|
664
|
+
error.__newrelic ??= {};
|
|
665
|
+
error.__newrelic[agentIdentifier] = {
|
|
666
|
+
interactionId: state.currentNode.interaction.id
|
|
667
|
+
};
|
|
668
|
+
if (state.currentNode.type && state.currentNode.type !== 'interaction') {
|
|
669
|
+
error.__newrelic[agentIdentifier].interactionNodeId = state.currentNode.id;
|
|
670
|
+
}
|
|
671
|
+
}, this.featureName, baseEE);
|
|
662
672
|
baseEE.on('interaction', saveInteraction);
|
|
663
673
|
function getActionText(node) {
|
|
664
674
|
var nodeType = node.tagName.toLowerCase();
|
|
@@ -7,10 +7,10 @@ exports.Instrument = void 0;
|
|
|
7
7
|
var _wrap = require("../../../common/wrap");
|
|
8
8
|
var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
|
|
9
9
|
var _instrumentBase = require("../../utils/instrument-base");
|
|
10
|
-
var _config = require("../../../common/config/config");
|
|
11
10
|
var CONSTANTS = _interopRequireWildcard(require("../constants"));
|
|
12
11
|
var _runtime = require("../../../common/constants/runtime");
|
|
13
12
|
var _now = require("../../../common/timing/now");
|
|
13
|
+
var _handle = require("../../../common/event-emitter/handle");
|
|
14
14
|
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); }
|
|
15
15
|
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 && Object.prototype.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; }
|
|
16
16
|
/*
|
|
@@ -33,11 +33,12 @@ const {
|
|
|
33
33
|
class Instrument extends _instrumentBase.InstrumentBase {
|
|
34
34
|
static featureName = FEATURE_NAME;
|
|
35
35
|
constructor(agentIdentifier, aggregator) {
|
|
36
|
+
var _this;
|
|
36
37
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
37
38
|
super(agentIdentifier, aggregator, FEATURE_NAME, auto);
|
|
39
|
+
_this = this;
|
|
38
40
|
if (!_runtime.isBrowserScope) return; // SPA not supported outside web env
|
|
39
41
|
|
|
40
|
-
if (!(0, _config.getRuntime)(agentIdentifier).xhrWrappable) return;
|
|
41
42
|
try {
|
|
42
43
|
this.removeOnAbort = new AbortController();
|
|
43
44
|
} catch (e) {}
|
|
@@ -58,6 +59,12 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
58
59
|
this.ee.on(FN_END, endTimestamp);
|
|
59
60
|
promiseEE.on(CB_END, endTimestamp);
|
|
60
61
|
jsonpEE.on(CB_END, endTimestamp);
|
|
62
|
+
this.ee.on('fn-err', function () {
|
|
63
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
64
|
+
args[_key] = arguments[_key];
|
|
65
|
+
}
|
|
66
|
+
if (!args[2]?.__newrelic?.[agentIdentifier]) (0, _handle.handle)('function-err', [...args], undefined, _this.featureName, _this.ee);
|
|
67
|
+
});
|
|
61
68
|
this.ee.buffer([FN_START, FN_END, 'xhr-resolved'], this.featureName);
|
|
62
69
|
eventsEE.buffer([FN_START], this.featureName);
|
|
63
70
|
timerEE.buffer(['setTimeout' + END, 'clearTimeout' + START, FN_START], this.featureName);
|
|
@@ -10,20 +10,16 @@ var _contextualEe = require("../../common/event-emitter/contextual-ee");
|
|
|
10
10
|
var _registerHandler = require("../../common/event-emitter/register-handler");
|
|
11
11
|
var _sessionEntity = require("../../common/session/session-entity");
|
|
12
12
|
var _localStorage = require("../../common/storage/local-storage.js");
|
|
13
|
-
var _firstPartyCookies = require("../../common/storage/first-party-cookies");
|
|
14
13
|
var _constants = require("../../common/session/constants");
|
|
15
14
|
let ranOnce = 0;
|
|
16
15
|
function setupAgentSession(agentIdentifier) {
|
|
17
16
|
const agentRuntime = (0, _config.getRuntime)(agentIdentifier);
|
|
18
17
|
if (ranOnce++) return agentRuntime.session;
|
|
19
18
|
const sessionInit = (0, _config.getConfiguration)(agentIdentifier).session;
|
|
20
|
-
/* Domain is a string that can be specified by customer. The only way to keep the session object across subdomains is using first party cookies.
|
|
21
|
-
This determines which storage wrapper the session manager will use to keep state. */
|
|
22
|
-
const storageTypeInst = sessionInit?.domain ? new _firstPartyCookies.FirstPartyCookies(sessionInit.domain) : new _localStorage.LocalStorage();
|
|
23
19
|
agentRuntime.session = new _sessionEntity.SessionEntity({
|
|
24
20
|
agentIdentifier,
|
|
25
21
|
key: _constants.DEFAULT_KEY,
|
|
26
|
-
storage:
|
|
22
|
+
storage: new _localStorage.LocalStorage(),
|
|
27
23
|
expiresMs: sessionInit?.expiresMs,
|
|
28
24
|
inactiveMs: sessionInit?.inactiveMs
|
|
29
25
|
});
|
|
@@ -57,7 +57,7 @@ class InstrumentBase extends _featureBase.FeatureBase {
|
|
|
57
57
|
if ((0, _config.getConfigurationValue)(this.agentIdentifier, "".concat(this.featureName, ".autoStart")) === false) this.auto = false;
|
|
58
58
|
/** if the feature requires opt-in (!auto-start), it will get registered once the api has been called */
|
|
59
59
|
if (this.auto) (0, _drain.registerDrain)(agentIdentifier, featureName);else {
|
|
60
|
-
this.ee.on(
|
|
60
|
+
this.ee.on('manual-start-all', (0, _invoke.single)(() => {
|
|
61
61
|
// register the feature to drain only once the API has been called, it will drain when importAggregator finishes for all the features
|
|
62
62
|
// called by the api in that cycle
|
|
63
63
|
(0, _drain.registerDrain)(this.agentIdentifier, this.featureName);
|
|
@@ -133,19 +133,10 @@ function setAPI(agentIdentifier, forceDrain) {
|
|
|
133
133
|
}
|
|
134
134
|
return appendJsAttribute('application.version', value, 'setApplicationVersion', false);
|
|
135
135
|
};
|
|
136
|
-
apiInterface.start =
|
|
136
|
+
apiInterface.start = () => {
|
|
137
137
|
try {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const featNames = Object.values(_features.FEATURE_NAMES);
|
|
141
|
-
if (features === undefined) features = featNames;else {
|
|
142
|
-
features = Array.isArray(features) && features.length ? features : [features];
|
|
143
|
-
if (features.some(f => !featNames.includes(f))) return (0, _console.warn)("Invalid feature name supplied. Acceptable feature names are: ".concat(featNames));
|
|
144
|
-
if (!features.includes(_features.FEATURE_NAMES.pageViewEvent)) features.push(_features.FEATURE_NAMES.pageViewEvent);
|
|
145
|
-
}
|
|
146
|
-
features.forEach(feature => {
|
|
147
|
-
instanceEE.emit("".concat(feature, "-opt-in"));
|
|
148
|
-
});
|
|
138
|
+
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/start/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
|
|
139
|
+
instanceEE.emit('manual-start-all');
|
|
149
140
|
} catch (err) {
|
|
150
141
|
(0, _console.warn)('An unexpected issue occurred', err);
|
|
151
142
|
}
|
|
@@ -176,9 +167,10 @@ function setAPI(agentIdentifier, forceDrain) {
|
|
|
176
167
|
try {
|
|
177
168
|
return cb.apply(this, arguments);
|
|
178
169
|
} catch (err) {
|
|
179
|
-
|
|
170
|
+
const error = typeof err === 'string' ? new Error(err) : err;
|
|
171
|
+
tracerEE.emit('fn-err', [arguments, this, error], contextStore);
|
|
180
172
|
// the error came from outside the agent, so don't swallow
|
|
181
|
-
throw
|
|
173
|
+
throw error;
|
|
182
174
|
} finally {
|
|
183
175
|
tracerEE.emit('fn-end', [(0, _now.now)()], contextStore);
|
|
184
176
|
}
|
|
@@ -11,6 +11,7 @@ var _handle = require("../../common/event-emitter/handle");
|
|
|
11
11
|
var _registerHandler = require("../../common/event-emitter/register-handler");
|
|
12
12
|
var _invoke = require("../../common/util/invoke");
|
|
13
13
|
var _constants = require("../../features/metrics/constants");
|
|
14
|
+
var _runtime = require("../../common/constants/runtime");
|
|
14
15
|
function setAPI(agentIdentifier) {
|
|
15
16
|
var instanceEE = _contextualEe.ee.get(agentIdentifier);
|
|
16
17
|
var api = {
|
|
@@ -30,13 +31,13 @@ function setAPI(agentIdentifier) {
|
|
|
30
31
|
// first parameter. These functions can be called asynchronously.
|
|
31
32
|
|
|
32
33
|
function finished(t, providedTime) {
|
|
33
|
-
var time = providedTime ? providedTime -
|
|
34
|
+
var time = providedTime ? providedTime - _runtime.originTime : t;
|
|
34
35
|
(0, _handle.handle)(_constants.CUSTOM_METRIC_CHANNEL, ['finished', {
|
|
35
36
|
time
|
|
36
37
|
}], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
|
|
37
38
|
addToTrace(t, {
|
|
38
39
|
name: 'finished',
|
|
39
|
-
start: time +
|
|
40
|
+
start: time + _runtime.originTime,
|
|
40
41
|
origin: 'nr'
|
|
41
42
|
});
|
|
42
43
|
(0, _handle.handle)('api-addPageAction', [time, 'finished'], undefined, _features.FEATURE_NAMES.pageAction, instanceEE);
|
|
@@ -45,8 +46,8 @@ function setAPI(agentIdentifier) {
|
|
|
45
46
|
if (!(evt && typeof evt === 'object' && evt.name && evt.start)) return;
|
|
46
47
|
var report = {
|
|
47
48
|
n: evt.name,
|
|
48
|
-
s: evt.start -
|
|
49
|
-
e: (evt.end || evt.start) -
|
|
49
|
+
s: evt.start - _runtime.originTime,
|
|
50
|
+
e: (evt.end || evt.start) - _runtime.originTime,
|
|
50
51
|
o: evt.origin || '',
|
|
51
52
|
t: 'api'
|
|
52
53
|
};
|
|
@@ -7,12 +7,15 @@ export function getModeledObject(obj, model) {
|
|
|
7
7
|
const output = Object.create(Object.getPrototypeOf(model), Object.getOwnPropertyDescriptors(model));
|
|
8
8
|
const target = Object.keys(output).length === 0 ? obj : output;
|
|
9
9
|
for (let key in target) {
|
|
10
|
-
if (obj[key]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
if (obj[key] === undefined) continue;
|
|
11
|
+
try {
|
|
12
|
+
if (obj[key] === null) {
|
|
13
|
+
output[key] = null;
|
|
14
|
+
continue;
|
|
15
15
|
}
|
|
16
|
+
if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]));else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key]);else output[key] = obj[key];
|
|
17
|
+
} catch (e) {
|
|
18
|
+
warn('An error occurred while setting a property of a Configurable', e);
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
21
|
return output;
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { getModeledObject } from './configurable';
|
|
2
2
|
import { getNREUMInitializedAgent } from '../../window/nreum';
|
|
3
|
-
import { globalScope } from '../../constants/runtime';
|
|
3
|
+
import { globalScope, originTime } from '../../constants/runtime';
|
|
4
4
|
import { BUILD_ENV, DIST_METHOD, VERSION } from "../../constants/env.npm";
|
|
5
|
-
const
|
|
5
|
+
const readonly = {
|
|
6
6
|
buildEnv: BUILD_ENV,
|
|
7
|
+
distMethod: DIST_METHOD,
|
|
8
|
+
version: VERSION,
|
|
9
|
+
originTime
|
|
10
|
+
};
|
|
11
|
+
const model = {
|
|
7
12
|
customTransaction: undefined,
|
|
8
13
|
disabled: false,
|
|
9
|
-
distMethod: DIST_METHOD,
|
|
10
14
|
isolatedBacklog: false,
|
|
11
15
|
loaderType: undefined,
|
|
12
16
|
maxBytes: 30000,
|
|
13
|
-
// The "timeOrigin" property is the new standard timestamp property shared across main frame and workers, but is not supported in some early Safari browsers (safari<15) + IE
|
|
14
|
-
// ingest expects an integer value, and timeOrigin can return a float.
|
|
15
|
-
offset: Math.floor(globalScope?.performance?.timeOrigin || globalScope?.performance?.timing?.navigationStart || Date.now()),
|
|
16
17
|
onerror: undefined,
|
|
17
18
|
origin: '' + globalScope.location,
|
|
18
19
|
ptid: undefined,
|
|
@@ -20,8 +21,6 @@ const model = {
|
|
|
20
21
|
/** Agent-specific metadata found in the RUM call response. ex. entityGuid */
|
|
21
22
|
appMetadata: {},
|
|
22
23
|
session: undefined,
|
|
23
|
-
xhrWrappable: typeof globalScope.XMLHttpRequest?.prototype?.addEventListener === 'function',
|
|
24
|
-
version: VERSION,
|
|
25
24
|
denyList: undefined,
|
|
26
25
|
harvestCount: 0,
|
|
27
26
|
timeKeeper: undefined
|
|
@@ -34,7 +33,10 @@ export function getRuntime(id) {
|
|
|
34
33
|
}
|
|
35
34
|
export function setRuntime(id, obj) {
|
|
36
35
|
if (!id) throw new Error('All runtime objects require an agent identifier!');
|
|
37
|
-
_cache[id] =
|
|
36
|
+
_cache[id] = {
|
|
37
|
+
...getModeledObject(obj, model),
|
|
38
|
+
...readonly
|
|
39
|
+
};
|
|
38
40
|
const agentInst = getNREUMInitializedAgent(id);
|
|
39
41
|
if (agentInst) agentInst.runtime = _cache[id];
|
|
40
42
|
}
|
|
@@ -38,4 +38,10 @@ export const ffVersion = (() => {
|
|
|
38
38
|
export const isIE = Boolean(isBrowserScope && window.document.documentMode); // deprecated property that only works in IE
|
|
39
39
|
|
|
40
40
|
export const supportsSendBeacon = !!globalScope.navigator?.sendBeacon;
|
|
41
|
-
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Represents the absolute timestamp in milliseconds that the page was loaded
|
|
44
|
+
* according to the browser's local clock.
|
|
45
|
+
* @type {number}
|
|
46
|
+
*/
|
|
47
|
+
export const originTime = Math.floor(Date.now() - performance.now());
|
|
@@ -10,14 +10,8 @@ var denyList = [];
|
|
|
10
10
|
* @returns {boolean} `true` if request does not match any entries of {@link denyList|deny list}; else `false`
|
|
11
11
|
*/
|
|
12
12
|
export function shouldCollectEvent(params) {
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// XHR requests with an undefined hostname (e.g., data URLs) should not be collected.
|
|
18
|
-
if (params.hostname === undefined) {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
13
|
+
if (hasUndefinedHostname(params)) return false;
|
|
14
|
+
if (denyList.length === 0) return true;
|
|
21
15
|
for (var i = 0; i < denyList.length; i++) {
|
|
22
16
|
var parsed = denyList[i];
|
|
23
17
|
if (parsed.hostname === '*') {
|
|
@@ -29,6 +23,9 @@ export function shouldCollectEvent(params) {
|
|
|
29
23
|
}
|
|
30
24
|
return true;
|
|
31
25
|
}
|
|
26
|
+
export function hasUndefinedHostname(params) {
|
|
27
|
+
return params.hostname === undefined; // requests with an undefined hostname (e.g., data URLs) should not be collected.
|
|
28
|
+
}
|
|
32
29
|
|
|
33
30
|
/**
|
|
34
31
|
* Initializes the {@link denyList|XHR deny list} by extracting hostname and pathname from an array of filter strings.
|
|
@@ -3,6 +3,7 @@ export const DEFAULT_KEY = 'SESSION';
|
|
|
3
3
|
export const DEFAULT_EXPIRES_MS = 14400000;
|
|
4
4
|
export const DEFAULT_INACTIVE_MS = 1800000;
|
|
5
5
|
export const SESSION_EVENTS = {
|
|
6
|
+
STARTED: 'session-started',
|
|
6
7
|
PAUSE: 'session-pause',
|
|
7
8
|
RESET: 'session-reset',
|
|
8
9
|
RESUME: 'session-resume',
|
|
@@ -24,6 +24,8 @@ const model = {
|
|
|
24
24
|
sessionReplaySentFirstChunk: false,
|
|
25
25
|
sessionTraceMode: MODE.OFF,
|
|
26
26
|
traceHarvestStarted: false,
|
|
27
|
+
serverTimeDiff: null,
|
|
28
|
+
// set by TimeKeeper; "undefined" value will not be stringified and stored but "null" will
|
|
27
29
|
custom: {}
|
|
28
30
|
};
|
|
29
31
|
export class SessionEntity {
|
|
@@ -138,6 +140,7 @@ export class SessionEntity {
|
|
|
138
140
|
// we can use a modeled object here to help us know and manage what values are being used. -- see "model" above
|
|
139
141
|
if (this.isNew) this.write(getModeledObject(this.state, model), true);else this.sync(initialRead);
|
|
140
142
|
this.initialized = true;
|
|
143
|
+
this.ee.emit(SESSION_EVENTS.STARTED, [this.isNew]);
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
// This is the actual key appended to the storage API
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import { originTime } from '../constants/runtime';
|
|
2
|
+
import { ee as baseEE } from '../event-emitter/contextual-ee';
|
|
3
|
+
import { getRuntime } from '../config/config';
|
|
4
|
+
import { SESSION_EVENT_TYPES, SESSION_EVENTS } from '../session/constants';
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
7
|
* Class used to adjust the timestamp of harvested data to New Relic server time. This
|
|
3
8
|
* is done by tracking the performance timings of the RUM call and applying a calculation
|
|
@@ -5,10 +10,10 @@
|
|
|
5
10
|
*/
|
|
6
11
|
export class TimeKeeper {
|
|
7
12
|
/**
|
|
8
|
-
*
|
|
9
|
-
* @type {
|
|
13
|
+
* Pointer to the current agent session if it exists.
|
|
14
|
+
* @type {import('../session/session-entity').SessionEntity}
|
|
10
15
|
*/
|
|
11
|
-
#
|
|
16
|
+
#session;
|
|
12
17
|
|
|
13
18
|
/**
|
|
14
19
|
* Represents the browser origin time corrected to NR server time.
|
|
@@ -29,15 +34,24 @@ export class TimeKeeper {
|
|
|
29
34
|
* @type {number}
|
|
30
35
|
*/
|
|
31
36
|
#ready = false;
|
|
32
|
-
constructor() {
|
|
33
|
-
this.#
|
|
37
|
+
constructor(agentIdentifier) {
|
|
38
|
+
this.#session = getRuntime(agentIdentifier)?.session;
|
|
39
|
+
if (this.#session) {
|
|
40
|
+
const ee = baseEE.get(agentIdentifier);
|
|
41
|
+
ee.on(SESSION_EVENTS.UPDATE, this.#processSessionUpdate.bind(this));
|
|
42
|
+
ee.on(SESSION_EVENTS.STARTED, () => {
|
|
43
|
+
if (this.#ready) {
|
|
44
|
+
this.#session.write({
|
|
45
|
+
serverTimeDiff: this.#localTimeDiff
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
this.#processSessionUpdate(null, this.#session.read());
|
|
50
|
+
}
|
|
34
51
|
}
|
|
35
52
|
get ready() {
|
|
36
53
|
return this.#ready;
|
|
37
54
|
}
|
|
38
|
-
get originTime() {
|
|
39
|
-
return this.#originTime;
|
|
40
|
-
}
|
|
41
55
|
get correctedOriginTime() {
|
|
42
56
|
return this.#correctedOriginTime;
|
|
43
57
|
}
|
|
@@ -49,6 +63,8 @@ export class TimeKeeper {
|
|
|
49
63
|
* @param endTime {number} The end time of the RUM request
|
|
50
64
|
*/
|
|
51
65
|
processRumRequest(rumRequest, startTime, endTime) {
|
|
66
|
+
if (this.#ready) return; // Server time calculated from session entity
|
|
67
|
+
|
|
52
68
|
const responseDateHeader = rumRequest.getResponseHeader('Date');
|
|
53
69
|
if (!responseDateHeader) {
|
|
54
70
|
throw new Error('Missing date header on rum response.');
|
|
@@ -58,10 +74,13 @@ export class TimeKeeper {
|
|
|
58
74
|
|
|
59
75
|
// Corrected page origin time
|
|
60
76
|
this.#correctedOriginTime = Math.floor(Date.parse(responseDateHeader) - serverOffset);
|
|
61
|
-
this.#localTimeDiff =
|
|
77
|
+
this.#localTimeDiff = originTime - this.#correctedOriginTime;
|
|
62
78
|
if (Number.isNaN(this.#correctedOriginTime)) {
|
|
63
79
|
throw new Error('Date header invalid format.');
|
|
64
80
|
}
|
|
81
|
+
if (this.#session) this.#session.write({
|
|
82
|
+
serverTimeDiff: this.#localTimeDiff
|
|
83
|
+
});
|
|
65
84
|
this.#ready = true;
|
|
66
85
|
}
|
|
67
86
|
|
|
@@ -83,4 +102,22 @@ export class TimeKeeper {
|
|
|
83
102
|
correctAbsoluteTimestamp(timestamp) {
|
|
84
103
|
return Math.floor(timestamp - this.#localTimeDiff);
|
|
85
104
|
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Processes a session entity update payload to extract the server time calculated.
|
|
108
|
+
* @param {import('../session/constants').SESSION_EVENT_TYPES | null} type
|
|
109
|
+
* @param {Object} data
|
|
110
|
+
*/
|
|
111
|
+
#processSessionUpdate(type, data) {
|
|
112
|
+
if (typeof data?.serverTimeDiff !== 'number') return;
|
|
113
|
+
if (!type && !this.#ready ||
|
|
114
|
+
// This captures the initial read from the session entity when the timekeeper first initializes
|
|
115
|
+
type === SESSION_EVENT_TYPES.CROSS_TAB // This captures any cross-tab write of the session entity
|
|
116
|
+
) {
|
|
117
|
+
// This captures the initial read from the session entity when the timekeeper first initializes
|
|
118
|
+
this.#localTimeDiff = data.serverTimeDiff;
|
|
119
|
+
this.#correctedOriginTime = originTime - this.#localTimeDiff;
|
|
120
|
+
this.#ready = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
86
123
|
}
|