@atlaskit/editor-common 110.29.1 → 110.30.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 (53) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/afm-jira/tsconfig.json +1 -1
  3. package/dist/cjs/experiences/Experience.js +83 -33
  4. package/dist/cjs/experiences/ExperienceCheckDomMutation.js +2 -6
  5. package/dist/cjs/experiences/ExperienceCheckTimeout.js +1 -3
  6. package/dist/cjs/experiences/consts.js +18 -4
  7. package/dist/cjs/experiences/experience-state.js +0 -4
  8. package/dist/cjs/experiences/index.js +6 -0
  9. package/dist/cjs/experiences/types.js +1 -0
  10. package/dist/cjs/monitoring/error.js +1 -1
  11. package/dist/cjs/ui/DropList/index.js +1 -1
  12. package/dist/cjs/utils/analytics.js +2 -0
  13. package/dist/es2019/experiences/Experience.js +86 -33
  14. package/dist/es2019/experiences/ExperienceCheckDomMutation.js +2 -6
  15. package/dist/es2019/experiences/ExperienceCheckTimeout.js +1 -3
  16. package/dist/es2019/experiences/consts.js +17 -3
  17. package/dist/es2019/experiences/experience-state.js +0 -4
  18. package/dist/es2019/experiences/index.js +1 -1
  19. package/dist/es2019/experiences/types.js +0 -0
  20. package/dist/es2019/monitoring/error.js +1 -1
  21. package/dist/es2019/ui/DropList/index.js +1 -1
  22. package/dist/es2019/utils/analytics.js +2 -0
  23. package/dist/esm/experiences/Experience.js +84 -34
  24. package/dist/esm/experiences/ExperienceCheckDomMutation.js +2 -6
  25. package/dist/esm/experiences/ExperienceCheckTimeout.js +1 -3
  26. package/dist/esm/experiences/consts.js +17 -3
  27. package/dist/esm/experiences/experience-state.js +0 -4
  28. package/dist/esm/experiences/index.js +1 -1
  29. package/dist/esm/experiences/types.js +0 -0
  30. package/dist/esm/monitoring/error.js +1 -1
  31. package/dist/esm/ui/DropList/index.js +1 -1
  32. package/dist/esm/utils/analytics.js +2 -0
  33. package/dist/types/analytics/types/experience-events.d.ts +27 -4
  34. package/dist/types/analytics/types/mention-events.d.ts +1 -0
  35. package/dist/types/experiences/Experience.d.ts +67 -15
  36. package/dist/types/experiences/ExperienceCheck.d.ts +3 -2
  37. package/dist/types/experiences/ExperienceCheckDomMutation.d.ts +2 -2
  38. package/dist/types/experiences/ExperienceCheckTimeout.d.ts +1 -1
  39. package/dist/types/experiences/consts.d.ts +16 -3
  40. package/dist/types/experiences/experience-state.d.ts +1 -4
  41. package/dist/types/experiences/index.d.ts +1 -1
  42. package/dist/types/experiences/types.d.ts +10 -0
  43. package/dist/types-ts4.5/analytics/types/experience-events.d.ts +27 -4
  44. package/dist/types-ts4.5/analytics/types/mention-events.d.ts +1 -0
  45. package/dist/types-ts4.5/experiences/Experience.d.ts +67 -15
  46. package/dist/types-ts4.5/experiences/ExperienceCheck.d.ts +3 -2
  47. package/dist/types-ts4.5/experiences/ExperienceCheckDomMutation.d.ts +2 -2
  48. package/dist/types-ts4.5/experiences/ExperienceCheckTimeout.d.ts +1 -1
  49. package/dist/types-ts4.5/experiences/consts.d.ts +16 -3
  50. package/dist/types-ts4.5/experiences/experience-state.d.ts +1 -4
  51. package/dist/types-ts4.5/experiences/index.d.ts +1 -1
  52. package/dist/types-ts4.5/experiences/types.d.ts +10 -0
  53. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/editor-common
2
2
 
3
+ ## 110.30.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`42bd907443147`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/42bd907443147) -
8
+ NO-ISSUE Ensures INP metrics and other analytics account for max appearance
9
+
10
+ ### Patch Changes
11
+
12
+ - Updated dependencies
13
+
14
+ ## 110.29.2
15
+
16
+ ### Patch Changes
17
+
18
+ - [`76e788fce7750`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/76e788fce7750) -
19
+ ED-29665 experience tracking updates
20
+ - Updated dependencies
21
+
3
22
  ## 110.29.1
4
23
 
5
24
  ### Patch Changes
@@ -3,7 +3,7 @@
3
3
  "compilerOptions": {
4
4
  "declaration": true,
5
5
  "target": "es5",
6
- "outDir": "../../../../../tsDist/@atlaskit__editor-common/app",
6
+ "outDir": "../../../../../jira/tsDist/@atlaskit__editor-common/app",
7
7
  "rootDir": "../",
8
8
  "composite": true
9
9
  },
@@ -23,14 +23,45 @@ var Experience = exports.Experience = /*#__PURE__*/function () {
23
23
  * Ensures that every tracked start has corresponding abort/fail/success tracked.
24
24
  */
25
25
 
26
+ /**
27
+ * Timestamp (in milliseconds) when the experience transitioned to 'started' state.
28
+ *
29
+ * Used to calculate experience duration. Set via Date.now() when experience starts.
30
+ */
31
+
32
+ /**
33
+ * Metadata provided at experience start time to merge into subsequent events.
34
+ *
35
+ * Used to retain experienceStartMethod and other start-specific metadata.
36
+ */
37
+
38
+ /**
39
+ * Creates a new Experience instance for tracking user experiences.
40
+ *
41
+ * @param id - Unique identifier for the experience
42
+ * @param options - Configuration options for the experience
43
+ * @param options.checks - Experience checks to monitor for completion
44
+ * @param options.dispatchAnalyticsEvent - Function to dispatch analytics events
45
+ * @param options.sampleRate - Sample rate for experienceSampled events
46
+ * @param options.metadata - Global metadata to attach to all events
47
+ * @param options.action - Optional sub identifier for the specific experience action
48
+ */
26
49
  function Experience(id, options) {
27
50
  var _options$sampleRate;
28
51
  (0, _classCallCheck2.default)(this, Experience);
29
52
  (0, _defineProperty2.default)(this, "currentState", 'pending');
53
+ /**
54
+ * Set of experience states that have been seen in the current session.
55
+ *
56
+ * Used to determine if experienceStatusFirstSeen flag should be set in events.
57
+ */
30
58
  (0, _defineProperty2.default)(this, "statesSeen", new Set());
31
59
  this.id = id;
32
60
  this.dispatchAnalyticsEvent = options.dispatchAnalyticsEvent;
33
61
  this.sampleRate = (_options$sampleRate = options.sampleRate) !== null && _options$sampleRate !== void 0 ? _options$sampleRate : _consts.DEFAULT_EXPERIENCE_SAMPLE_RATE;
62
+ this.globalMetadata = _objectSpread(_objectSpread({}, options.metadata), {}, {
63
+ experienceAction: options.action
64
+ });
34
65
  this.check = new _ExperienceCheckComposite.ExperienceCheckComposite(options.checks || []);
35
66
  }
36
67
  return (0, _createClass2.default)(Experience, [{
@@ -40,17 +71,21 @@ var Experience = exports.Experience = /*#__PURE__*/function () {
40
71
  this.stopCheck();
41
72
  this.check.start(function (_ref) {
42
73
  var status = _ref.status,
74
+ reason = _ref.reason,
43
75
  metadata = _ref.metadata;
44
76
  if (status === 'success') {
45
77
  _this.success({
78
+ reason: reason,
46
79
  metadata: metadata
47
80
  });
48
81
  } else if (status === 'abort') {
49
82
  _this.abort({
83
+ reason: reason,
50
84
  metadata: metadata
51
85
  });
52
86
  } else if (status === 'failure') {
53
87
  _this.failure({
88
+ reason: reason,
54
89
  metadata: metadata
55
90
  });
56
91
  }
@@ -62,12 +97,13 @@ var Experience = exports.Experience = /*#__PURE__*/function () {
62
97
  this.check.stop();
63
98
  }
64
99
  }, {
65
- key: "getEndStateConfig",
66
- value: function getEndStateConfig(options) {
67
- var _this$startOptions, _this$startOptions2;
68
- return {
69
- metadata: options !== null && options !== void 0 && options.metadata || (_this$startOptions = this.startOptions) !== null && _this$startOptions !== void 0 && _this$startOptions.metadata ? _objectSpread(_objectSpread({}, (_this$startOptions2 = this.startOptions) === null || _this$startOptions2 === void 0 ? void 0 : _this$startOptions2.metadata), options === null || options === void 0 ? void 0 : options.metadata) : undefined
70
- };
100
+ key: "getEndStateMetadata",
101
+ value: function getEndStateMetadata(options) {
102
+ var metadata = _objectSpread({}, options === null || options === void 0 ? void 0 : options.metadata);
103
+ if (options !== null && options !== void 0 && options.reason) {
104
+ metadata.experienceEndReason = options.reason;
105
+ }
106
+ return metadata;
71
107
  }
72
108
 
73
109
  /**
@@ -87,12 +123,20 @@ var Experience = exports.Experience = /*#__PURE__*/function () {
87
123
  if (!(0, _experienceState.canTransition)(this.currentState, toState)) {
88
124
  return false;
89
125
  }
90
- this.statesSeen.add(toState);
91
126
  this.currentState = toState;
92
127
  if (toState === 'started') {
93
128
  this.isSampledTrackingEnabled = Math.random() < this.sampleRate;
129
+ this.startTimeMs = Date.now();
130
+ this.startMetadata = metadata;
94
131
  }
95
132
  this.trackTransition(toState, metadata);
133
+ if (toState === 'started') {
134
+ this.startCheck();
135
+ } else {
136
+ this.stopCheck();
137
+ this.startMetadata = undefined;
138
+ }
139
+ this.statesSeen.add(toState);
96
140
  return true;
97
141
  }
98
142
 
@@ -107,11 +151,13 @@ var Experience = exports.Experience = /*#__PURE__*/function () {
107
151
  }, {
108
152
  key: "trackTransition",
109
153
  value: function trackTransition(toState, metadata) {
110
- var attributes = _objectSpread({
154
+ var attributes = _objectSpread(_objectSpread(_objectSpread({
111
155
  experienceKey: this.id,
112
156
  experienceStatus: toState,
113
- firstInSession: !this.statesSeen.has(toState)
114
- }, metadata);
157
+ experienceStatusFirstSeen: !this.statesSeen.has(toState),
158
+ experienceStartTime: this.startTimeMs || 0,
159
+ experienceDuration: this.startTimeMs ? Date.now() - this.startTimeMs : 0
160
+ }, this.globalMetadata), this.startMetadata), metadata);
115
161
  var experienceMeasuredEvent = {
116
162
  action: _analytics.ACTION.EXPERIENCE_MEASURED,
117
163
  actionSubject: _analytics.ACTION_SUBJECT.EDITOR,
@@ -138,31 +184,36 @@ var Experience = exports.Experience = /*#__PURE__*/function () {
138
184
  * Metadata from options will be merged with metadata provided in subsequent events.
139
185
  *
140
186
  * @param options - Configuration for starting the experience
141
- * @param options.metadata - Optional metadata attached to all subsequent events for this started experience
187
+ * @param options.metadata - Optional custom metadata attached to all subsequent events for this started experience
188
+ * @param options.forceRestart - If true and experience already in progress will abort and restart
189
+ * @param options.method - Optional method for experience start, e.g., how the experience was initiated
142
190
  */
143
191
  }, {
144
192
  key: "start",
145
193
  value: function start(options) {
146
- this.startOptions = options;
147
- if (this.transitionTo('started', options === null || options === void 0 ? void 0 : options.metadata)) {
148
- this.startCheck();
194
+ if (options.forceRestart && this.currentState === 'started') {
195
+ this.abort({
196
+ reason: _consts.EXPERIENCE_ABORT_REASON.RESTARTED
197
+ });
149
198
  }
199
+ return this.transitionTo('started', _objectSpread({
200
+ experienceStartMethod: options.method
201
+ }, options.metadata));
150
202
  }
151
203
 
152
204
  /**
153
205
  * Marks the experience as successful and stops any ongoing checks.
154
206
  *
155
207
  * @param options - Configuration for the success event
156
- * @param options.metadata - Optional metadata attached to the success event
208
+ * @param options.metadata - Optional custom metadata attached to the success event
209
+ * @param options.reason - Optional reason for success
210
+ * @returns false if transition to success state was not valid
157
211
  */
158
212
  }, {
159
213
  key: "success",
160
214
  value: function success(options) {
161
- var mergedConfig = this.getEndStateConfig(options);
162
- if (this.transitionTo('succeeded', mergedConfig.metadata)) {
163
- this.stopCheck();
164
- this.startOptions = undefined;
165
- }
215
+ var metadata = this.getEndStateMetadata(options);
216
+ return this.transitionTo('succeeded', metadata);
166
217
  }
167
218
 
168
219
  /**
@@ -172,22 +223,22 @@ var Experience = exports.Experience = /*#__PURE__*/function () {
172
223
  * (e.g., component unmount, navigation). This is neither success nor failure.
173
224
  *
174
225
  * @param options - Configuration for the abort event
175
- * @param options.metadata - Optional metadata attached to the abort event
226
+ * @param options.metadata - Optional custom metadata attached to the abort event
227
+ * @param options.reason - Optional reason for abort
176
228
  *
177
229
  * @example
178
230
  * // Abort on component unmount
179
231
  * useEffect(() => {
180
- * return () => experience.abort({ metadata: { reason: 'unmount' } });
232
+ * return () => experience.abort({ reason: 'unmount', metadata: { someKey: 'someValue' } });
181
233
  * }, []);
234
+ *
235
+ * @returns false if transition to aborted state was not valid
182
236
  */
183
237
  }, {
184
238
  key: "abort",
185
239
  value: function abort(options) {
186
- var mergedConfig = this.getEndStateConfig(options);
187
- if (this.transitionTo('aborted', mergedConfig.metadata)) {
188
- this.stopCheck();
189
- this.startOptions = undefined;
190
- }
240
+ var metadata = this.getEndStateMetadata(options);
241
+ return this.transitionTo('aborted', metadata);
191
242
  }
192
243
 
193
244
  /**
@@ -196,16 +247,15 @@ var Experience = exports.Experience = /*#__PURE__*/function () {
196
247
  * Use this for actual failures in the experience flow (e.g., timeout, error conditions).
197
248
  *
198
249
  * @param options - Configuration for the failure event
199
- * @param options.metadata - Optional metadata attached to the failure event
250
+ * @param options.metadata - Optional custom metadata attached to the failure event
251
+ * @param options.reason - Optional reason for failure
252
+ * @returns false if transition to failed state was not valid
200
253
  */
201
254
  }, {
202
255
  key: "failure",
203
256
  value: function failure(options) {
204
- var mergedConfig = this.getEndStateConfig(options);
205
- if (this.transitionTo('failed', mergedConfig.metadata)) {
206
- this.stopCheck();
207
- this.startOptions = undefined;
208
- }
257
+ var metadata = this.getEndStateMetadata(options);
258
+ return this.transitionTo('failed', metadata);
209
259
  }
210
260
  }]);
211
261
  }();
@@ -33,9 +33,7 @@ var ExperienceCheckDomMutation = exports.ExperienceCheckDomMutation = /*#__PURE_
33
33
  if (!(config !== null && config !== void 0 && config.target)) {
34
34
  callback({
35
35
  status: 'failure',
36
- metadata: {
37
- reason: _consts.EXPERIENCE_FAILURE_REASON.DOM_MUTATION_TARGET_NOT_FOUND
38
- }
36
+ reason: _consts.EXPERIENCE_FAILURE_REASON.DOM_MUTATION_TARGET_NOT_FOUND
39
37
  });
40
38
  return;
41
39
  }
@@ -50,9 +48,7 @@ var ExperienceCheckDomMutation = exports.ExperienceCheckDomMutation = /*#__PURE_
50
48
  } catch (error) {
51
49
  callback({
52
50
  status: 'failure',
53
- metadata: {
54
- reason: _consts.EXPERIENCE_FAILURE_REASON.DOM_MUTATION_CHECK_ERROR
55
- }
51
+ reason: _consts.EXPERIENCE_FAILURE_REASON.DOM_MUTATION_CHECK_ERROR
56
52
  });
57
53
  }
58
54
  });
@@ -11,9 +11,7 @@ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/de
11
11
  var _consts = require("./consts");
12
12
  var DEFAULT_FAILURE_RESULT = {
13
13
  status: 'failure',
14
- metadata: {
15
- reason: _consts.EXPERIENCE_FAILURE_REASON.TIMEOUT
16
- }
14
+ reason: _consts.EXPERIENCE_FAILURE_REASON.TIMEOUT
17
15
  };
18
16
 
19
17
  /**
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.EXPERIENCE_FAILURE_REASON = exports.DEFAULT_EXPERIENCE_SAMPLE_RATE = void 0;
6
+ exports.EXPERIENCE_FAILURE_REASON = exports.EXPERIENCE_ABORT_REASON = exports.DEFAULT_EXPERIENCE_SAMPLE_RATE = void 0;
7
7
  /**
8
8
  * Built-in failure reasons for experience checks
9
9
  *
@@ -25,13 +25,27 @@ var EXPERIENCE_FAILURE_REASON = exports.EXPERIENCE_FAILURE_REASON = {
25
25
  DOM_MUTATION_CHECK_ERROR: 'dom-mutation-check-error'
26
26
  };
27
27
 
28
+ /**
29
+ * Built-in abort reasons for experiences.
30
+ *
31
+ * These may be used by various ExperienceCheck implementations to
32
+ * provide consistent, well-known abort reasons for analytics and debugging.
33
+ */
34
+ var EXPERIENCE_ABORT_REASON = exports.EXPERIENCE_ABORT_REASON = {
35
+ /**
36
+ * Experience was aborted because it was restarted while already in progress
37
+ */
38
+ RESTARTED: 'restarted'
39
+ };
40
+
28
41
  /**
29
42
  * Default sample rate for experienceSampled events.
30
43
  * Set to 1 in 1000 (0.001) to balance data collection with event volume.
31
44
  *
32
45
  * Newly defined experiences should use this default unless they have data
33
- * to justify a different rate. The expectation is that measurements will be
34
- * gathered after initial instrumentation, then the sample rate can be tuned
35
- * up to a safe threshold.
46
+ * to justify a different rate.
47
+ *
48
+ * The expectation is that measurements will be gathered after initial
49
+ * instrumentation, then the sample rate can be tuned up to a safe threshold.
36
50
  */
37
51
  var DEFAULT_EXPERIENCE_SAMPLE_RATE = exports.DEFAULT_EXPERIENCE_SAMPLE_RATE = 0.001;
@@ -4,10 +4,6 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.canTransition = exports.EXPERIENCE_STATE_TRANSITIONS = void 0;
7
- /**
8
- * Represents the state of an experience throughout its lifecycle.
9
- */
10
-
11
7
  /**
12
8
  * State transition map defining valid state transitions.
13
9
  */
@@ -3,6 +3,12 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ Object.defineProperty(exports, "EXPERIENCE_ABORT_REASON", {
7
+ enumerable: true,
8
+ get: function get() {
9
+ return _consts.EXPERIENCE_ABORT_REASON;
10
+ }
11
+ });
6
12
  Object.defineProperty(exports, "EXPERIENCE_FAILURE_REASON", {
7
13
  enumerable: true,
8
14
  get: function get() {
@@ -0,0 +1 @@
1
+ "use strict";
@@ -16,7 +16,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
16
16
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
17
17
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
18
18
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
19
- var packageVersion = "110.29.0";
19
+ var packageVersion = "0.0.0-development";
20
20
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
21
21
  // Remove URL as it has UGC
22
22
  // Ignored via go/ees007
@@ -24,7 +24,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
24
24
  * @jsx jsx
25
25
  */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
26
26
  var packageName = "@atlaskit/editor-common";
27
- var packageVersion = "110.29.0";
27
+ var packageVersion = "0.0.0-development";
28
28
  var halfFocusRing = 1;
29
29
  var dropOffset = '0, 8';
30
30
  var fadeIn = (0, _react2.keyframes)({
@@ -22,6 +22,8 @@ var getAnalyticsAppearance = exports.getAnalyticsAppearance = function getAnalyt
22
22
  return _FabricEditorAnalyticsContext.EDITOR_APPEARANCE_CONTEXT.CHROMELESS;
23
23
  case 'mobile':
24
24
  return _FabricEditorAnalyticsContext.EDITOR_APPEARANCE_CONTEXT.MOBILE;
25
+ case 'max':
26
+ return _FabricEditorAnalyticsContext.EDITOR_APPEARANCE_CONTEXT.MAX;
25
27
  }
26
28
  };
27
29
  var getAnalyticsEditorAppearance = exports.getAnalyticsEditorAppearance = function getAnalyticsEditorAppearance(editorAppearance) {
@@ -1,6 +1,6 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '../analytics';
3
- import { DEFAULT_EXPERIENCE_SAMPLE_RATE } from './consts';
3
+ import { DEFAULT_EXPERIENCE_SAMPLE_RATE, EXPERIENCE_ABORT_REASON } from './consts';
4
4
  import { canTransition } from './experience-state';
5
5
  import { ExperienceCheckComposite } from './ExperienceCheckComposite';
6
6
  export class Experience {
@@ -12,31 +12,67 @@ export class Experience {
12
12
  * Ensures that every tracked start has corresponding abort/fail/success tracked.
13
13
  */
14
14
 
15
+ /**
16
+ * Timestamp (in milliseconds) when the experience transitioned to 'started' state.
17
+ *
18
+ * Used to calculate experience duration. Set via Date.now() when experience starts.
19
+ */
20
+
21
+ /**
22
+ * Metadata provided at experience start time to merge into subsequent events.
23
+ *
24
+ * Used to retain experienceStartMethod and other start-specific metadata.
25
+ */
26
+
27
+ /**
28
+ * Creates a new Experience instance for tracking user experiences.
29
+ *
30
+ * @param id - Unique identifier for the experience
31
+ * @param options - Configuration options for the experience
32
+ * @param options.checks - Experience checks to monitor for completion
33
+ * @param options.dispatchAnalyticsEvent - Function to dispatch analytics events
34
+ * @param options.sampleRate - Sample rate for experienceSampled events
35
+ * @param options.metadata - Global metadata to attach to all events
36
+ * @param options.action - Optional sub identifier for the specific experience action
37
+ */
15
38
  constructor(id, options) {
16
39
  var _options$sampleRate;
17
40
  _defineProperty(this, "currentState", 'pending');
41
+ /**
42
+ * Set of experience states that have been seen in the current session.
43
+ *
44
+ * Used to determine if experienceStatusFirstSeen flag should be set in events.
45
+ */
18
46
  _defineProperty(this, "statesSeen", new Set());
19
47
  this.id = id;
20
48
  this.dispatchAnalyticsEvent = options.dispatchAnalyticsEvent;
21
49
  this.sampleRate = (_options$sampleRate = options.sampleRate) !== null && _options$sampleRate !== void 0 ? _options$sampleRate : DEFAULT_EXPERIENCE_SAMPLE_RATE;
50
+ this.globalMetadata = {
51
+ ...options.metadata,
52
+ experienceAction: options.action
53
+ };
22
54
  this.check = new ExperienceCheckComposite(options.checks || []);
23
55
  }
24
56
  startCheck() {
25
57
  this.stopCheck();
26
58
  this.check.start(({
27
59
  status,
60
+ reason,
28
61
  metadata
29
62
  }) => {
30
63
  if (status === 'success') {
31
64
  this.success({
65
+ reason,
32
66
  metadata
33
67
  });
34
68
  } else if (status === 'abort') {
35
69
  this.abort({
70
+ reason,
36
71
  metadata
37
72
  });
38
73
  } else if (status === 'failure') {
39
74
  this.failure({
75
+ reason,
40
76
  metadata
41
77
  });
42
78
  }
@@ -45,14 +81,14 @@ export class Experience {
45
81
  stopCheck() {
46
82
  this.check.stop();
47
83
  }
48
- getEndStateConfig(options) {
49
- var _this$startOptions, _this$startOptions2;
50
- return {
51
- metadata: options !== null && options !== void 0 && options.metadata || (_this$startOptions = this.startOptions) !== null && _this$startOptions !== void 0 && _this$startOptions.metadata ? {
52
- ...((_this$startOptions2 = this.startOptions) === null || _this$startOptions2 === void 0 ? void 0 : _this$startOptions2.metadata),
53
- ...(options === null || options === void 0 ? void 0 : options.metadata)
54
- } : undefined
84
+ getEndStateMetadata(options) {
85
+ const metadata = {
86
+ ...(options === null || options === void 0 ? void 0 : options.metadata)
55
87
  };
88
+ if (options !== null && options !== void 0 && options.reason) {
89
+ metadata.experienceEndReason = options.reason;
90
+ }
91
+ return metadata;
56
92
  }
57
93
 
58
94
  /**
@@ -70,12 +106,20 @@ export class Experience {
70
106
  if (!canTransition(this.currentState, toState)) {
71
107
  return false;
72
108
  }
73
- this.statesSeen.add(toState);
74
109
  this.currentState = toState;
75
110
  if (toState === 'started') {
76
111
  this.isSampledTrackingEnabled = Math.random() < this.sampleRate;
112
+ this.startTimeMs = Date.now();
113
+ this.startMetadata = metadata;
77
114
  }
78
115
  this.trackTransition(toState, metadata);
116
+ if (toState === 'started') {
117
+ this.startCheck();
118
+ } else {
119
+ this.stopCheck();
120
+ this.startMetadata = undefined;
121
+ }
122
+ this.statesSeen.add(toState);
79
123
  return true;
80
124
  }
81
125
 
@@ -91,7 +135,11 @@ export class Experience {
91
135
  const attributes = {
92
136
  experienceKey: this.id,
93
137
  experienceStatus: toState,
94
- firstInSession: !this.statesSeen.has(toState),
138
+ experienceStatusFirstSeen: !this.statesSeen.has(toState),
139
+ experienceStartTime: this.startTimeMs || 0,
140
+ experienceDuration: this.startTimeMs ? Date.now() - this.startTimeMs : 0,
141
+ ...this.globalMetadata,
142
+ ...this.startMetadata,
95
143
  ...metadata
96
144
  };
97
145
  const experienceMeasuredEvent = {
@@ -120,27 +168,33 @@ export class Experience {
120
168
  * Metadata from options will be merged with metadata provided in subsequent events.
121
169
  *
122
170
  * @param options - Configuration for starting the experience
123
- * @param options.metadata - Optional metadata attached to all subsequent events for this started experience
171
+ * @param options.metadata - Optional custom metadata attached to all subsequent events for this started experience
172
+ * @param options.forceRestart - If true and experience already in progress will abort and restart
173
+ * @param options.method - Optional method for experience start, e.g., how the experience was initiated
124
174
  */
125
175
  start(options) {
126
- this.startOptions = options;
127
- if (this.transitionTo('started', options === null || options === void 0 ? void 0 : options.metadata)) {
128
- this.startCheck();
176
+ if (options.forceRestart && this.currentState === 'started') {
177
+ this.abort({
178
+ reason: EXPERIENCE_ABORT_REASON.RESTARTED
179
+ });
129
180
  }
181
+ return this.transitionTo('started', {
182
+ experienceStartMethod: options.method,
183
+ ...options.metadata
184
+ });
130
185
  }
131
186
 
132
187
  /**
133
188
  * Marks the experience as successful and stops any ongoing checks.
134
189
  *
135
190
  * @param options - Configuration for the success event
136
- * @param options.metadata - Optional metadata attached to the success event
191
+ * @param options.metadata - Optional custom metadata attached to the success event
192
+ * @param options.reason - Optional reason for success
193
+ * @returns false if transition to success state was not valid
137
194
  */
138
195
  success(options) {
139
- const mergedConfig = this.getEndStateConfig(options);
140
- if (this.transitionTo('succeeded', mergedConfig.metadata)) {
141
- this.stopCheck();
142
- this.startOptions = undefined;
143
- }
196
+ const metadata = this.getEndStateMetadata(options);
197
+ return this.transitionTo('succeeded', metadata);
144
198
  }
145
199
 
146
200
  /**
@@ -150,20 +204,20 @@ export class Experience {
150
204
  * (e.g., component unmount, navigation). This is neither success nor failure.
151
205
  *
152
206
  * @param options - Configuration for the abort event
153
- * @param options.metadata - Optional metadata attached to the abort event
207
+ * @param options.metadata - Optional custom metadata attached to the abort event
208
+ * @param options.reason - Optional reason for abort
154
209
  *
155
210
  * @example
156
211
  * // Abort on component unmount
157
212
  * useEffect(() => {
158
- * return () => experience.abort({ metadata: { reason: 'unmount' } });
213
+ * return () => experience.abort({ reason: 'unmount', metadata: { someKey: 'someValue' } });
159
214
  * }, []);
215
+ *
216
+ * @returns false if transition to aborted state was not valid
160
217
  */
161
218
  abort(options) {
162
- const mergedConfig = this.getEndStateConfig(options);
163
- if (this.transitionTo('aborted', mergedConfig.metadata)) {
164
- this.stopCheck();
165
- this.startOptions = undefined;
166
- }
219
+ const metadata = this.getEndStateMetadata(options);
220
+ return this.transitionTo('aborted', metadata);
167
221
  }
168
222
 
169
223
  /**
@@ -172,13 +226,12 @@ export class Experience {
172
226
  * Use this for actual failures in the experience flow (e.g., timeout, error conditions).
173
227
  *
174
228
  * @param options - Configuration for the failure event
175
- * @param options.metadata - Optional metadata attached to the failure event
229
+ * @param options.metadata - Optional custom metadata attached to the failure event
230
+ * @param options.reason - Optional reason for failure
231
+ * @returns false if transition to failed state was not valid
176
232
  */
177
233
  failure(options) {
178
- const mergedConfig = this.getEndStateConfig(options);
179
- if (this.transitionTo('failed', mergedConfig.metadata)) {
180
- this.stopCheck();
181
- this.startOptions = undefined;
182
- }
234
+ const metadata = this.getEndStateMetadata(options);
235
+ return this.transitionTo('failed', metadata);
183
236
  }
184
237
  }
@@ -21,9 +21,7 @@ export class ExperienceCheckDomMutation {
21
21
  if (!(config !== null && config !== void 0 && config.target)) {
22
22
  callback({
23
23
  status: 'failure',
24
- metadata: {
25
- reason: EXPERIENCE_FAILURE_REASON.DOM_MUTATION_TARGET_NOT_FOUND
26
- }
24
+ reason: EXPERIENCE_FAILURE_REASON.DOM_MUTATION_TARGET_NOT_FOUND
27
25
  });
28
26
  return;
29
27
  }
@@ -38,9 +36,7 @@ export class ExperienceCheckDomMutation {
38
36
  } catch (error) {
39
37
  callback({
40
38
  status: 'failure',
41
- metadata: {
42
- reason: EXPERIENCE_FAILURE_REASON.DOM_MUTATION_CHECK_ERROR
43
- }
39
+ reason: EXPERIENCE_FAILURE_REASON.DOM_MUTATION_CHECK_ERROR
44
40
  });
45
41
  }
46
42
  });
@@ -2,9 +2,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import { EXPERIENCE_FAILURE_REASON } from './consts';
3
3
  const DEFAULT_FAILURE_RESULT = {
4
4
  status: 'failure',
5
- metadata: {
6
- reason: EXPERIENCE_FAILURE_REASON.TIMEOUT
7
- }
5
+ reason: EXPERIENCE_FAILURE_REASON.TIMEOUT
8
6
  };
9
7
 
10
8
  /**