@atlaskit/react-ufo 5.1.2 → 5.1.4

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 (30) hide show
  1. package/AGENTS.md +426 -0
  2. package/CHANGELOG.md +16 -0
  3. package/dist/cjs/interaction-metrics/index.js +11 -3
  4. package/dist/cjs/set-terminal-error/index.js +11 -3
  5. package/dist/cjs/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +39 -27
  6. package/dist/cjs/vc/vc-observer-new/metric-calculator/fy25_03/index.js +1 -3
  7. package/dist/cjs/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +22 -7
  8. package/dist/es2019/interaction-metrics/index.js +10 -2
  9. package/dist/es2019/set-terminal-error/index.js +12 -4
  10. package/dist/es2019/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +17 -4
  11. package/dist/es2019/vc/vc-observer-new/metric-calculator/fy25_03/index.js +1 -3
  12. package/dist/es2019/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +22 -7
  13. package/dist/esm/interaction-metrics/index.js +10 -2
  14. package/dist/esm/set-terminal-error/index.js +12 -4
  15. package/dist/esm/vc/vc-observer-new/metric-calculator/abstract-base-vc-calculator.js +39 -27
  16. package/dist/esm/vc/vc-observer-new/metric-calculator/fy25_03/index.js +1 -3
  17. package/dist/esm/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.js +22 -7
  18. package/dist/types/common/vc/types.d.ts +2 -0
  19. package/dist/types/create-terminal-error-payload/index.d.ts +4 -0
  20. package/dist/types/interaction-metrics/index.d.ts +8 -0
  21. package/dist/types/set-terminal-error/index.d.ts +4 -0
  22. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +3 -3
  23. package/dist/types/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +12 -0
  24. package/dist/types-ts4.5/common/vc/types.d.ts +2 -0
  25. package/dist/types-ts4.5/create-terminal-error-payload/index.d.ts +4 -0
  26. package/dist/types-ts4.5/interaction-metrics/index.d.ts +8 -0
  27. package/dist/types-ts4.5/set-terminal-error/index.d.ts +4 -0
  28. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/canvas-heatmap/index.d.ts +3 -3
  29. package/dist/types-ts4.5/vc/vc-observer-new/metric-calculator/percentile-calc/types.d.ts +12 -0
  30. package/package.json +4 -4
package/AGENTS.md ADDED
@@ -0,0 +1,426 @@
1
+ # AGENTS.md - React UFO (@atlaskit/react-ufo)
2
+
3
+ ## Overview
4
+
5
+ React UFO (User-Facing Observability) is a performance instrumentation library that captures key user experience metrics across Atlassian products. It produces structured performance events that are consumed by downstream analytics systems, primarily the **Glance (Performance Portal)** system.
6
+
7
+ ## Team & Product Context
8
+
9
+ - **Team**: Frontend Observability (FeObs) — provides end-to-end solutions for frontend observability (performance + reliability)
10
+ - **Product**: Glance (formerly Performance Portal) — the one-stop shop for frontend performance data at Atlassian
11
+ - **Mission**: Make performance data available, accountable, and actionable
12
+ - **Support channel**: `#help-devinfra-fe-observability` (Slack)
13
+ - **Jira project**: AFO on `product-fabric.atlassian.net`
14
+ - **Shortlinks**: `go/glance`, `go/feo`
15
+ - **Package**: `@atlaskit/react-ufo`
16
+
17
+ ## Architecture
18
+
19
+ ### Local Package Architecture
20
+
21
+ ```mermaid
22
+ graph TD
23
+ subgraph "React Application"
24
+ A[traceUFOPageLoad / traceUFOTransition / traceUFOPress] --> B[InteractionMetrics]
25
+ C[UFOSegment] --> B
26
+ D[UFOLoadHold] --> B
27
+ E[UFOLabel] --> B
28
+ F[Custom Data/Timings] --> B
29
+ end
30
+
31
+ subgraph "React UFO Core"
32
+ B --> G[VCObserver - Visual Completion]
33
+ B --> H[HoldTracking]
34
+ B --> I[ReactProfiler]
35
+ G --> J[createPayload]
36
+ H --> J
37
+ I --> J
38
+ end
39
+
40
+ subgraph "Output"
41
+ J --> K[GASv3 Analytics Events]
42
+ K --> L[Performance Portal / Databricks]
43
+ end
44
+ ```
45
+
46
+ ### End-to-End Data Pipeline
47
+
48
+ React UFO is the **client-side instrumentation** in a larger data pipeline:
49
+
50
+ ```mermaid
51
+ graph LR
52
+ subgraph "Client Instrumentation"
53
+ A[React UFO in Products]
54
+ end
55
+
56
+ subgraph "Transport Layer"
57
+ B[GASv3 analytics-service]
58
+ C[StreamHub / Kinesis]
59
+ end
60
+
61
+ subgraph "Event Processing - perf-portal-monorepo"
62
+ D[UFO Service - splits events]
63
+ E[BM Data Pipeline]
64
+ end
65
+
66
+ subgraph "Data Storage"
67
+ F[Socrates - data lake]
68
+ G[Databricks - aggregation]
69
+ end
70
+
71
+ subgraph "Data Serving"
72
+ H[Perf Portal GQL]
73
+ I[Glance UI]
74
+ end
75
+
76
+ A --> B --> C --> D --> E --> F --> G --> H --> I
77
+ ```
78
+
79
+ **Key Pipeline Details:**
80
+ - **UFO Service** splits root interactions from page segments (e.g., `jira.fe.page-load.issue-view` generates multiple `jira.fe.page-segment-load.*` events)
81
+ - **Databricks** has streaming (high-resolution) and daily aggregation jobs
82
+ - **External dependencies**: Socrates (data lake), Observo (high-cardinality data), SignalFx (metrics/alerting)
83
+
84
+ ## Key Concepts
85
+
86
+ ### Interaction Types
87
+ - **`page_load`**: Initial page rendering measurement
88
+ - **`transition`**: SPA navigation between routes
89
+ - **`press`**: User click/tap interactions
90
+ - **`hover`**: Mouse hover interactions
91
+ - **`typing`**: Keyboard input performance
92
+
93
+ ### Core Metrics
94
+ - **TTI (Time to Interactive)**: Legacy metric from BM3 (Browser Metrics 3)
95
+ - **TTAI (Time to All Interactive)**: When all holds are released
96
+ - **TTVC (Time to Visual Completion)**: When visual changes stabilize at various thresholds (VC50, VC80, VC90, VC99)
97
+ - **FMP (First Meaningful Paint)**: When primary content is visible
98
+ - **Speed Index**: Weighted average of visual progress over time
99
+
100
+ ### TTVC Revisions
101
+ The package supports multiple TTVC calculation algorithms:
102
+ - `fy25.01`, `fy25.02`: Legacy revisions
103
+ - `fy25.03`: Current default revision (`DEFAULT_TTVC_REVISION`)
104
+ - `fy26.04`, `next`: Experimental revisions
105
+
106
+ ## Directory Structure
107
+
108
+ ```
109
+ src/
110
+ ├── trace-pageload/ # Page load interaction initialization
111
+ ├── trace-transition/ # SPA transition tracking
112
+ ├── trace-press/ # Press interaction tracking
113
+ ├── trace-interaction/ # Generic interaction tracing
114
+ ├── trace-redirect/ # Route redirect tracking
115
+ ├── trace-hover/ # Hover interaction tracking
116
+ ├── interaction-metrics/ # Core interaction state management
117
+ ├── create-payload/ # Analytics payload generation
118
+ ├── vc/ # Visual Completion observer system
119
+ │ ├── vc-observer/ # Legacy VC observer (fy25.01, fy25.02)
120
+ │ └── vc-observer-new/ # New VC observer (fy25.03+)
121
+ ├── segment/ # UFOSegment component
122
+ ├── load-hold/ # UFOLoadHold component
123
+ ├── label/ # UFOLabel component
124
+ ├── config/ # Configuration management
125
+ ├── custom-data/ # Custom data injection
126
+ ├── custom-cohort-data/ # Cohort data for A/B testing
127
+ ├── custom-timings/ # Custom timing tracking
128
+ ├── custom-spans/ # Custom span tracking
129
+ ├── feature-flags-accessed/ # Feature flag correlation
130
+ ├── ssr/ # SSR timing integration
131
+ ├── suspense/ # Instrumented React Suspense
132
+ ├── placeholder/ # Lazy loading placeholder integration
133
+ ├── report-error/ # Error reporting utilities
134
+ ├── set-interaction-error/ # Interaction error handling
135
+ ├── set-terminal-error/ # Terminal error handling
136
+ └── common/ # Shared types and utilities
137
+ ```
138
+
139
+ ## Key Entry Points
140
+
141
+ ### Interaction Initialization
142
+ - [`traceUFOPageLoad`](./src/trace-pageload/index.ts) - Start page load measurement
143
+ - [`traceUFOTransition`](./src/trace-transition/index.ts) - Start transition measurement
144
+ - [`traceUFOPress`](./src/trace-press/index.ts) - Start press interaction measurement
145
+ - [`traceUFOInteraction`](./src/trace-interaction/index.ts) - Generic interaction from browser events
146
+ - [`traceUFORedirect`](./src/trace-redirect/index.ts) - Track route redirects
147
+
148
+ ### React Components
149
+ - [`UFOSegment`](./src/segment/segment.tsx) - Define measurable page sections
150
+ - [`UFOLoadHold`](./src/load-hold/UFOLoadHold.tsx) - Hold interaction completion during loading
151
+ - [`UFOLabel`](./src/label/UFOLabel.tsx) - Annotate component tree for debugging
152
+ - [`Suspense`](./src/suspense/Suspense.tsx) - Instrumented React Suspense boundary
153
+ - [`UFOCustomData`](./src/custom-data/index.ts) - Add custom data to interactions
154
+ - [`UFOCustomCohortData`](./src/custom-cohort-data/index.ts) - Add cohort data for experiments
155
+
156
+ ### Configuration
157
+ - [`setUFOConfig`](./src/config/index.ts) - Configure UFO behavior, sampling rates, and features
158
+
159
+ ### Payload Generation
160
+ - [`createPayloads`](./src/create-payload/index.ts) - Generate analytics payloads from interaction data
161
+
162
+ ## Payload Schema
163
+
164
+ Events are sent with `experience:key` of `custom.interaction-metrics` and follow the schema defined in [`react-ufo-payload-schema.ts`](./src/common/react-ufo-payload-schema.ts).
165
+
166
+ Key payload fields:
167
+ - `experience:name` - The UFO interaction name (e.g., `issue-view`)
168
+ - `interactionMetrics.type` - Interaction type (page_load, transition, press, etc.)
169
+ - `interactionMetrics.start/end` - Timing boundaries
170
+ - `interactionMetrics.segments` - Measured page sections
171
+ - `interactionMetrics.holdInfo` - Loading hold timings
172
+ - `interactionMetrics.errors` - Captured errors during interaction
173
+ - `ufo:vc:rev` - Visual completion revision data
174
+ - `ufo:vc:ratios` - VC percentage at various thresholds
175
+ - `metric:ttai` - Time to All Interactive
176
+ - `metric:fp`, `metric:fcp`, `metric:lcp` - Paint metrics
177
+
178
+ ## Downstream Consumer: Glance (Performance Portal)
179
+
180
+ The events produced by this package are consumed by the **Glance** system (https://bitbucket.org/atlassian/perf-portal-monorepo):
181
+
182
+ ### perf-portal-monorepo Apps
183
+
184
+ | App | Purpose | Role in Pipeline |
185
+ |-----|---------|------------------|
186
+ | `apps/ufo` | UFO event processing service | Event Ingestion & Splitting |
187
+ | `apps/bm-data-pipeline` | Browser Metrics data pipeline | Event Processing (BM3) |
188
+ | `apps/databricks-jobs` | Data aggregation notebooks | Data Aggregation |
189
+ | `apps/perf-portal-gql` | GraphQL API server | Data Serving + Alerts |
190
+ | `apps/performance-portal` | React frontend (Glance UI) | Data Visualization |
191
+
192
+ ### Event Flow
193
+
194
+ ```mermaid
195
+ sequenceDiagram
196
+ participant App as Atlassian Product
197
+ participant UFO as React UFO
198
+ participant GAS as GASv3 Analytics
199
+ participant Stream as StreamHub/Kinesis
200
+ participant UFOSvc as UFO Service
201
+ participant DB as Databricks
202
+ participant GQL as Perf Portal GQL
203
+ participant UI as Glance UI
204
+
205
+ App->>UFO: traceUFOPageLoad('issue-view')
206
+ UFO->>UFO: Measure TTAI, TTVC
207
+ UFO->>GAS: Send interaction-metrics event
208
+ GAS->>Stream: Forward to Kinesis
209
+ Stream->>UFOSvc: Process event
210
+ UFOSvc->>UFOSvc: Split root + segments
211
+ UFOSvc->>DB: Streaming aggregation
212
+ DB->>DB: Daily aggregation
213
+ DB->>GQL: Query aggregated data
214
+ GQL->>UI: GraphQL response
215
+ UI->>UI: Display dashboards
216
+ ```
217
+
218
+ ### What Glance Provides
219
+
220
+ 1. **Topline Metrics**: FMP, TTVC, TTAI dashboards
221
+ 2. **Experience Breakdown**: Per-route performance analysis
222
+ 3. **Segment Analysis**: Component-level timing breakdown
223
+ 4. **Regression Detection**: Automated performance alerts
224
+ 5. **P-BREACH Tickets**: Automatic Jira tickets for regressions
225
+ 6. **AI Investigation**: LLM-powered regression analysis
226
+
227
+ ## Feature Flags
228
+
229
+ The package uses platform feature flags for gradual rollouts. Key flags are defined in [`package.json`](./package.json):
230
+
231
+ | Flag | Purpose |
232
+ |------|---------|
233
+ | `platform_ufo_enable_ttai_with_3p` | Third-party segment timing |
234
+ | `platform_ufo_enable_vc_raw_data` | Raw VC data inclusion |
235
+ | `platform_ufo_segment_critical_metrics` | Segment-level metrics |
236
+ | `platform_ufo_enable_terminal_errors` | Terminal error tracking |
237
+ | `platform_ufo_raw_data_thirdparty` | Third-party raw data behavior |
238
+ | `platform_ufo_enable_finish_interaction_transition` | Finish interaction on transition |
239
+
240
+ ## Testing
241
+
242
+ ### Unit Tests
243
+ ```bash
244
+ cd platform && afm test unit packages/react-ufo/atlaskit/src/<test-file>
245
+ # Or run all:
246
+ cd platform && yarn test packages/react-ufo
247
+ ```
248
+
249
+ ### Integration Tests
250
+ ```bash
251
+ # Terminal 1: Start dev server
252
+ cd platform && yarn start:rspack react-ufo
253
+
254
+ # Terminal 2: Run integration tests
255
+ cd platform && yarn test:integration packages/react-ufo/atlaskit/__tests__/ \
256
+ --retries 0 --reporter list --reuse-dev-server \
257
+ --project=desktop-chromium --max-failures=0
258
+ ```
259
+
260
+ ### Verifying Non-Flaky Tests
261
+ To simulate CI-like environment:
262
+ ```bash
263
+ yarn test:integration packages/react-ufo/atlaskit/__tests__/playwright/base.spec.ts \
264
+ --retries 0 --reporter list --reuse-dev-server \
265
+ --project=desktop-chromium --max-failures=0 \
266
+ --repeat-each 50 --workers 50
267
+ ```
268
+
269
+ Integration tests are located in `__tests__/playwright/` and use example pages from `examples/`.
270
+
271
+ ## Common Tasks
272
+
273
+ ### Adding a New Interaction Type
274
+ 1. Create trace function in `src/trace-{type}/`
275
+ 2. Update `InteractionType` in common types
276
+ 3. Add configuration support in `src/config/`
277
+ 4. Update payload generation in `src/create-payload/`
278
+
279
+ ### Modifying VC Calculation
280
+ 1. VC observers are in `src/vc/vc-observer-new/` (current) and `src/vc/vc-observer/` (legacy)
281
+ 2. Revisions are controlled by `enabledVCRevisions` config
282
+ 3. Test changes against existing integration tests
283
+
284
+ ### Adding Custom Metrics
285
+ 1. Use `addUFOCustomData` for custom attributes
286
+ 2. Use `addCustomTiming` for custom timing spans
287
+ 3. Custom data appears in payload under `interactionMetrics.customData`
288
+
289
+ ### Adding Feature Flags
290
+ 1. Import `fg` from `@atlaskit/platform-feature-flags`
291
+ 2. Register flag in `package.json` under `platform-feature-flags`
292
+ 3. Put fg check as **last condition** in if statements: `if (a === b && fg('my_fg'))`
293
+
294
+ ## Important Patterns
295
+
296
+ ### Singleton Pattern
297
+ The package uses a singleton pattern for global state:
298
+ ```typescript
299
+ // VCObserver is global per interaction
300
+ globalThis.__vcObserver = new VCObserverWrapper(opts);
301
+
302
+ // Config is module-level singleton
303
+ let config: Config | undefined;
304
+ ```
305
+
306
+ ### Hold System
307
+ Holds prevent interaction completion until all loading states resolve:
308
+ ```typescript
309
+ // Hold is added when loading starts
310
+ const release = addHold(interactionId, labelStack, 'my-loading');
311
+
312
+ // Hold is released when loading completes
313
+ release();
314
+ ```
315
+
316
+ ### Segment Tree
317
+ Segments are tracked hierarchically for nested measurement:
318
+ ```typescript
319
+ // Parent segment
320
+ <UFOSegment name="page">
321
+ // Child segment
322
+ <UFOSegment name="sidebar">
323
+ <Content />
324
+ </UFOSegment>
325
+ </UFOSegment>
326
+ ```
327
+
328
+ ### Third-Party Segments
329
+ For external/plugin content:
330
+ ```typescript
331
+ import { UFOThirdPartySegment } from '@atlaskit/react-ufo/segment';
332
+
333
+ <UFOThirdPartySegment name="external-widget">
334
+ <ExternalWidget />
335
+ </UFOThirdPartySegment>
336
+ ```
337
+
338
+ ## Debugging
339
+
340
+ ### Performance Tracing
341
+ Enable Chrome DevTools performance tracing:
342
+ ```typescript
343
+ window.__REACT_UFO_ENABLE_PERF_TRACING = true;
344
+ ```
345
+
346
+ ### Event Inspection
347
+ Listen for interaction completion:
348
+ ```typescript
349
+ window.addEventListener('UFO_FINISH_INTERACTION', (event) => {
350
+ console.log('UFO interaction finished:', event.detail);
351
+ });
352
+ ```
353
+
354
+ ### Chrome Extension
355
+ Use the **UFO Chrome Extension** (`apps/ufo-chrome-extension` in perf-portal-monorepo) for DevTools integration.
356
+
357
+ ## Related Packages
358
+
359
+ | Package | Purpose |
360
+ |---------|---------|
361
+ | `@atlaskit/react-ufo` | Core UFO instrumentation (this package) |
362
+ | `ufo-load-hold` | Standalone load hold component |
363
+ | `ufo-apollo-log` | Apollo Client integration |
364
+ | `ufo-resource-router-log` | react-resource-router integration |
365
+ | `ufo-use-layout-effect-safe` | SSR-safe useLayoutEffect |
366
+
367
+ ## Key Confluence References
368
+
369
+ | Topic | Link |
370
+ |-------|------|
371
+ | Team Home (FeObs) | `go/feo` |
372
+ | Glance Product Page | `go/glance` |
373
+ | Data Architecture Diagram | `go/glance-data-architecture` |
374
+ | UFO Pipeline Architecture | [UFO Design - M1 Pipeline Architecture](https://hello.atlassian.net/wiki/spaces/UFO/pages/1050787866) |
375
+ | Team Onboarding | [Onboarding](https://hello.atlassian.net/wiki/spaces/APD/pages/1386514416) |
376
+ | React UFO Onboarding | [React UFO and UFO Onboarding](https://hello.atlassian.net/wiki/spaces/APD/pages/3615063023) |
377
+ | React UFO v2 Design | [react-ufo UFO v2](https://hello.atlassian.net/wiki/spaces/UFO/pages/2305847386) |
378
+ | Performance Blog | [react-UFO: A deeper understanding of performance](https://hello.atlassian.net/wiki/spaces/UFO/blog/2022/12/16/2280380649) |
379
+
380
+ ## Configuration Reference
381
+
382
+ Key configuration options in `setUFOConfig`:
383
+
384
+ ```typescript
385
+ interface Config {
386
+ product: string; // Product identifier (e.g., 'jira', 'confluence')
387
+ region: string; // Deployment region
388
+ population?: string; // Event population for segmentation
389
+ rates?: Record<string, number>; // Sampling rates per experience
390
+ kind?: Record<InteractionKind, number>; // Rates by interaction type
391
+ vc?: {
392
+ enabled?: boolean;
393
+ enabledVCRevisions?: {
394
+ all: TTVCRevision[];
395
+ byExperience?: Record<string, TTVCRevision[]>;
396
+ };
397
+ heatmapSize?: number;
398
+ selectorConfig?: SelectorConfig;
399
+ };
400
+ ssr?: {
401
+ getSSRTimings?: () => SSRTiming[];
402
+ getSSRDoneTime?: () => number | undefined;
403
+ };
404
+ postInteractionLog?: { enabled?: boolean; rates?: Rates; };
405
+ experimentalInteractionMetrics?: { enabled?: boolean; rates?: Rates; };
406
+ terminalErrors?: { enabled?: boolean; };
407
+ }
408
+ ```
409
+
410
+ ## Contributing
411
+
412
+ When making changes:
413
+ 1. Run unit tests: `cd platform && yarn test packages/react-ufo`
414
+ 2. Run integration tests to verify VC measurement accuracy
415
+ 3. Consider impact on downstream Glance/Performance Portal processing
416
+ 4. Update sampling rates carefully as they affect data volume
417
+ 5. Feature flag new functionality for gradual rollout
418
+ 6. Coordinate with the FeObs team (`#help-devinfra-fe-observability`) for major changes
419
+
420
+ ## Dangerous Operations — Never Do These
421
+
422
+ 1. **Never modify sampling rates without approval** — affects data volume and costs
423
+ 2. **Never change payload schema without coordination** — breaks downstream processing
424
+ 3. **Never remove TTVC revisions** — experiences may depend on specific revisions
425
+ 4. **Never disable holds without understanding impact** — affects TTAI accuracy
426
+ 5. **Never add blocking operations in hot paths** — affects user performance
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @atlaskit/ufo-interaction-ignore
2
2
 
3
+ ## 5.1.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [`aa7b28d013b4f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/aa7b28d013b4f) -
8
+ Add Speed Index metric using TTVC v4 ruleset
9
+ - [`e565e9abbe8fd`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/e565e9abbe8fd) -
10
+ Added Previous Interaction information to terminal error metric
11
+
12
+ ## 5.1.3
13
+
14
+ ### Patch Changes
15
+
16
+ - [`376606c3c8197`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/376606c3c8197) -
17
+ FG cleanup - platform_ufo_enable_media_for_ttvc_v3
18
+
3
19
  ## 5.1.2
4
20
 
5
21
  ### Patch Changes
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.ModuleLoadingProfiler = void 0;
7
+ exports.PreviousInteractionLog = exports.ModuleLoadingProfiler = void 0;
8
8
  exports.abort = abort;
9
9
  exports.abortAll = abortAll;
10
10
  exports.abortByNewInteraction = abortByNewInteraction;
@@ -71,9 +71,12 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
71
71
  function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
72
72
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
73
73
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
74
- var PreviousInteractionLog = {
74
+ var PreviousInteractionLog = exports.PreviousInteractionLog = {
75
+ id: undefined,
75
76
  name: undefined,
76
- isAborted: undefined
77
+ type: undefined,
78
+ isAborted: undefined,
79
+ timestamp: undefined
77
80
  };
78
81
  var postInteractionLog = exports.postInteractionLog = new _postInteractionLog.default();
79
82
  var interactionExtraMetrics = exports.interactionExtraMetrics = new _interactionExtraMetrics.default();
@@ -751,6 +754,11 @@ function finishInteraction(id, data) {
751
754
  remove(id);
752
755
  }
753
756
  }
757
+ if ((0, _platformFeatureFlags.fg)('platform_ufo_enable_terminal_errors')) {
758
+ PreviousInteractionLog.id = data.id;
759
+ PreviousInteractionLog.type = data.type;
760
+ PreviousInteractionLog.timestamp = data.end;
761
+ }
754
762
  PreviousInteractionLog.name = data.ufoName || 'unknown';
755
763
  PreviousInteractionLog.isAborted = data.abortReason != null;
756
764
  if (data.ufoName) {
@@ -18,18 +18,26 @@ function sinkTerminalErrorHandler(fn) {
18
18
  sinkHandlerFn = fn;
19
19
  }
20
20
  function setTerminalError(error, additionalAttributes, labelStack) {
21
- var _activeInteraction$uf, _activeInteraction$id, _activeInteraction$ty;
21
+ var _activeInteraction$uf, _activeInteraction$id, _activeInteraction$ty, _PreviousInteractionL, _PreviousInteractionL2, _PreviousInteractionL3;
22
22
  var activeInteraction = (0, _interactionMetrics.getActiveInteraction)();
23
+ var currentTime = performance.now();
23
24
  var errorData = _objectSpread({
24
25
  errorType: error.name || 'Error',
25
26
  errorMessage: error.message.slice(0, 100),
26
- timestamp: performance.now()
27
+ timestamp: currentTime
27
28
  }, additionalAttributes);
29
+
30
+ // Calculate time since previous interaction
31
+ var timeSincePreviousInteraction = _interactionMetrics.PreviousInteractionLog.timestamp != null ? currentTime - _interactionMetrics.PreviousInteractionLog.timestamp : null;
28
32
  var context = {
29
33
  labelStack: labelStack !== null && labelStack !== void 0 ? labelStack : null,
30
34
  activeInteractionName: (_activeInteraction$uf = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.ufoName) !== null && _activeInteraction$uf !== void 0 ? _activeInteraction$uf : null,
31
35
  activeInteractionId: (_activeInteraction$id = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.id) !== null && _activeInteraction$id !== void 0 ? _activeInteraction$id : null,
32
- activeInteractionType: (_activeInteraction$ty = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.type) !== null && _activeInteraction$ty !== void 0 ? _activeInteraction$ty : null
36
+ activeInteractionType: (_activeInteraction$ty = activeInteraction === null || activeInteraction === void 0 ? void 0 : activeInteraction.type) !== null && _activeInteraction$ty !== void 0 ? _activeInteraction$ty : null,
37
+ previousInteractionId: (_PreviousInteractionL = _interactionMetrics.PreviousInteractionLog.id) !== null && _PreviousInteractionL !== void 0 ? _PreviousInteractionL : null,
38
+ previousInteractionName: (_PreviousInteractionL2 = _interactionMetrics.PreviousInteractionLog.name) !== null && _PreviousInteractionL2 !== void 0 ? _PreviousInteractionL2 : null,
39
+ previousInteractionType: (_PreviousInteractionL3 = _interactionMetrics.PreviousInteractionLog.type) !== null && _PreviousInteractionL3 !== void 0 ? _PreviousInteractionL3 : null,
40
+ timeSincePreviousInteraction: timeSincePreviousInteraction
33
41
  };
34
42
  sinkHandlerFn(errorData, context);
35
43
  }
@@ -120,13 +120,14 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
120
120
  value: function () {
121
121
  var _calculateWithDebugInfo = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(filteredEntries, startTime, stopTime, isPostInteraction, isVCClean, interactionType, isPageVisible, interactionId, dirtyReason, allEntries, include3p, excludeSmartAnswersInSearch, interactionAbortReason, includeSSRRatio) {
122
122
  var _window, _window2, _window3, _window4, _window6;
123
- var percentiles, viewportEntries, vcLogs, vcDetails, percentileIndex, entryDataBuffer, ssrRatio, _iterator4, _step4, _entry3, time, viewportPercentage, entries, elementNames, previousResult, i, percentile, enhancedVcLogs, shouldCalculate3p, shouldCalculateDebugDetails, sortedVcLogs, maxViewportPercentageAtTime, maxSoFar, _iterator5, _step5, log, getBiggestPreviousViewportPercentage, ignoredEntriesByTime, _iterator6, _step6, _entry4, _ignoredEntriesByTime, _viewportData$rect, _viewportData$previou, viewportData, timestamp, additionalVcLogs, _iterator7, _step7, _step7$value, _timestamp, ignoredEntries, _viewportPercentage, v3RevisionDebugDetails, _window5, _window5$__ufo_devtoo, _window7, _window7$__on_ufo_vc_;
123
+ var percentiles, viewportEntries, shouldCalculateSpeedIndex, _yield$calculateTTVCP, vcLogs, speedIndex, vcDetails, percentileIndex, entryDataBuffer, ssrRatio, _iterator4, _step4, _entry3, time, viewportPercentage, entries, elementNames, previousResult, i, percentile, enhancedVcLogs, shouldCalculate3p, shouldCalculateDebugDetails, sortedVcLogs, maxViewportPercentageAtTime, maxSoFar, _iterator5, _step5, log, getBiggestPreviousViewportPercentage, ignoredEntriesByTime, _iterator6, _step6, _entry4, _ignoredEntriesByTime, _viewportData$rect, _viewportData$previou, viewportData, timestamp, additionalVcLogs, _iterator7, _step7, _step7$value, _timestamp, ignoredEntries, _viewportPercentage, v3RevisionDebugDetails, _window5, _window5$__ufo_devtoo, _window7, _window7$__on_ufo_vc_;
124
124
  return _regenerator.default.wrap(function _callee$(_context) {
125
125
  while (1) switch (_context.prev = _context.next) {
126
126
  case 0:
127
127
  percentiles = [25, 50, 75, 80, 85, 90, 95, 98, 99, 100];
128
128
  viewportEntries = this.filterViewportEntries(filteredEntries);
129
- _context.next = 4;
129
+ shouldCalculateSpeedIndex = (0, _platformFeatureFlags.fg)('platform_ufo_ttvc_v4_speed_index');
130
+ _context.next = 5;
130
131
  return (0, _percentileCalc.calculateTTVCPercentilesWithDebugInfo)({
131
132
  viewport: {
132
133
  width: (0, _getViewportWidth.default)(),
@@ -134,24 +135,27 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
134
135
  },
135
136
  startTime: startTime,
136
137
  stopTime: stopTime,
137
- orderedEntries: viewportEntries
138
+ orderedEntries: viewportEntries,
139
+ calculateSpeedIndex: shouldCalculateSpeedIndex
138
140
  });
139
- case 4:
140
- vcLogs = _context.sent;
141
+ case 5:
142
+ _yield$calculateTTVCP = _context.sent;
143
+ vcLogs = _yield$calculateTTVCP.entries;
144
+ speedIndex = _yield$calculateTTVCP.speedIndex;
141
145
  vcDetails = {};
142
146
  percentileIndex = 0;
143
147
  entryDataBuffer = new Set();
144
148
  ssrRatio = -1;
145
149
  if (!vcLogs) {
146
- _context.next = 30;
150
+ _context.next = 33;
147
151
  break;
148
152
  }
149
153
  _iterator4 = _createForOfIteratorHelper(vcLogs);
150
- _context.prev = 11;
154
+ _context.prev = 14;
151
155
  _iterator4.s();
152
- case 13:
156
+ case 16:
153
157
  if ((_step4 = _iterator4.n()).done) {
154
- _context.next = 22;
158
+ _context.next = 25;
155
159
  break;
156
160
  }
157
161
  _entry3 = _step4.value;
@@ -164,11 +168,11 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
164
168
 
165
169
  // Only process entries if we haven't reached all percentiles
166
170
  if (!(percentileIndex >= percentiles.length)) {
167
- _context.next = 19;
171
+ _context.next = 22;
168
172
  break;
169
173
  }
170
- return _context.abrupt("break", 22);
171
- case 19:
174
+ return _context.abrupt("break", 25);
175
+ case 22:
172
176
  // Check if this entry matches any checkpoint percentiles
173
177
  if (viewportPercentage >= percentiles[percentileIndex]) {
174
178
  elementNames = [];
@@ -199,21 +203,21 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
199
203
  return entryDataBuffer.add(e);
200
204
  });
201
205
  }
202
- case 20:
203
- _context.next = 13;
206
+ case 23:
207
+ _context.next = 16;
204
208
  break;
205
- case 22:
206
- _context.next = 27;
209
+ case 25:
210
+ _context.next = 30;
207
211
  break;
208
- case 24:
209
- _context.prev = 24;
210
- _context.t0 = _context["catch"](11);
211
- _iterator4.e(_context.t0);
212
212
  case 27:
213
213
  _context.prev = 27;
214
- _iterator4.f();
215
- return _context.finish(27);
214
+ _context.t0 = _context["catch"](14);
215
+ _iterator4.e(_context.t0);
216
216
  case 30:
217
+ _context.prev = 30;
218
+ _iterator4.f();
219
+ return _context.finish(30);
220
+ case 33:
217
221
  // Fill in any missing percentiles with the last known values
218
222
  previousResult = {
219
223
  t: 0,
@@ -373,13 +377,14 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
373
377
  }
374
378
  return _context.abrupt("return", {
375
379
  vcDetails: vcDetails,
376
- ssrRatio: ssrRatio
380
+ ssrRatio: ssrRatio,
381
+ speedIndex: speedIndex
377
382
  });
378
- case 42:
383
+ case 45:
379
384
  case "end":
380
385
  return _context.stop();
381
386
  }
382
- }, _callee, this, [[11, 24, 27, 30]]);
387
+ }, _callee, this, [[14, 27, 30, 33]]);
383
388
  }));
384
389
  function calculateWithDebugInfo(_x, _x2, _x3, _x4, _x5, _x6, _x7, _x8, _x9, _x0, _x1, _x10, _x11, _x12) {
385
390
  return _calculateWithDebugInfo.apply(this, arguments);
@@ -393,7 +398,7 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
393
398
  var _this = this,
394
399
  _vcDetails$90$t,
395
400
  _vcDetails$;
396
- var startTime, stopTime, orderedEntries, interactionId, isPostInteraction, include3p, excludeSmartAnswersInSearch, includeSSRRatio, interactionType, isPageVisible, interactionAbortReason, filteredEntries, isVCClean, dirtyReason, getVCCleanStatusResult, _yield$this$calculate, vcDetails, ssrRatio, result;
401
+ var startTime, stopTime, orderedEntries, interactionId, isPostInteraction, include3p, excludeSmartAnswersInSearch, includeSSRRatio, interactionType, isPageVisible, interactionAbortReason, filteredEntries, isVCClean, dirtyReason, getVCCleanStatusResult, _yield$this$calculate, vcDetails, ssrRatio, speedIndex, result;
397
402
  return _regenerator.default.wrap(function _callee2$(_context2) {
398
403
  while (1) switch (_context2.prev = _context2.next) {
399
404
  case 0:
@@ -422,6 +427,7 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
422
427
  _yield$this$calculate = _context2.sent;
423
428
  vcDetails = _yield$this$calculate.vcDetails;
424
429
  ssrRatio = _yield$this$calculate.ssrRatio;
430
+ speedIndex = _yield$this$calculate.speedIndex;
425
431
  result = {
426
432
  revision: this.revisionNo,
427
433
  clean: true,
@@ -432,9 +438,15 @@ var AbstractVCCalculatorBase = exports.default = /*#__PURE__*/function () {
432
438
  if (ssrRatio !== -1) {
433
439
  result.ssrRatio = ssrRatio;
434
440
  }
441
+
442
+ // speedIndex is only calculated when platform_ufo_ttvc_v4_speed_index is enabled,
443
+ // so we only include it in the result when it has a meaningful value (> 0)
444
+ if (speedIndex > 0) {
445
+ result.speedIndex = speedIndex;
446
+ }
435
447
  result.labelStacks = this.getLabelStacks(filteredEntries, isPostInteraction);
436
448
  return _context2.abrupt("return", result);
437
- case 17:
449
+ case 19:
438
450
  case "end":
439
451
  return _context2.stop();
440
452
  }
@@ -34,9 +34,7 @@ var getConsideredEntryTypes = function getConsideredEntryTypes(include3p, exclud
34
34
  entryTypes.push('mutation:smart-answers-element');
35
35
  entryTypes.push('mutation:smart-answers-attribute');
36
36
  }
37
- if ((0, _platformFeatureFlags.fg)('platform_ufo_enable_media_for_ttvc_v3')) {
38
- entryTypes.push('mutation:media');
39
- }
37
+ entryTypes.push('mutation:media');
40
38
 
41
39
  // Still included as part of TTVC v3
42
40
  entryTypes.push('mutation:attribute:non-visual-input-name');