@newrelic/browser-agent 1.319.0 → 1.320.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/common/config/init.js +17 -2
  3. package/dist/cjs/common/config/runtime.js +2 -1
  4. package/dist/cjs/common/constants/env.cdn.js +1 -1
  5. package/dist/cjs/common/constants/env.npm.js +1 -1
  6. package/dist/cjs/common/constants/iframe-constants.js +16 -0
  7. package/dist/cjs/common/url/add-url.js +23 -0
  8. package/dist/cjs/common/util/console.js +40 -33
  9. package/dist/cjs/common/v2/mfe-vitals.js +9 -2
  10. package/dist/cjs/common/v2/script-correlation.js +2 -0
  11. package/dist/cjs/common/v2/script-tracker-constants.js +13 -0
  12. package/dist/cjs/common/v2/script-tracker.js +54 -29
  13. package/dist/cjs/common/v2/timing-factory.js +18 -0
  14. package/dist/cjs/common/v2/utils.js +100 -6
  15. package/dist/cjs/features/ajax/instrument/index.js +4 -14
  16. package/dist/cjs/features/utils/instrument-base.js +13 -0
  17. package/dist/cjs/interfaces/registered-iframe-entity.js +527 -0
  18. package/dist/cjs/loaders/api/register.js +10 -6
  19. package/dist/cjs/loaders/configure/configure.js +14 -0
  20. package/dist/cjs/loaders/configure/iframe-message-handler.js +270 -0
  21. package/dist/esm/common/config/init.js +17 -2
  22. package/dist/esm/common/config/runtime.js +2 -1
  23. package/dist/esm/common/constants/env.cdn.js +1 -1
  24. package/dist/esm/common/constants/env.npm.js +1 -1
  25. package/dist/esm/common/constants/iframe-constants.js +10 -0
  26. package/dist/esm/common/url/add-url.js +16 -0
  27. package/dist/esm/common/util/console.js +40 -33
  28. package/dist/esm/common/v2/mfe-vitals.js +9 -2
  29. package/dist/esm/common/v2/script-correlation.js +2 -0
  30. package/dist/esm/common/v2/script-tracker-constants.js +7 -0
  31. package/dist/esm/common/v2/script-tracker.js +54 -29
  32. package/dist/esm/common/v2/timing-factory.js +12 -0
  33. package/dist/esm/common/v2/utils.js +97 -6
  34. package/dist/esm/features/ajax/instrument/index.js +1 -11
  35. package/dist/esm/features/utils/instrument-base.js +13 -0
  36. package/dist/esm/interfaces/registered-iframe-entity.js +518 -0
  37. package/dist/esm/loaders/api/register.js +10 -6
  38. package/dist/esm/loaders/configure/configure.js +15 -1
  39. package/dist/esm/loaders/configure/iframe-message-handler.js +263 -0
  40. package/dist/tsconfig.tsbuildinfo +1 -1
  41. package/dist/types/common/config/init.d.ts.map +1 -1
  42. package/dist/types/common/config/runtime.d.ts.map +1 -1
  43. package/dist/types/common/constants/iframe-constants.d.ts +11 -0
  44. package/dist/types/common/constants/iframe-constants.d.ts.map +1 -0
  45. package/dist/types/common/url/add-url.d.ts +2 -0
  46. package/dist/types/common/url/add-url.d.ts.map +1 -0
  47. package/dist/types/common/util/console.d.ts +40 -33
  48. package/dist/types/common/util/console.d.ts.map +1 -1
  49. package/dist/types/common/v2/mfe-vitals.d.ts.map +1 -1
  50. package/dist/types/common/v2/script-correlation.d.ts +2 -0
  51. package/dist/types/common/v2/script-correlation.d.ts.map +1 -1
  52. package/dist/types/common/v2/script-tracker-constants.d.ts +7 -0
  53. package/dist/types/common/v2/script-tracker-constants.d.ts.map +1 -0
  54. package/dist/types/common/v2/script-tracker.d.ts +4 -1
  55. package/dist/types/common/v2/script-tracker.d.ts.map +1 -1
  56. package/dist/types/common/v2/timing-factory.d.ts +9 -0
  57. package/dist/types/common/v2/timing-factory.d.ts.map +1 -0
  58. package/dist/types/common/v2/utils.d.ts +37 -0
  59. package/dist/types/common/v2/utils.d.ts.map +1 -1
  60. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  61. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  62. package/dist/types/interfaces/registered-iframe-entity.d.ts +116 -0
  63. package/dist/types/interfaces/registered-iframe-entity.d.ts.map +1 -0
  64. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  65. package/dist/types/loaders/configure/iframe-message-handler.d.ts +17 -0
  66. package/dist/types/loaders/configure/iframe-message-handler.d.ts.map +1 -0
  67. package/package.json +9 -1
  68. package/src/common/config/init.js +13 -2
  69. package/src/common/config/runtime.js +2 -1
  70. package/src/common/constants/iframe-constants.js +11 -0
  71. package/src/common/url/add-url.js +18 -0
  72. package/src/common/util/console.js +40 -33
  73. package/src/common/v2/mfe-vitals.js +7 -4
  74. package/src/common/v2/script-correlation.js +2 -0
  75. package/src/common/v2/script-tracker-constants.js +7 -0
  76. package/src/common/v2/script-tracker.js +65 -26
  77. package/src/common/v2/timing-factory.js +10 -0
  78. package/src/common/v2/utils.js +102 -6
  79. package/src/features/ajax/instrument/index.js +1 -13
  80. package/src/features/utils/instrument-base.js +12 -0
  81. package/src/interfaces/registered-iframe-entity.js +482 -0
  82. package/src/loaders/api/register.js +9 -5
  83. package/src/loaders/configure/configure.js +15 -1
  84. package/src/loaders/configure/iframe-message-handler.js +244 -0
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/common/v2/utils.js"],"names":[],"mappings":"AAkBA;;;;;GAKG;AACH,+CAJW,MAAM,GAAC,MAAM,YACb,GAAC,GACC,OAAO,oCAAoC,EAAE,yBAAyB,EAAE,CAMpF;AAED;;;;;GAKG;AACH,2DAJW,MAAM,YACN,GAAC,GACC,OAAO,oCAAoC,EAAE,yBAAyB,EAAE,CAMpF;AAED;;;;;;;GAOG;AACH,+CAJW,MAAM,sBACN,iBAAiB,GACf,MAAM,CAclB;AAED;;;;;;;GAOG;AACH,yDAJW,OAAO,oCAAoC,EAAE,yBAAyB,qBACtE,GAAC,GACC,MAAM,CAKlB;AAED;;;;;;GAMG;AACH,wCAJW,OAAO,oCAAoC,EAAE,yBAAyB,qBACtE,GAAC,GACC,OAAO,CAInB;AAED;;;;GAIG;AACH,oDAHW,GAAC,SAkBX;;;;uBApGS,MAAM"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/common/v2/utils.js"],"names":[],"mappings":"AAkBA;;;;;GAKG;AACH,0EAJW,MAAM,YACN,GAAC,GACC,OAAO,sCAAsC,EAAE,WAAW,GAAC,SAAS,CAMhF;AAED;;;;;GAKG;AACH,+CAJW,MAAM,GAAC,MAAM,YACb,GAAC,GACC,OAAO,oCAAoC,EAAE,yBAAyB,EAAE,CAMpF;AAED;;;;;;;;;;;;GAYG;AACH,2DAJW,MAAM,YACN,GAAC,GACC,OAAO,oCAAoC,EAAE,yBAAyB,EAAE,CAOpF;AAED;;;;;;;;;;;;GAYG;AACH,wEA6BC;AAED;;;;;;;GAOG;AACH,+CAJW,MAAM,sBACN,iBAAiB,GACf,MAAM,CAclB;AAED;;;;;;;GAOG;AACH,yDAJW,OAAO,oCAAoC,EAAE,yBAAyB,qBACtE,GAAC,GACC,MAAM,CAKlB;AAED;;;;;;GAMG;AACH,wCAJW,OAAO,oCAAoC,EAAE,yBAAyB,qBACtE,GAAC,GACC,OAAO,CAInB;AAED;;;;GAIG;AACH,oDAHW,GAAC,SAoBX;AAED;;;;;;;GAOG;AACH,+DAUC;;;;uBA1LS,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/instrument/index.js"],"names":[],"mappings":"AA+BA;IACE,2BAAiC;IACjC,2BAiCC;IA9BC,OAA0B;IAE1B,8DAAkF;CA6BrF;AA6bD,qCAA8B;+BAjfC,6BAA6B;mBAFzC,uBAAuB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/instrument/index.js"],"names":[],"mappings":"AAgCA;IACE,2BAAiC;IACjC,2BAiCC;IA9BC,OAA0B;IAE1B,8DAAkF;CA6BrF;AAgbD,qCAA8B;+BAreC,6BAA6B;mBAFzC,uBAAuB"}
@@ -1 +1 @@
1
- {"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"AA4BA;;;GAGG;AACH;IACE;;;;OAIG;IACH,sBAHW,MAAM,eACN,MAAM,EA0ChB;IArCC,8IAA8I;IAC9I,cADW,WAAW,SAAS,CACF;IAE7B;;;MAGE;IACF,eAHU,OAAO,kBAAkB,EAAE,aAAa,CAGpB;IAE9B;;;MAGE;IACF,iCAAmC;IACnC,kCAEE;IAEF;;;MAGE;IACF,uBAAiC;IAiBnC;;;;;;;OAOG;IACH,2BALW,MAAM,qDAEN,MAAM,QA6DhB;;CAiDF;4BAvL2B,gBAAgB"}
1
+ {"version":3,"file":"instrument-base.d.ts","sourceRoot":"","sources":["../../../../src/features/utils/instrument-base.js"],"names":[],"mappings":"AA4BA;;;GAGG;AACH;IACE;;;;OAIG;IACH,sBAHW,MAAM,eACN,MAAM,EA0ChB;IArCC,8IAA8I;IAC9I,cADW,WAAW,SAAS,CACF;IAE7B;;;MAGE;IACF,eAHU,OAAO,kBAAkB,EAAE,aAAa,CAGpB;IAE9B;;;MAGE;IACF,iCAAmC;IACnC,kCAEE;IAEF;;;MAGE;IACF,uBAAiC;IAiBnC;;;;;;;OAOG;IACH,2BALW,MAAM,qDAEN,MAAM,QAyEhB;;CAiDF;4BAnM2B,gBAAgB"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @typedef {import('../loaders/api/register-api-types').RegisterAPI} RegisterAPI
3
+ * @typedef {import('../loaders/api/register-api-types').RegisterAPIMetadata} RegisterAPIMetadata
4
+ * @typedef {import('../loaders/api/register-api-types').RegisterAPIConstructor} RegisterAPIConstructor
5
+ */
6
+ /**
7
+ * @experimental
8
+ * IMPORTANT: This feature is being developed for use internally and is not in a public-facing production-ready state.
9
+ * It is not recommended for use in production environments and will not receive support for issues.
10
+ *
11
+ * An interface for registering an external caller to report through the base agent to a different target than the base agent.
12
+ */
13
+ export class RegisteredIframeEntity {
14
+ /**
15
+ *
16
+ * @param {RegisterAPIConstructor} opts The options for setting up the registered iframe entity.
17
+ */
18
+ constructor(opts: RegisterAPIConstructor);
19
+ /** @type {RegisterAPIMetadata} */
20
+ metadata: RegisterAPIMetadata;
21
+ set blocked(value: boolean);
22
+ /**
23
+ * Whether this entity is blocked from sending further calls to the container. Backed by
24
+ * `this.metadata.target.blocked` (rather than an independent field) so that this always
25
+ * reflects the container's blocked state too, including future syncs from response metadata --
26
+ * there is only ever one source of truth for "blocked", not one flag per side.
27
+ * @returns {boolean}
28
+ */
29
+ get blocked(): boolean;
30
+ /**
31
+ * Reports a browser PageAction event along with a name and optional attributes to the registered target.
32
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/addpageaction/}
33
+ * @param {string} name Name or category of the action. Reported as the actionName attribute.
34
+ * @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}. The key is reported as its own PageAction attribute with the specified values.
35
+ */
36
+ addPageAction(name: string, attributes?: object): void;
37
+ /**
38
+ * @experimental
39
+ * IMPORTANT: This feature is being developed for use internally and is not in a public-facing production-ready state.
40
+ * It is not recommended for use in production environments and will not receive support for issues.
41
+ *
42
+ * Deregister the registered entity (this), which blocks its use and captures end of life timings.
43
+ * @returns {Promise<void>}
44
+ */
45
+ deregister(): Promise<void>;
46
+ /**
47
+ * Records a custom event with a specified eventType and attributes.
48
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordCustomEvent/}
49
+ * @param {string} eventType The eventType to store the event as.
50
+ * @param {Object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}.
51
+ */
52
+ recordCustomEvent(eventType: string, attributes?: Object): void;
53
+ /**
54
+ * Measures a task that is recorded as a BrowserPerformance event.
55
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/measure/}
56
+ * @param {string} name The name of the task
57
+ * @param {{start?: number|PerformanceMark, end?: number|PerformanceMark, customAttributes?: object}} [options] An object used to control the way the measure API operates
58
+ * @returns {Promise<{start: number, end: number, duration: number, customAttributes: object}>} Measurement details
59
+ */
60
+ measure(name: string, options?: {
61
+ start?: number | PerformanceMark;
62
+ end?: number | PerformanceMark;
63
+ customAttributes?: object;
64
+ }): Promise<{
65
+ start: number;
66
+ end: number;
67
+ duration: number;
68
+ customAttributes: object;
69
+ }>;
70
+ /**
71
+ * Adds a user-defined attribute name and value to subsequent events on the page for the registered target. Note -- the persist flag does not work with the register API.
72
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setcustomattribute/}
73
+ * @param {string} name Name of the attribute. Appears as column in the PageView event. It will also appear as a column in the PageAction event if you are using it.
74
+ * @param {string|number|boolean|null} value Value of the attribute. Appears as the value in the named attribute column in the PageView event. It will appear as a column in the PageAction event if you are using it. Custom attribute values cannot be complex objects, only simple types such as Strings, Integers and Booleans. Passing a null value unsets any existing attribute of the same name.
75
+ * @param {boolean} [persist] Default false. If set to true, the name-value pair will also be set into the browser's storage API. Then on the following instrumented pages that load within the same session, the pair will be re-applied as a custom attribute.
76
+ */
77
+ setCustomAttribute(name: string, value: string | number | boolean | null, persist?: boolean): void;
78
+ /**
79
+ * Identifies a browser error without disrupting your app's operations for the registered target.
80
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/noticeerror/}
81
+ * @param {Error|string} error Provide a meaningful error message that you can use when analyzing data on browser's JavaScript errors page.
82
+ * @param {object} [customAttributes] An object containing name/value pairs representing custom attributes.
83
+ */
84
+ noticeError(error: Error | string, customAttributes?: object): void;
85
+ /**
86
+ * Adds a user-defined identifier string to subsequent events on the page for the registered target.
87
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setuserid/}
88
+ * @param {string|null} value A string identifier for the end-user, useful for tying all browser events to specific users. The value parameter does not have to be unique. If IDs should be unique, the caller is responsible for that validation. Passing a null value unsets any existing user ID.
89
+ * @param {boolean} [resetSession=false] Optional param. Should not be used from a registered entity context. To reset a session when updating user id, must be initiated by the main agent.
90
+ */
91
+ setUserId(value: string | null, resetSession?: boolean): void;
92
+ /**
93
+ * Adds a user-defined application version string to subsequent events on the page for the registered target.
94
+ * This decorates all payloads with an attribute of `application.version` which is queryable in NR1.
95
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setapplicationversion/}
96
+ * @param {string|null} value A string identifier for the application version, useful for
97
+ * tying all browser events to a specific release tag. The value parameter does not
98
+ * have to be unique. Passing a null value unsets any existing value.
99
+ */
100
+ setApplicationVersion(value: string | null): void;
101
+ /**
102
+ * Capture a single log for the registered target.
103
+ * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/log/}
104
+ * @param {string} message String to be captured as log message
105
+ * @param {{customAttributes?: object, level?: 'ERROR'|'TRACE'|'DEBUG'|'INFO'|'WARN'}} [options] customAttributes defaults to `{}` if not assigned, level defaults to `info` if not assigned.
106
+ */
107
+ log(message: string, options?: {
108
+ customAttributes?: object;
109
+ level?: "ERROR" | "TRACE" | "DEBUG" | "INFO" | "WARN";
110
+ }): void;
111
+ #private;
112
+ }
113
+ export type RegisterAPI = import("../loaders/api/register-api-types").RegisterAPI;
114
+ export type RegisterAPIMetadata = import("../loaders/api/register-api-types").RegisterAPIMetadata;
115
+ export type RegisterAPIConstructor = import("../loaders/api/register-api-types").RegisterAPIConstructor;
116
+ //# sourceMappingURL=registered-iframe-entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registered-iframe-entity.d.ts","sourceRoot":"","sources":["../../../src/interfaces/registered-iframe-entity.js"],"names":[],"mappings":"AAsBA;;;;GAIG;AAEH;;;;;;GAMG;AACH;IAkDE;;;OAGG;IACH,kBAFW,sBAAsB,EAsDhC;IAzGD,kCAAkC;IAClC,UADW,mBAAmB,CAW7B;IAyBD,mBANa,OAAO,EAQnB;IAbD;;;;;;OAMG;IACH,eAFa,OAAO,CAInB;IAgTD;;;;;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;0BA1cY,OAAO,mCAAmC,EAAE,WAAW;kCACvD,OAAO,mCAAmC,EAAE,mBAAmB;qCAC/D,OAAO,mCAAmC,EAAE,sBAAsB"}
@@ -1 +1 @@
1
- {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/configure.js"],"names":[],"mappings":"AAeA;;;GAGG;AACH,oGAkEC"}
1
+ {"version":3,"file":"configure.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/configure.js"],"names":[],"mappings":"AAiBA;;;GAGG;AACH,oGA8EC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Handles both entity registration and method calls on existing entities
3
+ * @async
4
+ * @param {MessageEvent} event - The message event containing the method call or registration request
5
+ * @param {Object} agent - The browser agent instance
6
+ * @returns {Promise<{entity: Object|null, result: any}>}
7
+ */
8
+ export function handleMethodCall(event: MessageEvent, agent: Object): Promise<{
9
+ entity: Object | null;
10
+ result: any;
11
+ }>;
12
+ /**
13
+ * Sets up a postMessage listener to handle API calls and timing updates from iframes
14
+ * @param {Object} agent The agent instance
15
+ */
16
+ export function setupIframeMFEMessageListener(agent: Object): void;
17
+ //# sourceMappingURL=iframe-message-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iframe-message-handler.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/iframe-message-handler.js"],"names":[],"mappings":"AA+GA;;;;;;GAMG;AACH,wCAJW,YAAY,SACZ,MAAM,GACJ,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,GAAC,IAAI,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAC,CAAC,CA+DvD;AA6BD;;;GAGG;AACH,qDAFW,MAAM,QAiChB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.319.0",
3
+ "version": "1.320.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -32,6 +32,9 @@
32
32
  "interfaces/registered-entity": [
33
33
  "dist/types/interfaces/registered-entity.d.ts"
34
34
  ],
35
+ "interfaces/registered-iframe-entity": [
36
+ "dist/types/interfaces/registered-iframe-entity.d.ts"
37
+ ],
35
38
  "features/ajax": [
36
39
  "dist/types/features/ajax/instrument/index.d.ts"
37
40
  ],
@@ -98,6 +101,11 @@
98
101
  "require": "./dist/cjs/interfaces/registered-entity.js",
99
102
  "default": "./dist/esm/interfaces/registered-entity.js"
100
103
  },
104
+ "./interfaces/registered-iframe-entity": {
105
+ "types": "./dist/types/interfaces/registered-iframe-entity.d.ts",
106
+ "require": "./dist/cjs/interfaces/registered-iframe-entity.js",
107
+ "default": "./dist/esm/interfaces/registered-iframe-entity.js"
108
+ },
101
109
  "./features/ajax": {
102
110
  "types": "./dist/types/features/ajax/instrument/index.d.ts",
103
111
  "require": "./dist/cjs/features/ajax/instrument/index.js",
@@ -23,7 +23,9 @@ const InitModelFn = () => {
23
23
  feature_flags: [],
24
24
  experimental: {
25
25
  register: false,
26
- resources: false
26
+ resources: false,
27
+ iframe_bridge: false,
28
+ iframe_domains: []
27
29
  },
28
30
  mask_selector: '*',
29
31
  block_selector: '[data-nr-block]',
@@ -53,7 +55,16 @@ const InitModelFn = () => {
53
55
  register: {
54
56
  get enabled () { return hiddenState.feature_flags.includes(FEATURE_FLAGS.REGISTER) || hiddenState.experimental.register },
55
57
  set enabled (val) { hiddenState.experimental.register = val },
56
- duplicate_data_to_container: false
58
+ duplicate_data_to_container: false,
59
+ // experimental iframe bridge feature
60
+ get allow_iframe_bridge () { return hiddenState.feature_flags.includes(FEATURE_FLAGS.IFRAME_BRIDGE) || hiddenState.experimental.iframe_bridge },
61
+ set allow_iframe_bridge (val) { hiddenState.experimental.iframe_bridge = val },
62
+ get iframe_domains () { return hiddenState.experimental.iframe_domains },
63
+ set iframe_domains (val) {
64
+ if (Array.isArray(val)) hiddenState.experimental.iframe_domains = val
65
+ else warn(1, val)
66
+ }
67
+
57
68
  }
58
69
  },
59
70
  browser_consent_mode: { enabled: false },
@@ -55,7 +55,8 @@ const RuntimeModel = {
55
55
  registeredEntities: [],
56
56
  /** a proxy is set in agent-session to track jsAttributes changes for harvesting mechanics */
57
57
  jsAttributesMetadata: { bytes: 0 },
58
- get harvestCount () { return ++_harvestCount }
58
+ get harvestCount () { return ++_harvestCount },
59
+ listeningForIframeMessages: false
59
60
  }
60
61
 
61
62
  export const mergeRuntime = (runtime) => {
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ export const prefix = 'newrelic-iframe-'
6
+
7
+ export const IFRAME_TIMING_UPDATE = prefix + 'timing-update'
8
+ export const IFRAME_API = prefix + 'api'
9
+ export const IFRAME_API_RESPONSE = prefix + 'api-response'
10
+ export const IFRAME_VITALS_UPDATE = prefix + 'vitals-update'
11
+ export const IFRAME_AJAX = prefix + 'ajax'
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { parseUrl } from './parse-url.js'
6
+
7
+ export function addUrl (ctx, url) {
8
+ var parsed = parseUrl(url)
9
+ var params = ctx.params || ctx
10
+
11
+ params.hostname = parsed.hostname
12
+ params.port = parsed.port
13
+ params.protocol = parsed.protocol
14
+ params.host = parsed.hostname + ':' + parsed.port
15
+ params.pathname = parsed.pathname
16
+ ctx.parsedOrigin = parsed
17
+ ctx.sameOrigin = parsed.sameOrigin
18
+ }
@@ -16,39 +16,39 @@ import { dispatchGlobalEvent } from '../dispatch/global-event'
16
16
  *
17
17
  * | Code | Message |
18
18
  * |------|---------|
19
- * | 1 | An error occurred while setting a property of a Configurable |
20
- * | 2 | An error occurred while setting a Configurable |
21
- * | 3 | Setting a Configurable requires an object as input |
22
- * | 4 | Setting a Configurable requires a model to set its initial properties |
19
+ * | 1 | An error occurred while setting a property of a Configurable. |
20
+ * | 2 | An error occurred while setting a Configurable. |
21
+ * | 3 | Setting a Configurable requires an object as input. |
22
+ * | 4 | Setting a Configurable requires a model to set its initial properties. |
23
23
  * | 5 | An invalid session_replay.mask_selector was provided. \* will be used. |
24
- * | 6 | An invalid session_replay.block_selector was provided and will not be used |
25
- * | 7 | An invalid session_replay.mask_input_option was provided and will not be used |
26
- * | 8 | Shared context requires an object as input |
27
- * | 9 | An error occurred while setting SharedContext |
28
- * | 10 | Failed to read from storage API |
29
- * | 11 | Failed to write to the storage API |
24
+ * | 6 | An invalid session_replay.block_selector was provided and will not be used. |
25
+ * | 7 | An invalid session_replay.mask_input_option was provided and will not be used. |
26
+ * | 8 | Shared context requires an object as input. |
27
+ * | 9 | An error occurred while setting SharedContext. |
28
+ * | 10 | Failed to read from storage API. |
29
+ * | 11 | Failed to write to the storage API. |
30
30
  * | 12 | An obfuscation replacement rule was detected missing a "regex" value. |
31
- * | 13 | An obfuscation replacement rule contains a "regex" value with an invalid type (must be a string or RegExp) |
32
- * | 14 | An obfuscation replacement rule contains a "replacement" value with an invalid type (must be a string) |
33
- * | 15 | An error occurred while intercepting XHR |
34
- * | 16 | Could not cast log message to string |
31
+ * | 13 | An obfuscation replacement rule contains a "regex" value with an invalid type (must be a string or RegExp). |
32
+ * | 14 | An obfuscation replacement rule contains a "replacement" value with an invalid type (must be a string). |
33
+ * | 15 | An error occurred while intercepting XHR. |
34
+ * | 16 | Could not cast log message to string. |
35
35
  * | 17 | Could not calculate New Relic server time. Agent shutting down. |
36
36
  * | 18 | RUM call failed. Agent shutting down. |
37
37
  * | 19 | SPA scheduler is not initialized. Saved interaction is not sent! |
38
38
  * | 20 | A problem occurred when starting up session manager. This page will not start or extend any session. |
39
39
  * | 21 | Failed to initialize the agent. Could not determine the runtime environment. |
40
40
  * | 22 | Failed to initialize all enabled instrument classes (agent aborted) - |
41
- * | 23 | An unexpected issue occurred |
41
+ * | 23 | An unexpected issue occurred. |
42
42
  * | 24 | Something prevented the agent from instrumenting. |
43
43
  * | 25 | Something prevented the agent from being downloaded. |
44
44
  * | 26 | Failed to initialize instrument classes. |
45
45
  * | 27 | Downloading runtime APIs failed... |
46
46
  * | 28 | The Browser Agent is attempting to send a very large payload. This is usually tied to large amounts of custom attributes. Please check your configurations. |
47
- * | 29 | Failed to wrap logger: invalid argument(s) |
48
- * | 30 | Invalid log level |
49
- * | 31 | Ignored log: Log is larger than maximum payload size |
50
- * | 32 | Ignored log: Invalid message |
51
- * | 33 | Session Replay Aborted |
47
+ * | 29 | Failed to wrap logger: invalid argument(s). |
48
+ * | 30 | Invalid log level. |
49
+ * | 31 | Ignored log: Log is larger than maximum payload size. |
50
+ * | 32 | Ignored log: Invalid message. |
51
+ * | 33 | Session Replay Aborted. |
52
52
  * | 34 | Downloading and initializing a feature failed... |
53
53
  * | 35 | Call to agent api failed. The API is not currently initialized. |
54
54
  * | 36 | A feature is enabled but one or more dependent features have not been initialized. This may cause unintended consequences or missing data... |
@@ -57,7 +57,7 @@ import { dispatchGlobalEvent } from '../dispatch/global-event'
57
57
  * | 39 | Failed to execute setCustomAttribute. Name must be a string type. |
58
58
  * | 40 | Failed to execute setCustomAttribute. Non-null value must be a string, number or boolean type. |
59
59
  * | 41 | Failed to execute setUserId. Non-null value must be a string type. |
60
- * | 42 | Failed to execute setApplicationVersion. Expected <String \| null> |
60
+ * | 42 | Failed to execute setApplicationVersion. Expected <String \| null>. |
61
61
  * | 43 | Agent not configured properly. |
62
62
  * | 44 | Invalid object passed to generic event aggregate. Missing "eventType". |
63
63
  * | 45 | An internal agent process failed to execute. |
@@ -66,27 +66,34 @@ import { dispatchGlobalEvent } from '../dispatch/global-event'
66
66
  * | 48 | Supplied an invalid API target. Must be an <Object> that contains valid (string) id and name properties. |
67
67
  * | 49 | Supplied API target is missing an entityGuid. Some APIs may not behave correctly without a valid entityGuid (ex. logs). |
68
68
  * | 50 | Failed to connect. Cannot allow registered API. |
69
- * | 51 | Container agent is not available to register with. Can not connect |
69
+ * | 51 | Container agent is not available to register with. Can not connect. |
70
70
  * | 52 | Unexpected problem encountered. There should be at least one app for harvest! |
71
- * | 53 | Did not receive a valid entityGuid from connection response |
72
- * | 54 | An experimental feature is being used. Support can not be offered for issues |
73
- * | 55 | Register API has been disabled on the container agent |
74
- * | 56 | Could not find a matching entity to store data |
71
+ * | 53 | Did not receive a valid entityGuid from connection response. |
72
+ * | 54 | An experimental feature is being used. Support can not be offered for issues. |
73
+ * | 55 | Register API has been disabled on the container agent. |
74
+ * | 56 | Could not find a matching entity to store data. |
75
75
  * | 57 | Failed to execute measure. Arguments must have valid types. |
76
76
  * | 58 | Failed to execute measure. Resulting duration must be non-negative. |
77
- * | 59 | Session replay harvested before a session trace payload could be sent. This could be problematic for replays that rely on a trace |
78
- * | 60 | Session trace aborted |
77
+ * | 59 | Session replay harvested before a session trace payload could be sent. This could be problematic for replays that rely on a trace. |
78
+ * | 60 | Session trace aborted. |
79
79
  * | 61 | Timestamps must be non-negative and end time cannot be before start time. |
80
- * | 62 | Timestamp must be a unix timestamp greater than the page origin time |
81
- * | 63 | A single event was larger than the maximum allowed payload size |
80
+ * | 62 | Timestamp must be a unix timestamp greater than the page origin time. |
81
+ * | 63 | A single event was larger than the maximum allowed payload size. |
82
82
  * | 64 | Required globals have been mutated before being accessed by the browser agent. This can cause issues and should be avoided. |
83
- * | 65 | Consent API argument must be boolean or undefined |
84
- * | 66 | A new agent session has started |
83
+ * | 65 | Consent API argument must be boolean or undefined. |
84
+ * | 66 | A new agent session has started. |
85
85
  * | 67 | The "spa" feature has been deprecated and disabled. Please use/import "soft_navigations" instead for tracking of BrowserInteraction data. |
86
86
  * | 68 | API has been deregistered and can no longer be used. Call "register" API again with credentials to start over. |
87
- * | 69 | More than one Browser agent is running on the page |
87
+ * | 69 | More than one Browser agent is running on the page. |
88
88
  * | 70 | A session replay payload failed to send and is being retried. Recording is paused during the retry period, and will resume when a successful harvest is made. Some replay activity may be missed during retry phases. |
89
89
  * | 71 | An invalid feature mode was detected and set to "off". |
90
+ * | 72 | RegisteredIframeEntity failed to transmit API data from an iframe to window context. |
91
+ * | 73 | RegisteredIframeEntity failed to register with window context. |
92
+ * | 74 | RegisteredIframeEntity rejected message from unauthorized origin. |
93
+ * | 75 | RegisteredIframeEntity rejected message with mismatched iframeInterfaceId. |
94
+ * | 76 | Agent rejected post message, could not match with existing entity. |
95
+ * | 77 | Agent rejected post message, could not validate origin. |
96
+ * | 78 | RegisteredIframeEntity could not determine parent origin and will not register, to avoid trusting messages from any origin. |
90
97
  *
91
98
  * @param {number} code The warning code to emit, which will be used to link to the warning code documentation
92
99
  * @param {*} [secondary] Secondary data to include, usually an extra message, error or object
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { globalScope, isBrowserScope } from '../constants/runtime'
7
7
  import { now } from '../timing/now'
8
+ import { CORRELATION_STALE_THRESHOLD_MS } from './script-tracker-constants'
8
9
 
9
10
  /**
10
11
  * @typedef {import('../../loaders/api/register-api-types').RegisterAPITimings} RegisterAPITimings
@@ -131,10 +132,12 @@ export function trackMFEVitals (target, timings) {
131
132
 
132
133
  const vitals = {
133
134
  fcp: {
134
- get value () { return getTimeRelativeToScriptStart(fcpObservedAt) }
135
+ get value () { return getTimeRelativeToScriptStart(fcpObservedAt) },
136
+ set value (v) { fcpObservedAt = v }
135
137
  },
136
138
  lcp: {
137
- get value () { return getTimeRelativeToScriptStart(lcpObservedAt) }
139
+ get value () { return getTimeRelativeToScriptStart(lcpObservedAt) },
140
+ set value (v) { lcpObservedAt = v }
138
141
  },
139
142
  cls: {
140
143
  value: undefined
@@ -149,11 +152,11 @@ export function trackMFEVitals (target, timings) {
149
152
 
150
153
  const observers = []
151
154
 
152
- // If FCP hasn't been observed within 10 seconds, give up and shut down all observers.
155
+ // If FCP hasn't been observed within this window, give up and shut down all observers.
153
156
  // Once FCP is observed, the other vitals are left to record until their natural lifespan ends.
154
157
  setTimeout(() => {
155
158
  if (!fcpObservedAt) vitals.disconnect()
156
- }, 10000)
159
+ }, CORRELATION_STALE_THRESHOLD_MS)
157
160
 
158
161
  const populateVitalMinimums = () => {
159
162
  fcpObservedAt ??= now()
@@ -18,6 +18,8 @@ export class ScriptCorrelation {
18
18
  constructor (url) {
19
19
  /** @type {string} The cleaned URL of the script */
20
20
  this.url = url
21
+ /** @type {Set<string>} MFE target ids that have already claimed this correlation via a completed `findScriptTimings()` call. Keyed per target rather than a single flag so one script that registers multiple distinct MFEs (each with its own id) doesn't have the second MFE's registration mistaken for a stale reuse of the first's. */
22
+ this.claimedBy = new Set()
21
23
  }
22
24
 
23
25
  /**
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /** @type {number} - Correlations are keyed only by script URL and never cleared per-registration, so a script registered more than once in a session (SPA remount, shared bundle, etc.) can reuse a correlation from a much earlier load. If the correlation's recorded start predates `registeredAt` by more than this, treat it as stale and fall back to `registeredAt`. */
7
+ export const CORRELATION_STALE_THRESHOLD_MS = 10000
@@ -8,9 +8,12 @@ import { now } from '../timing/now'
8
8
  import { cleanURL } from '../url/clean-url'
9
9
  import { chrome, chromeEval, gecko } from '../util/browser-stack-matchers'
10
10
  import { ScriptCorrelation } from './script-correlation'
11
+ import { CORRELATION_STALE_THRESHOLD_MS } from './script-tracker-constants'
12
+ import { timingFactory } from './timing-factory'
11
13
 
12
14
  /**
13
15
  * @typedef {import('./register-api-types').RegisterAPITimings} RegisterAPITimings
16
+ * @typedef {import('../../loaders/api/register-api-types').RegisterAPITarget} RegisterAPITarget
14
17
  */
15
18
 
16
19
  /** export for testing purposes */
@@ -22,7 +25,7 @@ try {
22
25
  }
23
26
 
24
27
  /** @type {(entry: PerformanceEntry) => boolean} - A shared function to determine if a performance entry is a valid script or link resource for evaluation */
25
- const validEntryCriteria = entry => entry.initiatorType === 'script' || (['link', 'fetch'].includes(entry.initiatorType) && entry.name.endsWith('.js'))
28
+ const validEntryCriteria = entry => entry.initiatorType === 'script' || (['link', 'fetch'].includes(entry.initiatorType) && cleanURL(entry.name).endsWith('.js'))
26
29
 
27
30
  /** @type {Map<string, ScriptCorrelation>} - Central registry for script correlations containing both DOM and Performance data */
28
31
  export const scriptCorrelations = new Map()
@@ -196,11 +199,33 @@ function applyPerformanceEntry (timings, entry) {
196
199
  timings.type = entry.initiatorType
197
200
  }
198
201
 
202
+ /**
203
+ * Subscribes to late resource timing emissions for a script URL.
204
+ * @param {RegisterAPITimings} timings - The timings object to update
205
+ * @param {string} mfeScriptUrl - The script URL to match
206
+ */
207
+ function subscribeToLatePerformanceEntry (timings, mfeScriptUrl) {
208
+ if (!globalScope.PerformanceObserver?.supportedEntryTypes?.includes('resource')) return
209
+
210
+ poSubscribers.push({
211
+ addedAt: now(),
212
+ test: (entry) => {
213
+ if (entryMatchesUrl(entry, mfeScriptUrl)) {
214
+ applyPerformanceEntry(timings, entry)
215
+ return true
216
+ }
217
+ return false
218
+ }
219
+ })
220
+ }
221
+
199
222
  /**
200
223
  * Uses the stack of the initiator function, returns script timing information if a script can be found with the resource timing API matching the URL found in the stack.
224
+ * @param {RegisterAPITarget} [target] - The MFE target being registered. Its id is used to scope stale-correlation detection per-MFE rather than per-script-URL, so one script registering multiple distinct MFEs doesn't misclassify a later MFE's registration as a stale reuse of an earlier one's.
201
225
  * @returns {RegisterAPITimings} Object containing script fetch start and end times, and the asset URL if found
202
226
  */
203
- export function findScriptTimings () {
227
+ export function findScriptTimings (target) {
228
+ const mfeId = target?.id
204
229
  const timings = { registeredAt: now(), reportedAt: undefined, fetchStart: 0, fetchEnd: 0, scriptStart: 0, scriptEnd: 0, asset: undefined, type: 'unknown' }
205
230
  const stack = getDeepStackTrace()
206
231
  if (!stack) return timings
@@ -223,35 +248,49 @@ export function findScriptTimings () {
223
248
  // Get correlation data
224
249
  timings.correlation = findCorrelation(mfeScriptUrl)
225
250
 
226
- // Use correlation's performance entry if available, otherwise check live performance API
227
- const performanceEntry = timings.correlation?.performance.value || performance.getEntriesByType('resource').find(e => entryMatchesUrl(e, mfeScriptUrl))
251
+ // Use correlation's performance entry if available, otherwise check the live performance API before falling back to the buffered observer.
252
+ const performanceEntry = timings.correlation?.performance.value || globalScope.performance?.getEntriesByType('resource')?.find(e => entryMatchesUrl(e, mfeScriptUrl))
228
253
 
229
254
  if (performanceEntry) {
230
255
  applyPerformanceEntry(timings, performanceEntry)
231
- } else if (wasPreloaded(mfeScriptUrl)) {
232
- // Handle preloaded scripts that may report late
233
- timings.asset = mfeScriptUrl
234
- timings.type = 'preload'
235
-
236
- // Subscribe to late performance observer callbacks
237
- poSubscribers.push({
238
- addedAt: now(),
239
- test: (entry) => {
240
- if (entryMatchesUrl(entry, mfeScriptUrl)) {
241
- applyPerformanceEntry(timings, entry)
242
- return true
243
- }
244
- return false
245
- }
246
- })
256
+ } else {
257
+ const isPreloaded = wasPreloaded(mfeScriptUrl)
258
+
259
+ // Handle preloaded scripts and any late resource emissions through the shared buffered observer.
260
+ if (isPreloaded) {
261
+ timings.asset = mfeScriptUrl
262
+ timings.type = 'preload'
263
+ }
264
+
265
+ subscribeToLatePerformanceEntry(timings, mfeScriptUrl)
266
+ }
267
+
268
+ // A correlation can be reused across multiple `register()` calls for the same script URL (e.g. an SPA
269
+ // remounting the same MFE without the script actually reloading). When that happens, its dom/performance
270
+ // timings still describe the *original* load, not this one. Detect that case so scriptStart/scriptEnd
271
+ // below can ignore the stale data instead of reporting it as if it were fresh.
272
+ const correlation = timings.correlation
273
+ const alreadyClaimedByThisMFE = !!mfeId && !!correlation?.claimedBy.has(mfeId)
274
+ if (correlation && mfeId) correlation.claimedBy.add(mfeId)
275
+ const isCorrelationStale = () => {
276
+ const correlationStart = correlation?.script.start
277
+ if (!alreadyClaimedByThisMFE || !correlationStart) return false
278
+ const staleness = timings.registeredAt - correlationStart
279
+ return staleness > CORRELATION_STALE_THRESHOLD_MS
247
280
  }
248
281
 
249
- /*
250
- * Use getters here because the correlation data may arrive after this function returns the timing object, and we want to provide the most up-to-date timing information possible when the getters are accessed at harvest time.
251
- * The getters will fall back to fetchEnd if correlation data isn't available yet, which is our best approximation for script execution start when actual script timings can not be determined.
252
- */
253
- Object.defineProperty(timings, 'scriptStart', { get: () => timings.correlation?.script.start || timings.fetchEnd })
254
- Object.defineProperty(timings, 'scriptEnd', { get: () => timings.correlation?.script.end || timings.registeredAt })
282
+ // Use getters here because the correlation data may arrive after this function returns the timing object, and we want to provide the most up-to-date timing information possible when the getters are accessed at harvest time.
283
+ // Non-stale: fall back to fetchEnd if correlation data isn't available yet (our best approximation for script execution start). Stale: fall back straight to registeredAt fetchEnd would be derived from the same stale correlation, so it can't be trusted either.
284
+ Object.defineProperty(
285
+ timings,
286
+ 'scriptStart',
287
+ timingFactory(() => isCorrelationStale() ? timings.registeredAt : (correlation?.script.start ?? timings.fetchEnd))
288
+ )
289
+ Object.defineProperty(
290
+ timings,
291
+ 'scriptEnd',
292
+ timingFactory(() => isCorrelationStale() ? timings.registeredAt : (correlation?.script.end ?? timings.registeredAt))
293
+ )
255
294
  } catch (error) {
256
295
  // Don't let stack parsing errors break anything
257
296
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ export function timingFactory (getLazyValue = () => {}, override) {
6
+ return {
7
+ get: () => (override !== undefined ? override : getLazyValue()),
8
+ set: (newValue) => { override = newValue }
9
+ }
10
+ }