@newrelic/browser-agent 1.323.0-rc.0 → 1.323.0-rc.10
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/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/features/ajax/aggregate/index.js +17 -4
- package/dist/cjs/features/jserrors/aggregate/index.js +22 -0
- package/dist/cjs/interfaces/registered-iframe-entity.js +4 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/features/ajax/aggregate/index.js +18 -5
- package/dist/esm/features/jserrors/aggregate/index.js +22 -0
- package/dist/esm/interfaces/registered-iframe-entity.js +4 -1
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/interfaces/registered-iframe-entity.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/features/ajax/aggregate/index.js +15 -2
- package/src/features/jserrors/aggregate/index.js +22 -0
- package/src/interfaces/registered-iframe-entity.js +4 -1
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.323.0-rc.
|
|
20
|
+
const VERSION = exports.VERSION = "1.323.0-rc.10";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.323.0-rc.
|
|
20
|
+
const VERSION = exports.VERSION = "1.323.0-rc.10";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -133,10 +133,23 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
133
133
|
...event,
|
|
134
134
|
targetAttributes: (0, _utils.getVersion2Attributes)(target, this)
|
|
135
135
|
});
|
|
136
|
-
if ((0, _utils.shouldDuplicate)(target, this))
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
if ((0, _utils.shouldDuplicate)(target, this)) {
|
|
137
|
+
const duplicateEvent = {
|
|
138
|
+
...event,
|
|
139
|
+
targetAttributes: (0, _utils.getVersion2DuplicationAttributes)(target, this)
|
|
140
|
+
};
|
|
141
|
+
/** the container-duplicate copy represents the same single real HTTP call as the MFE event above, so it
|
|
142
|
+
* needs its own independent trace/span identity -- otherwise the two would collide on the same
|
|
143
|
+
* spanId/traceId, and BELC could not create two separate Spans/Traces from them. Only the id namespace is
|
|
144
|
+
* independent; spanTimestamp is intentionally left as-is (event.spanTimestamp), since it still reflects the
|
|
145
|
+
* real call's actual timing. When `event.spanId` was never set (e.g. distributed tracing is disabled), there
|
|
146
|
+
* is no span identity to duplicate either -- both copies correctly end up with no spanId/traceId. */
|
|
147
|
+
if (event.spanId) {
|
|
148
|
+
duplicateEvent.spanId = (0, _uniqueId.generateSpanId)();
|
|
149
|
+
duplicateEvent.traceId = (0, _uniqueId.generateTraceId)();
|
|
150
|
+
}
|
|
151
|
+
this.reportContainerEvent(duplicateEvent, ctx);
|
|
152
|
+
}
|
|
140
153
|
} else {
|
|
141
154
|
this.reportContainerEvent(event, ctx);
|
|
142
155
|
}
|
|
@@ -22,6 +22,7 @@ var _utils = require("../../../common/v2/utils");
|
|
|
22
22
|
var _causeString = require("./cause-string");
|
|
23
23
|
var _shortCircuit = require("../../../common/util/short-circuit");
|
|
24
24
|
var _events = require("../../../common/constants/events");
|
|
25
|
+
var _constants2 = require("../../../common/session/constants");
|
|
25
26
|
/**
|
|
26
27
|
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
27
28
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -50,6 +51,27 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
50
51
|
(0, _registerHandler.registerHandler)('ierr', this.processError.bind(this), this.featureName, this.ee);
|
|
51
52
|
(0, _registerHandler.registerHandler)('returnJserror', (jsErrorEvent, softNavAttrs) => this.#storeJserrorForHarvest(jsErrorEvent, softNavAttrs), this.featureName, this.ee);
|
|
52
53
|
|
|
54
|
+
/*
|
|
55
|
+
* The backend only remembers a stack trace for as long as the session that first reported it.
|
|
56
|
+
* The SessionEntity emits SESSION_EVENTS.RESET whenever the session expires (max duration or
|
|
57
|
+
* inactivity) or is otherwise reset, which is the same boundary the backend uses to forget
|
|
58
|
+
* previously-seen stack hashes. Forget them here too, so the next occurrence of an
|
|
59
|
+
* already-seen error resends its full stack_trace once, instead of a browser_stack_hash the
|
|
60
|
+
* backend can no longer resolve.
|
|
61
|
+
*/
|
|
62
|
+
const forgetReportedStacks = () => {
|
|
63
|
+
this.stackReported = {};
|
|
64
|
+
this.observedAt = {};
|
|
65
|
+
};
|
|
66
|
+
let currentSessionId = this.agentRef.runtime.session?.state?.value;
|
|
67
|
+
this.ee.on(_constants2.SESSION_EVENTS.RESET, forgetReportedStacks);
|
|
68
|
+
this.ee.on(_constants2.SESSION_EVENTS.UPDATE, (type, data) => {
|
|
69
|
+
if (!data?.value) return;
|
|
70
|
+
/* A reset in another tab arrived as a cross-tab update carrying a new session id. */
|
|
71
|
+
if (type === _constants2.SESSION_EVENT_TYPES.CROSS_TAB && currentSessionId && data.value !== currentSessionId) forgetReportedStacks();
|
|
72
|
+
currentSessionId = data.value; // also tracks same-tab writes, so a local reset's new id is picked up
|
|
73
|
+
});
|
|
74
|
+
|
|
53
75
|
// 0 == off, 1 == on
|
|
54
76
|
this.waitForFlags(['err']).then(([errFlag]) => {
|
|
55
77
|
if (errFlag) {
|
|
@@ -123,10 +123,13 @@ class RegisteredIframeEntity {
|
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/* Script timings are found by parsing the call stack for the script that called register(), so this has to run
|
|
127
|
+
synchronously here. By the time the registration promise resolves, that caller is no longer on the stack. */
|
|
128
|
+
const timings = (0, _scriptTracker.findScriptTimings)(opts);
|
|
129
|
+
|
|
126
130
|
// Store the registration promise so other methods can wait for it
|
|
127
131
|
this.#registrationPromise = this.#register(opts);
|
|
128
132
|
this.#registrationPromise.then(() => {
|
|
129
|
-
const timings = (0, _scriptTracker.findScriptTimings)();
|
|
130
133
|
// Send initial timing values, skipping ones still at their "not yet known" default
|
|
131
134
|
// (e.g. reportedAt: undefined, fetchStart: 0) to avoid pointless postMessage calls
|
|
132
135
|
for (const [key, value] of Object.entries(timings)) {
|
|
@@ -16,7 +16,7 @@ import { canCapturePayload, isLikelyHumanReadable, parseQueryString, createStrin
|
|
|
16
16
|
import { Obfuscator } from '../../../common/util/obfuscate';
|
|
17
17
|
import { getVersion2Attributes, getVersion2DuplicationAttributes, shouldDuplicate, isMfeTarget } from '../../../common/v2/utils';
|
|
18
18
|
import { EVENT_TYPES } from '../../../common/constants/events';
|
|
19
|
-
import { generateUuid } from '../../../common/ids/unique-id';
|
|
19
|
+
import { generateSpanId, generateTraceId, generateUuid } from '../../../common/ids/unique-id';
|
|
20
20
|
export class Aggregate extends AggregateBase {
|
|
21
21
|
static featureName = FEATURE_NAME;
|
|
22
22
|
constructor(agentRef) {
|
|
@@ -126,10 +126,23 @@ export class Aggregate extends AggregateBase {
|
|
|
126
126
|
...event,
|
|
127
127
|
targetAttributes: getVersion2Attributes(target, this)
|
|
128
128
|
});
|
|
129
|
-
if (shouldDuplicate(target, this))
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
if (shouldDuplicate(target, this)) {
|
|
130
|
+
const duplicateEvent = {
|
|
131
|
+
...event,
|
|
132
|
+
targetAttributes: getVersion2DuplicationAttributes(target, this)
|
|
133
|
+
};
|
|
134
|
+
/** the container-duplicate copy represents the same single real HTTP call as the MFE event above, so it
|
|
135
|
+
* needs its own independent trace/span identity -- otherwise the two would collide on the same
|
|
136
|
+
* spanId/traceId, and BELC could not create two separate Spans/Traces from them. Only the id namespace is
|
|
137
|
+
* independent; spanTimestamp is intentionally left as-is (event.spanTimestamp), since it still reflects the
|
|
138
|
+
* real call's actual timing. When `event.spanId` was never set (e.g. distributed tracing is disabled), there
|
|
139
|
+
* is no span identity to duplicate either -- both copies correctly end up with no spanId/traceId. */
|
|
140
|
+
if (event.spanId) {
|
|
141
|
+
duplicateEvent.spanId = generateSpanId();
|
|
142
|
+
duplicateEvent.traceId = generateTraceId();
|
|
143
|
+
}
|
|
144
|
+
this.reportContainerEvent(duplicateEvent, ctx);
|
|
145
|
+
}
|
|
133
146
|
} else {
|
|
134
147
|
this.reportContainerEvent(event, ctx);
|
|
135
148
|
}
|
|
@@ -21,6 +21,7 @@ import { getRegisteredTargetsFromFilename, getVersion2Attributes, getVersion2Dup
|
|
|
21
21
|
import { buildCauseString } from './cause-string';
|
|
22
22
|
import { ShortCircuit } from '../../../common/util/short-circuit';
|
|
23
23
|
import { EVENT_TYPES } from '../../../common/constants/events';
|
|
24
|
+
import { SESSION_EVENTS, SESSION_EVENT_TYPES } from '../../../common/session/constants';
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
|
|
@@ -45,6 +46,27 @@ export class Aggregate extends AggregateBase {
|
|
|
45
46
|
register('ierr', this.processError.bind(this), this.featureName, this.ee);
|
|
46
47
|
register('returnJserror', (jsErrorEvent, softNavAttrs) => this.#storeJserrorForHarvest(jsErrorEvent, softNavAttrs), this.featureName, this.ee);
|
|
47
48
|
|
|
49
|
+
/*
|
|
50
|
+
* The backend only remembers a stack trace for as long as the session that first reported it.
|
|
51
|
+
* The SessionEntity emits SESSION_EVENTS.RESET whenever the session expires (max duration or
|
|
52
|
+
* inactivity) or is otherwise reset, which is the same boundary the backend uses to forget
|
|
53
|
+
* previously-seen stack hashes. Forget them here too, so the next occurrence of an
|
|
54
|
+
* already-seen error resends its full stack_trace once, instead of a browser_stack_hash the
|
|
55
|
+
* backend can no longer resolve.
|
|
56
|
+
*/
|
|
57
|
+
const forgetReportedStacks = () => {
|
|
58
|
+
this.stackReported = {};
|
|
59
|
+
this.observedAt = {};
|
|
60
|
+
};
|
|
61
|
+
let currentSessionId = this.agentRef.runtime.session?.state?.value;
|
|
62
|
+
this.ee.on(SESSION_EVENTS.RESET, forgetReportedStacks);
|
|
63
|
+
this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
|
|
64
|
+
if (!data?.value) return;
|
|
65
|
+
/* A reset in another tab arrived as a cross-tab update carrying a new session id. */
|
|
66
|
+
if (type === SESSION_EVENT_TYPES.CROSS_TAB && currentSessionId && data.value !== currentSessionId) forgetReportedStacks();
|
|
67
|
+
currentSessionId = data.value; // also tracks same-tab writes, so a local reset's new id is picked up
|
|
68
|
+
});
|
|
69
|
+
|
|
48
70
|
// 0 == off, 1 == on
|
|
49
71
|
this.waitForFlags(['err']).then(([errFlag]) => {
|
|
50
72
|
if (errFlag) {
|
|
@@ -115,10 +115,13 @@ export class RegisteredIframeEntity {
|
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
/* Script timings are found by parsing the call stack for the script that called register(), so this has to run
|
|
119
|
+
synchronously here. By the time the registration promise resolves, that caller is no longer on the stack. */
|
|
120
|
+
const timings = findScriptTimings(opts);
|
|
121
|
+
|
|
118
122
|
// Store the registration promise so other methods can wait for it
|
|
119
123
|
this.#registrationPromise = this.#register(opts);
|
|
120
124
|
this.#registrationPromise.then(() => {
|
|
121
|
-
const timings = findScriptTimings();
|
|
122
125
|
// Send initial timing values, skipping ones still at their "not yet known" default
|
|
123
126
|
// (e.g. reportedAt: undefined, fetchStart: 0) to avoid pointless postMessage calls
|
|
124
127
|
for (const [key, value] of Object.entries(timings)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IAEjC,2BA6BC;IAvBC,uBAA4D;IAyB9D,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IAEjC,2BA6BC;IAvBC,uBAA4D;IAyB9D,0GAwGC;IAED,+CAOC;IAED,iDA2EC;CACF;8BA3O6B,4BAA4B;2BAK/B,gCAAgC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/jserrors/aggregate/index.js"],"names":[],"mappings":"AA2BA;;GAEG;AAEH;IACE,2BAAiC;IACjC,2BAgDC;IA3CC,kBAAuB;IACvB,eAAoB;IACpB,qBAA0B;IAC1B,qBAAwB;IAGxB,4BAAgE;IAChE,0BAA+D;IAsCjE;;;;MAMC;IAED;;;MAcC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,kBATW,KAAK,GAAC,aAAa,SACnB,MAAM,aACN,OAAO,qBACP,MAAM,cACN,OAAO,kBACP,MAAM,WACN,MAAM,GACJ,IAAI,CAiBhB;;CAyQF;wBAnYY,OAAO,0BAA0B,EAAE,SAAS;8BAX3B,4BAA4B;2BAE/B,gCAAgC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registered-iframe-entity.d.ts","sourceRoot":"","sources":["../../../src/interfaces/registered-iframe-entity.js"],"names":[],"mappings":"AAuBA;;;;GAIG;AAEH;;;;;;GAMG;AACH;IAkDE;;;OAGG;IACH,kBAFW,sBAAsB,
|
|
1
|
+
{"version":3,"file":"registered-iframe-entity.d.ts","sourceRoot":"","sources":["../../../src/interfaces/registered-iframe-entity.js"],"names":[],"mappings":"AAuBA;;;;GAIG;AAEH;;;;;;GAMG;AACH;IAkDE;;;OAGG;IACH,kBAFW,sBAAsB,EAyDhC;IA5GD,kCAAkC;IAClC,UADW,mBAAmB,CAW7B;IAyBD,mBANa,OAAO,EAQnB;IAbD;;;;;;OAMG;IACH,eAFa,OAAO,CAInB;IAuTD;;;;;OAKG;IACH,oBAHW,MAAM,eACN,MAAM,QAIhB;IAED;;;;;;;OAOG;IACH,cAFa,OAAO,CAAC,IAAI,CAAC,CAgBzB;IAED;;;;;SAKK;IACL,6BAHa,MAAM,eACN,MAAM,QAIlB;IAED;;;;;;OAMG;IACH,cAJW,MAAM,YACN;QAAC,KAAK,CAAC,EAAE,MAAM,GAAC,eAAe,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,GAAC,eAAe,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAC,GACvF,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAC,CAAC,CAI7F;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,OAAO,GAAC,IAAI,YAC1B,OAAO,QAIjB;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,qBACZ,MAAM,QAIhB;IAED;;;;;OAKG;IACH,iBAHW,MAAM,GAAC,IAAI,iBACX,OAAO,QAIjB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,QAMrB;IAED;;;;;MAKE;IACF,aAHW,MAAM,YACN;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAC,OAAO,GAAC,OAAO,GAAC,MAAM,GAAC,MAAM,CAAA;KAAC,QAIpF;;CACF;0BAjdY,OAAO,mCAAmC,EAAE,WAAW;kCACvD,OAAO,mCAAmC,EAAE,mBAAmB;qCAC/D,OAAO,mCAAmC,EAAE,sBAAsB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newrelic/browser-agent",
|
|
3
|
-
"version": "1.323.0-rc.
|
|
3
|
+
"version": "1.323.0-rc.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
|
|
6
6
|
"description": "New Relic Browser Agent",
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
"runtime": {
|
|
171
171
|
"name": "node",
|
|
172
172
|
"onFail": "error",
|
|
173
|
-
"version": ">=
|
|
173
|
+
"version": ">=24.14.0"
|
|
174
174
|
},
|
|
175
175
|
"packageManager": {
|
|
176
176
|
"name": "npm",
|
|
@@ -16,7 +16,7 @@ import { canCapturePayload, isLikelyHumanReadable, parseQueryString, createStrin
|
|
|
16
16
|
import { Obfuscator } from '../../../common/util/obfuscate'
|
|
17
17
|
import { getVersion2Attributes, getVersion2DuplicationAttributes, shouldDuplicate, isMfeTarget } from '../../../common/v2/utils'
|
|
18
18
|
import { EVENT_TYPES } from '../../../common/constants/events'
|
|
19
|
-
import { generateUuid } from '../../../common/ids/unique-id'
|
|
19
|
+
import { generateSpanId, generateTraceId, generateUuid } from '../../../common/ids/unique-id'
|
|
20
20
|
|
|
21
21
|
export class Aggregate extends AggregateBase {
|
|
22
22
|
static featureName = FEATURE_NAME
|
|
@@ -139,7 +139,20 @@ export class Aggregate extends AggregateBase {
|
|
|
139
139
|
/** make a copy of the event for the MFE target if it exists */
|
|
140
140
|
if (isMfeTarget(target)) {
|
|
141
141
|
this.events.add({ ...event, targetAttributes: getVersion2Attributes(target, this) })
|
|
142
|
-
if (shouldDuplicate(target, this))
|
|
142
|
+
if (shouldDuplicate(target, this)) {
|
|
143
|
+
const duplicateEvent = { ...event, targetAttributes: getVersion2DuplicationAttributes(target, this) }
|
|
144
|
+
/** the container-duplicate copy represents the same single real HTTP call as the MFE event above, so it
|
|
145
|
+
* needs its own independent trace/span identity -- otherwise the two would collide on the same
|
|
146
|
+
* spanId/traceId, and BELC could not create two separate Spans/Traces from them. Only the id namespace is
|
|
147
|
+
* independent; spanTimestamp is intentionally left as-is (event.spanTimestamp), since it still reflects the
|
|
148
|
+
* real call's actual timing. When `event.spanId` was never set (e.g. distributed tracing is disabled), there
|
|
149
|
+
* is no span identity to duplicate either -- both copies correctly end up with no spanId/traceId. */
|
|
150
|
+
if (event.spanId) {
|
|
151
|
+
duplicateEvent.spanId = generateSpanId()
|
|
152
|
+
duplicateEvent.traceId = generateTraceId()
|
|
153
|
+
}
|
|
154
|
+
this.reportContainerEvent(duplicateEvent, ctx)
|
|
155
|
+
}
|
|
143
156
|
} else {
|
|
144
157
|
this.reportContainerEvent(event, ctx)
|
|
145
158
|
}
|
|
@@ -23,6 +23,7 @@ import { getRegisteredTargetsFromFilename, getVersion2Attributes, getVersion2Dup
|
|
|
23
23
|
import { buildCauseString } from './cause-string'
|
|
24
24
|
import { ShortCircuit } from '../../../common/util/short-circuit'
|
|
25
25
|
import { EVENT_TYPES } from '../../../common/constants/events'
|
|
26
|
+
import { SESSION_EVENTS, SESSION_EVENT_TYPES } from '../../../common/session/constants'
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
|
|
@@ -48,6 +49,27 @@ export class Aggregate extends AggregateBase {
|
|
|
48
49
|
register('ierr', this.processError.bind(this), this.featureName, this.ee)
|
|
49
50
|
register('returnJserror', (jsErrorEvent, softNavAttrs) => this.#storeJserrorForHarvest(jsErrorEvent, softNavAttrs), this.featureName, this.ee)
|
|
50
51
|
|
|
52
|
+
/*
|
|
53
|
+
* The backend only remembers a stack trace for as long as the session that first reported it.
|
|
54
|
+
* The SessionEntity emits SESSION_EVENTS.RESET whenever the session expires (max duration or
|
|
55
|
+
* inactivity) or is otherwise reset, which is the same boundary the backend uses to forget
|
|
56
|
+
* previously-seen stack hashes. Forget them here too, so the next occurrence of an
|
|
57
|
+
* already-seen error resends its full stack_trace once, instead of a browser_stack_hash the
|
|
58
|
+
* backend can no longer resolve.
|
|
59
|
+
*/
|
|
60
|
+
const forgetReportedStacks = () => {
|
|
61
|
+
this.stackReported = {}
|
|
62
|
+
this.observedAt = {}
|
|
63
|
+
}
|
|
64
|
+
let currentSessionId = this.agentRef.runtime.session?.state?.value
|
|
65
|
+
this.ee.on(SESSION_EVENTS.RESET, forgetReportedStacks)
|
|
66
|
+
this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
|
|
67
|
+
if (!data?.value) return
|
|
68
|
+
/* A reset in another tab arrived as a cross-tab update carrying a new session id. */
|
|
69
|
+
if (type === SESSION_EVENT_TYPES.CROSS_TAB && currentSessionId && data.value !== currentSessionId) forgetReportedStacks()
|
|
70
|
+
currentSessionId = data.value // also tracks same-tab writes, so a local reset's new id is picked up
|
|
71
|
+
})
|
|
72
|
+
|
|
51
73
|
// 0 == off, 1 == on
|
|
52
74
|
this.waitForFlags(['err']).then(([errFlag]) => {
|
|
53
75
|
if (errFlag) {
|
|
@@ -107,11 +107,14 @@ export class RegisteredIframeEntity {
|
|
|
107
107
|
return
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/* Script timings are found by parsing the call stack for the script that called register(), so this has to run
|
|
111
|
+
synchronously here. By the time the registration promise resolves, that caller is no longer on the stack. */
|
|
112
|
+
const timings = findScriptTimings(opts)
|
|
113
|
+
|
|
110
114
|
// Store the registration promise so other methods can wait for it
|
|
111
115
|
this.#registrationPromise = this.#register(opts)
|
|
112
116
|
this.#registrationPromise
|
|
113
117
|
.then(() => {
|
|
114
|
-
const timings = findScriptTimings()
|
|
115
118
|
// Send initial timing values, skipping ones still at their "not yet known" default
|
|
116
119
|
// (e.g. reportedAt: undefined, fetchStart: 0) to avoid pointless postMessage calls
|
|
117
120
|
for (const [key, value] of Object.entries(timings)) {
|