Skip to content

feat: add main-thread blocking detection (Long Tasks + LoAF + web Vitals)#166

Open
Dobrunia wants to merge 38 commits intomasterfrom
feat/thread-blocking-detection
Open

feat: add main-thread blocking detection (Long Tasks + LoAF + web Vitals)#166
Dobrunia wants to merge 38 commits intomasterfrom
feat/thread-blocking-detection

Conversation

@Dobrunia
Copy link
Copy Markdown
Member

@Dobrunia Dobrunia commented Feb 23, 2026

Summary

  • Add main-thread freeze detection to JS catcher:
    • Long Tasks API
    • Long Animation Frames (LoAF)
  • Add aggregated Web Vitals issues monitoring (LCP, FCP, TTFB, INP, CLS):
    • sends one dedicated Hawk issue event when at least one metric is poor
    • includes full metrics payload (value, rating, delta) for diagnostics
  • New unified config:
    • issues.errors
    • issues.webVitals
    • issues.longTasks.thresholdMs
    • issues.longAnimationFrames.thresholdMs

Defaults

issues: {  
  errors: true,  
  webVitals: false,  
  longTasks: false, 
  longAnimationFrames: false
}

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds main-thread blocking detection to the JavaScript error catcher using the Long Tasks API and Long Animation Frames (LoAF) API. When a blocking entry is detected (task >50 ms), a dedicated Hawk event is sent immediately with blocking details in the context.

Changes:

  • Added new longTasks.ts addon that sets up PerformanceObservers for Long Tasks and LoAF APIs
  • Integrated the observers into the Catcher constructor with configurable options
  • Added mainThreadBlocking configuration option to HawkInitialSettings

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/javascript/src/addons/longTasks.ts New addon implementing Long Tasks and LoAF observers with feature detection and data serialization
packages/javascript/src/catcher.ts Integrated main-thread blocking detection into the constructor with callback to send()
packages/javascript/src/types/hawk-initial-settings.ts Added mainThreadBlocking configuration option type definition
packages/javascript/package.json Bumped minor version from 3.2.18 to 3.3.0 for new feature
packages/javascript/README.md Added documentation for main-thread blocking detection feature with configuration examples
Comments suppressed due to low confidence (2)

packages/javascript/src/addons/longTasks.ts:182

  • Using buffered: true means the observer will immediately process all past long task entries that occurred before the observer was set up. If Hawk is initialized late in the page lifecycle on a slow device, this could result in a burst of events being sent all at once, potentially overwhelming the transport or causing rate limiting issues. Consider documenting this behavior or providing an option to disable buffering for users who only want to track tasks from the point of initialization forward.
    }).observe({ type: 'longtask', buffered: true });

packages/javascript/src/addons/longTasks.ts:245

  • Using buffered: true means the observer will immediately process all past LoAF entries that occurred before the observer was set up. If Hawk is initialized late in the page lifecycle on a slow device, this could result in a burst of events being sent all at once, potentially overwhelming the transport or causing rate limiting issues. Consider documenting this behavior or providing an option to disable buffering for users who only want to track frames from the point of initialization forward.
    }).observe({ type: 'long-animation-frame', buffered: true });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
/**
* Build a Json object from entries, dropping null / undefined / empty-string values
*/
function compact(entries: [string, JsonNode | null | undefined][]): Json {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utility function should be added to /utils and covered by tests

Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/addons/longTasks.ts Outdated
Comment thread packages/javascript/src/types/hawk-initial-settings.ts Outdated
Dobrunia and others added 8 commits February 24, 2026 00:59
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Peter <specc.dev@gmail.com>
Co-authored-by: Peter <specc.dev@gmail.com>
…interfaces and improving script serialization
- Introduced `web-vitals` as a peer dependency and added it to the package.json.
- Updated README to reflect new issues detection configuration, including `issues.webVitals`.
- Refactored the catcher to utilize an `IssuesMonitor` for handling global errors and performance issues.
- Removed the Long Tasks and LoAF tracking functionality, consolidating it under the new issues detection system.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 10 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (10)

packages/javascript/src/addons/issues.ts:298

  • JSDoc comment is missing a description. It should describe what this function does, e.g., "Resolve and validate the threshold value, applying minimum and default values as needed."
/**
 *
 * @param value custom threshold
 * @param fallback default threshold
 */

packages/javascript/src/addons/issues.ts:319

  • JSDoc comment is missing a description. It should describe what this function does, e.g., "Serialize a LoAF script entry to a compact JSON object, omitting null and undefined values."
/**
 *
 * @param script loaf script entry
 */

packages/javascript/src/addons/issues.ts:338

  • JSDoc comment is missing a description. It should describe what this function does, e.g., "Serialize a Web Vitals report to a compact JSON object for transmission."
/**
 *
 * @param report aggregated vitals report
 */

packages/javascript/src/addons/issues.ts:81

  • JSDoc comment is missing a description. It should describe what this method does, e.g., "Observe Long Tasks using the Performance API and emit issues when duration exceeds threshold."
  /**
   *
   * @param thresholdMs max allowed duration
   * @param onIssue issue callback
   */

packages/javascript/src/addons/issues.ts:203

  • JSDoc comment is missing a description. It should describe what this method does, e.g., "Initialize Web Vitals monitoring and emit issue when at least one metric is poor."
  /**
   *
   * @param onIssue issue callback
   */

packages/javascript/src/addons/issues.ts:311

  • JSDoc comment is missing a description. It should describe what this function does, e.g., "Format a Web Vital metric value for display (fixed precision for CLS, milliseconds for others)."
/**
 *
 * @param name metric name
 * @param value metric value
 */

packages/javascript/src/addons/issues.ts:121

  • The observe call should have the options object on a single line or properly formatted. The current formatting with buffered: true on a separate line without proper indentation is inconsistent with typical JavaScript style conventions.
      this.longTaskObserver.observe({ type: 'longtask',
        buffered: true });

packages/javascript/src/addons/issues.ts:194

  • The observe call should have the options object on a single line or properly formatted. The current formatting with buffered: true on a separate line without proper indentation is inconsistent with typical JavaScript style conventions.
      this.loafObserver.observe({ type: 'long-animation-frame',
        buffered: true });

packages/javascript/src/addons/issues.ts:131

  • JSDoc comment is missing a description. It should describe what this method does, e.g., "Observe Long Animation Frames using the Performance API and emit issues when duration exceeds threshold."
  /**
   *
   * @param thresholdMs max allowed duration
   * @param onIssue issue callback
   */

packages/javascript/src/addons/issues.ts:281

  • JSDoc comment is missing a description. It should describe what this function does, e.g., "Check if the Performance API supports a specific entry type."
/**
 *
 * @param type performance entry type
 */

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +209 to +212
const tryReport = (): void => {
if (this.destroyed || reported || collected.length < TOTAL_WEB_VITALS) {
return;
}
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Web Vitals reporting waits for all 5 metrics (LCP, FCP, TTFB, INP, CLS) to be collected before reporting. However, not all metrics may be available on every page (e.g., INP requires user interaction, LCP may not occur on certain pages). This means poor metrics might never be reported if the page doesn't trigger all 5 metrics. Consider reporting when a timeout expires or when the page is about to be unloaded, rather than waiting for all 5 metrics indefinitely.

Copilot uses AI. Check for mistakes.
Comment thread packages/javascript/src/addons/issues.ts Outdated
Comment thread packages/javascript/src/catcher.ts Outdated
Comment thread packages/javascript/src/addons/issues.ts Outdated
Comment thread packages/javascript/README.md Outdated
@Dobrunia Dobrunia changed the title feat: add main-thread blocking detection (Long Tasks + LoAF) feat: add main-thread blocking detection (Long Tasks + LoAF + web Vitals) Feb 26, 2026
CLS: [0.1, 0.25],
};

const TOTAL_WEB_VITALS = 5;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its unclear for me. Describe the use case in jsdoc

Comment thread packages/javascript/src/addons/performance-issues.ts Outdated
Comment thread packages/javascript/src/addons/performance-issues.ts
Comment thread packages/javascript/src/addons/performance-issues.ts Outdated
Comment thread packages/javascript/src/addons/performance-issues.ts Outdated
Comment thread packages/javascript/src/addons/performance-issues.ts Outdated
Comment thread packages/javascript/src/addons/performance-issues.ts Outdated
Comment thread packages/javascript/src/types/issues.ts
Comment thread packages/javascript/README.md Outdated
Comment thread packages/javascript/README.md Outdated
Copy link
Copy Markdown
Member

@neSpecc neSpecc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All new features should be enabled out of the box by default

Comment thread packages/javascript/src/addons/performance-issues.ts Outdated
@Dobrunia Dobrunia requested a review from neSpecc April 24, 2026 18:02
Comment thread packages/javascript/README.md Outdated

All detectors are enabled by default.
If the browser does not support a specific Performance API (`longtask`, `long-animation-frame`), the corresponding detector is silently skipped.
Default `issues` (when you omit the option or pass `{}`) is opt-in for performance detectors; global error listeners stay on:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make more straigtforward

Comment thread packages/javascript/README.md Outdated
- 🛡️ Sensitive data filtering
- 🌟 Source maps consuming
- 💬 Console logs tracking
- 🧊 Main-thread blocking detection (Long Tasks + LoAF, Chromium-only)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- 🧊 Main-thread blocking detection (Long Tasks + LoAF, Chromium-only)
- 🧊 Performance issues detection (Long Tasks + Long Animation Frames)

Comment thread packages/javascript/README.md Outdated
- 💬 Console logs tracking
- 🧊 Main-thread blocking detection (Long Tasks + LoAF, Chromium-only)
- 📊 Web Vitals issues monitoring
- ⚙️ Unified `issues` configuration (errors + performance detectors)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- ⚙️ Unified `issues` configuration (errors + performance detectors)

Pass `true` or an options object for any detector you want. If the browser does not support a specific Performance API (`longtask`, `long-animation-frame`), the corresponding detector is silently skipped.

Performance data is transmitted in the event **addons** (keys: `Long Task`, `Long Animation Frame`, `Web Vitals`).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add Warning block:

It is highly recommended to specify a Grouping Pattern in your Project Settings to merge all "Long Task: ..." under a single Even to prevent overloading of event list

and pattern example

Long Task.+
Long Animation Frame.+

Comment on lines +264 to +265
this.isInitialized = true;
this.destroyed = false;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one of them seems redundant.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

find the reason how init can be called several times?

*
* @param entries
*/
export function compactJson(entries: [string, JsonNode | null | undefined][]): Json {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider using Sanitizer.sanitize()

Comment thread packages/javascript/src/catcher.ts Outdated
/**
* Issues monitor instance
*/
private readonly issuesMonitor = new PerformanceIssuesMonitor();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we dont need to call PerformanceIssuesMonitor if it is not enabled

}

if (shouldDetectPerformanceIssues) {
this.issuesMonitor.init(issues, (entry) => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like it can't be initied several times, is isInitialized is probably redundant

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redundant

…h a single isActive flag for improved clarity and maintainability.
…with a new sanitizeIssuePayload function for improved data sanitization. Remove obsolete compactJson utility and its associated tests.
@Dobrunia Dobrunia requested a review from neSpecc April 27, 2026 17:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants