11// TODO (v8): This import can be removed once we only support Node with global URL
22import { URL } from 'url' ;
33import { convertIntegrationFnToClass , defineIntegration , getCurrentScope } from '@sentry/core' ;
4- import type { Client , Contexts , Event , EventHint , Integration , IntegrationClass , IntegrationFn } from '@sentry/types' ;
4+ import type {
5+ Client ,
6+ Contexts ,
7+ Event ,
8+ EventHint ,
9+ Integration ,
10+ IntegrationClass ,
11+ IntegrationFn ,
12+ IntegrationFnResult ,
13+ } from '@sentry/types' ;
514import { dynamicRequire , logger } from '@sentry/utils' ;
615import type { Worker , WorkerOptions } from 'worker_threads' ;
716import type { NodeClient } from '../../client' ;
@@ -52,23 +61,51 @@ interface InspectorApi {
5261
5362const INTEGRATION_NAME = 'Anr' ;
5463
64+ type AnrInternal = { startWorker : ( ) => void ; stopWorker : ( ) => void } ;
65+
5566const _anrIntegration = ( ( options : Partial < AnrIntegrationOptions > = { } ) => {
67+ let worker : Promise < ( ) => void > | undefined ;
68+ let client : NodeClient | undefined ;
69+
5670 return {
5771 name : INTEGRATION_NAME ,
5872 // TODO v8: Remove this
5973 setupOnce ( ) { } , // eslint-disable-line @typescript-eslint/no-empty-function
60- setup ( client : NodeClient ) {
74+ startWorker : ( ) => {
75+ if ( worker ) {
76+ return ;
77+ }
78+
79+ if ( client ) {
80+ worker = _startWorker ( client , options ) ;
81+ }
82+ } ,
83+ stopWorker : ( ) => {
84+ if ( worker ) {
85+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
86+ worker . then ( stop => {
87+ stop ( ) ;
88+ worker = undefined ;
89+ } ) ;
90+ }
91+ } ,
92+ setup ( initClient : NodeClient ) {
6193 if ( NODE_VERSION . major < 16 || ( NODE_VERSION . major === 16 && NODE_VERSION . minor < 17 ) ) {
6294 throw new Error ( 'ANR detection requires Node 16.17.0 or later' ) ;
6395 }
6496
65- // setImmediate is used to ensure that all other integrations have been setup
66- setImmediate ( ( ) => _startWorker ( client , options ) ) ;
97+ client = initClient ;
98+
99+ // setImmediate is used to ensure that all other integrations have had their setup called first.
100+ // This allows us to call into all integrations to fetch the full context
101+ setImmediate ( ( ) => this . startWorker ( ) ) ;
67102 } ,
68- } ;
103+ } as IntegrationFnResult & AnrInternal ;
69104} ) satisfies IntegrationFn ;
70105
71- export const anrIntegration = defineIntegration ( _anrIntegration ) ;
106+ type AnrReturn = ( options ?: Partial < AnrIntegrationOptions > ) => IntegrationFnResult & AnrInternal ;
107+
108+ export const anrIntegration = defineIntegration ( _anrIntegration ) as AnrReturn ;
72109
73110/**
74111 * Starts a thread to detect App Not Responding (ANR) events
@@ -90,14 +127,20 @@ export type Anr = typeof Anr;
90127/**
91128 * Starts the ANR worker thread
92129 */
93- async function _startWorker ( client : NodeClient , _options : Partial < AnrIntegrationOptions > ) : Promise < void > {
94- const contexts = await getContexts ( client ) ;
130+ async function _startWorker (
131+ client : NodeClient ,
132+ integrationOptions : Partial < AnrIntegrationOptions > ,
133+ ) : Promise < ( ) => void > {
95134 const dsn = client . getDsn ( ) ;
96135
97136 if ( ! dsn ) {
98- return ;
137+ return ( ) => {
138+ //
139+ } ;
99140 }
100141
142+ const contexts = await getContexts ( client ) ;
143+
101144 // These will not be accurate if sent later from the worker thread
102145 delete contexts . app ?. app_memory ;
103146 delete contexts . device ?. free_memory ;
@@ -116,11 +159,11 @@ async function _startWorker(client: NodeClient, _options: Partial<AnrIntegration
116159 release : initOptions . release ,
117160 dist : initOptions . dist ,
118161 sdkMetadata,
119- appRootPath : _options . appRootPath ,
120- pollInterval : _options . pollInterval || DEFAULT_INTERVAL ,
121- anrThreshold : _options . anrThreshold || DEFAULT_HANG_THRESHOLD ,
122- captureStackTrace : ! ! _options . captureStackTrace ,
123- staticTags : _options . staticTags || { } ,
162+ appRootPath : integrationOptions . appRootPath ,
163+ pollInterval : integrationOptions . pollInterval || DEFAULT_INTERVAL ,
164+ anrThreshold : integrationOptions . anrThreshold || DEFAULT_HANG_THRESHOLD ,
165+ captureStackTrace : ! ! integrationOptions . captureStackTrace ,
166+ staticTags : integrationOptions . staticTags || { } ,
124167 contexts,
125168 } ;
126169
@@ -139,6 +182,7 @@ async function _startWorker(client: NodeClient, _options: Partial<AnrIntegration
139182 } ) ;
140183
141184 process . on ( 'exit' , ( ) => {
185+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
142186 worker . terminate ( ) ;
143187 } ) ;
144188
@@ -176,4 +220,10 @@ async function _startWorker(client: NodeClient, _options: Partial<AnrIntegration
176220
177221 // Ensure this thread can't block app exit
178222 worker . unref ( ) ;
223+
224+ return ( ) => {
225+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
226+ worker . terminate ( ) ;
227+ clearInterval ( timer ) ;
228+ } ;
179229}
0 commit comments