@sentry/node-native 10.53.1 → 10.55.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.
@@ -1 +1 @@
1
- {"version":3,"file":"event-loop-block-watchdog.js","sources":["../../src/event-loop-block-watchdog.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport type { DebugImage, Event, ScopeData, Session, StackFrame, Thread } from '@sentry/core';\nimport {\n applyScopeDataToEvent,\n createEventEnvelope,\n createSessionEnvelope,\n filenameIsInApp,\n generateSpanId,\n getEnvelopeEndpointWithUrlEncodedAuth,\n makeSession,\n mergeScopeData,\n normalizeUrlToBase,\n Scope,\n stripSentryFramesAndReverse,\n updateSession,\n uuid4,\n} from '@sentry/core';\nimport { makeNodeTransport } from '@sentry/node';\nimport { captureStackTrace, getThreadsLastSeen } from '@sentry-internal/node-native-stacktrace';\nimport type { ThreadState, WorkerStartData } from './common';\nimport { POLL_RATIO } from './common';\n\ntype CurrentScopes = {\n isolationScope: Scope;\n};\n\nconst {\n threshold,\n appRootPath,\n contexts,\n debug,\n dist,\n dsn,\n environment,\n maxEventsPerHour,\n release,\n sdkMetadata,\n staticTags: tags,\n tunnel,\n} = workerData as WorkerStartData;\n\nconst pollInterval = threshold / POLL_RATIO;\nconst triggeredThreads = new Set<string>();\n\nfunction log(...msg: unknown[]): void {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log('[Sentry Event Loop Blocked Watchdog]', ...msg);\n }\n}\n\nfunction createRateLimiter(maxEventsPerHour: number): () => boolean {\n let currentHour = 0;\n let currentCount = 0;\n\n return function isRateLimited(): boolean {\n const hour = new Date().getHours();\n\n if (hour !== currentHour) {\n currentHour = hour;\n currentCount = 0;\n }\n\n if (currentCount >= maxEventsPerHour) {\n if (currentCount === maxEventsPerHour) {\n currentCount += 1;\n log(`Rate limit reached: ${currentCount} events in this hour`);\n }\n return true;\n }\n\n currentCount += 1;\n return false;\n };\n}\n\nconst url = getEnvelopeEndpointWithUrlEncodedAuth(dsn, tunnel, sdkMetadata.sdk);\nconst transport = makeNodeTransport({\n url,\n recordDroppedEvent: () => {\n //\n },\n});\nconst isRateLimited = createRateLimiter(maxEventsPerHour);\n\nasync function sendAbnormalSession(serializedSession: Session | undefined): Promise<void> {\n if (!serializedSession) {\n return;\n }\n\n log('Sending abnormal session');\n const session = makeSession(serializedSession);\n\n updateSession(session, {\n status: 'abnormal',\n abnormal_mechanism: 'anr_foreground',\n release,\n environment,\n });\n\n const envelope = createSessionEnvelope(session, dsn, sdkMetadata, tunnel);\n // Log the envelope so to aid in testing\n log(JSON.stringify(envelope));\n\n await transport.send(envelope);\n}\n\nlog('Started');\n\nfunction prepareStackFrames(stackFrames: StackFrame[] | undefined): StackFrame[] | undefined {\n if (!stackFrames) {\n return undefined;\n }\n\n // Strip Sentry frames and reverse the stack frames so they are in the correct order\n const strippedFrames = stripSentryFramesAndReverse(stackFrames);\n\n for (const frame of strippedFrames) {\n if (!frame.filename) {\n continue;\n }\n\n frame.in_app = filenameIsInApp(frame.filename);\n\n // If we have an app root path, rewrite the filenames to be relative to the app root\n if (appRootPath) {\n frame.filename = normalizeUrlToBase(frame.filename, appRootPath);\n }\n }\n\n return strippedFrames;\n}\n\nfunction stripFileProtocol(filename: string | undefined): string | undefined {\n if (!filename) {\n return undefined;\n }\n return filename.replace(/^file:\\/\\//, '');\n}\n\n// eslint-disable-next-line complexity\nfunction applyDebugMeta(event: Event, debugImages: Record<string, string>): void {\n if (Object.keys(debugImages).length === 0) {\n return;\n }\n\n const normalisedDebugImages = appRootPath ? {} : debugImages;\n if (appRootPath) {\n for (const [path, debugId] of Object.entries(debugImages)) {\n normalisedDebugImages[normalizeUrlToBase(path, appRootPath)] = debugId;\n }\n }\n\n const filenameToDebugId = new Map<string, string>();\n\n for (const exception of event.exception?.values || []) {\n for (const frame of exception.stacktrace?.frames || []) {\n const filename = stripFileProtocol(frame.abs_path || frame.filename);\n if (filename && normalisedDebugImages[filename]) {\n filenameToDebugId.set(filename, normalisedDebugImages[filename]);\n }\n }\n }\n\n for (const thread of event.threads?.values || []) {\n for (const frame of thread.stacktrace?.frames || []) {\n const filename = stripFileProtocol(frame.abs_path || frame.filename);\n if (filename && normalisedDebugImages[filename]) {\n filenameToDebugId.set(filename, normalisedDebugImages[filename]);\n }\n }\n }\n\n if (filenameToDebugId.size > 0) {\n const images: DebugImage[] = [];\n for (const [code_file, debug_id] of filenameToDebugId.entries()) {\n images.push({\n type: 'sourcemap',\n code_file,\n debug_id,\n });\n }\n event.debug_meta = { images };\n }\n}\n\nfunction getExceptionAndThreads(\n crashedThreadId: string,\n threads: ReturnType<typeof captureStackTrace<CurrentScopes, ThreadState>>,\n): Event {\n const crashedThread = threads[crashedThreadId];\n\n return {\n exception: {\n values: [\n {\n type: 'EventLoopBlocked',\n value: `Event Loop Blocked for at least ${threshold} ms`,\n stacktrace: { frames: prepareStackFrames(crashedThread?.frames) },\n // This ensures the UI doesn't say 'Crashed in' for the stack trace\n mechanism: { type: 'ANR' },\n thread_id: crashedThreadId,\n },\n ],\n },\n threads: {\n values: Object.entries(threads).map(([threadId, threadState]) => {\n const crashed = threadId === crashedThreadId;\n\n const thread: Thread = {\n id: threadId,\n name: threadId === '0' ? 'main' : `worker-${threadId}`,\n crashed,\n current: true,\n main: threadId === '0',\n };\n\n if (!crashed) {\n thread.stacktrace = { frames: prepareStackFrames(threadState.frames) };\n }\n\n return thread;\n }),\n },\n };\n}\n\nfunction applyScopeToEvent(event: Event, scope: ScopeData): void {\n applyScopeDataToEvent(event, scope);\n\n if (!event.contexts?.trace) {\n const { traceId, parentSpanId, propagationSpanId } = scope.propagationContext;\n event.contexts = {\n trace: {\n trace_id: traceId,\n span_id: propagationSpanId || generateSpanId(),\n parent_span_id: parentSpanId,\n },\n ...event.contexts,\n };\n }\n}\n\nasync function sendBlockEvent(crashedThreadId: string): Promise<void> {\n if (isRateLimited()) {\n return;\n }\n\n const threads = captureStackTrace<CurrentScopes, ThreadState>();\n const crashedThread = threads[crashedThreadId];\n\n if (!crashedThread) {\n log(`No thread found with ID '${crashedThreadId}'`);\n return;\n }\n\n try {\n await sendAbnormalSession(crashedThread.pollState?.session);\n } catch (error) {\n log(`Failed to send abnormal session for thread '${crashedThreadId}':`, error);\n }\n\n log('Sending event');\n\n const event: Event = {\n event_id: uuid4(),\n contexts,\n release,\n environment,\n dist,\n platform: 'node',\n level: 'error',\n tags,\n ...getExceptionAndThreads(crashedThreadId, threads),\n };\n\n const scope = crashedThread.pollState?.scope\n ? new Scope().update(crashedThread.pollState.scope).getScopeData()\n : new Scope().getScopeData();\n\n if (crashedThread?.asyncState?.isolationScope) {\n // We need to rehydrate the scope from the serialized object with properties beginning with _user, etc\n const isolationScope = Object.assign(new Scope(), crashedThread.asyncState.isolationScope).getScopeData();\n mergeScopeData(scope, isolationScope);\n }\n applyScopeToEvent(event, scope);\n\n const allDebugImages: Record<string, string> = Object.values(threads).reduce((acc, threadState) => {\n return { ...acc, ...threadState.pollState?.debugImages };\n }, {});\n\n applyDebugMeta(event, allDebugImages);\n\n const envelope = createEventEnvelope(event, dsn, sdkMetadata, tunnel);\n // Log the envelope to aid in testing\n log(JSON.stringify(envelope));\n\n await transport.send(envelope);\n await transport.flush(2000);\n}\n\nsetInterval(async () => {\n for (const [threadId, time] of Object.entries(getThreadsLastSeen())) {\n if (time > threshold) {\n if (triggeredThreads.has(threadId)) {\n continue;\n }\n\n log(`Blocked thread detected '${threadId}' last polled ${time} ms ago.`);\n triggeredThreads.add(threadId);\n\n try {\n await sendBlockEvent(threadId);\n } catch (error) {\n log(`Failed to send event for thread '${threadId}':`, error);\n }\n } else {\n triggeredThreads.delete(threadId);\n }\n }\n}, pollInterval);\n"],"names":["workerData","POLL_RATIO","getEnvelopeEndpointWithUrlEncodedAuth","makeNodeTransport","makeSession","updateSession","createSessionEnvelope","stripSentryFramesAndReverse","filenameIsInApp","normalizeUrlToBase","applyScopeDataToEvent","generateSpanId","captureStackTrace","uuid4","Scope","mergeScopeData","createEventEnvelope","getThreadsLastSeen"],"mappings":"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"}
1
+ {"version":3,"file":"event-loop-block-watchdog.js","sources":["../../src/event-loop-block-watchdog.ts"],"sourcesContent":["import { workerData } from 'node:worker_threads';\nimport type { DebugImage, Event, ScopeData, Session, StackFrame, Thread } from '@sentry/core';\nimport {\n applyScopeDataToEvent,\n createEventEnvelope,\n createSessionEnvelope,\n filenameIsInApp,\n generateSpanId,\n getEnvelopeEndpointWithUrlEncodedAuth,\n makeSession,\n mergeScopeData,\n normalizeUrlToBase,\n Scope,\n stripSentryFramesAndReverse,\n updateSession,\n uuid4,\n} from '@sentry/core';\nimport { makeNodeTransport } from '@sentry/node';\nimport { captureStackTrace, getThreadsLastSeen } from '@sentry-internal/node-native-stacktrace';\nimport type { ThreadState, WorkerStartData } from './common';\nimport { POLL_RATIO } from './common';\n\ntype CurrentScopes = {\n isolationScope: Scope;\n};\n\nconst {\n threshold,\n appRootPath,\n contexts,\n debug,\n dist,\n dsn,\n environment,\n maxEventsPerHour,\n release,\n sdkMetadata,\n staticTags: tags,\n tunnel,\n} = workerData as WorkerStartData;\n\nconst pollInterval = threshold / POLL_RATIO;\nconst triggeredThreads = new Set<string>();\n\nfunction log(...msg: unknown[]): void {\n if (debug) {\n // eslint-disable-next-line no-console\n console.log('[Sentry Event Loop Blocked Watchdog]', ...msg);\n }\n}\n\nfunction createRateLimiter(maxEventsPerHour: number): () => boolean {\n let currentHour = 0;\n let currentCount = 0;\n\n return function isRateLimited(): boolean {\n const hour = new Date().getHours();\n\n if (hour !== currentHour) {\n currentHour = hour;\n currentCount = 0;\n }\n\n if (currentCount >= maxEventsPerHour) {\n if (currentCount === maxEventsPerHour) {\n currentCount += 1;\n log(`Rate limit reached: ${currentCount} events in this hour`);\n }\n return true;\n }\n\n currentCount += 1;\n return false;\n };\n}\n\nconst url = getEnvelopeEndpointWithUrlEncodedAuth(dsn, tunnel, sdkMetadata.sdk);\nconst transport = makeNodeTransport({\n url,\n recordDroppedEvent: () => {\n //\n },\n});\nconst isRateLimited = createRateLimiter(maxEventsPerHour);\n\nasync function sendAbnormalSession(serializedSession: Session | undefined): Promise<void> {\n if (!serializedSession) {\n return;\n }\n\n log('Sending abnormal session');\n const session = makeSession(serializedSession);\n\n updateSession(session, {\n status: 'abnormal',\n abnormal_mechanism: 'anr_foreground',\n release,\n environment,\n });\n\n const envelope = createSessionEnvelope(session, dsn, sdkMetadata, tunnel);\n // Log the envelope so to aid in testing\n log(JSON.stringify(envelope));\n\n await transport.send(envelope);\n}\n\nlog('Started');\n\nfunction prepareStackFrames(stackFrames: StackFrame[] | undefined): StackFrame[] | undefined {\n if (!stackFrames) {\n return undefined;\n }\n\n // Strip Sentry frames and reverse the stack frames so they are in the correct order\n const strippedFrames = stripSentryFramesAndReverse(stackFrames);\n\n for (const frame of strippedFrames) {\n if (!frame.filename) {\n continue;\n }\n\n frame.in_app = filenameIsInApp(frame.filename);\n\n // If we have an app root path, rewrite the filenames to be relative to the app root\n if (appRootPath) {\n frame.filename = normalizeUrlToBase(frame.filename, appRootPath);\n }\n }\n\n return strippedFrames;\n}\n\nfunction stripFileProtocol(filename: string | undefined): string | undefined {\n if (!filename) {\n return undefined;\n }\n return filename.replace(/^file:\\/\\//, '');\n}\n\n// eslint-disable-next-line complexity\nfunction applyDebugMeta(event: Event, debugImages: Record<string, string>): void {\n if (Object.keys(debugImages).length === 0) {\n return;\n }\n\n const normalisedDebugImages = appRootPath ? {} : debugImages;\n if (appRootPath) {\n for (const [path, debugId] of Object.entries(debugImages)) {\n normalisedDebugImages[normalizeUrlToBase(path, appRootPath)] = debugId;\n }\n }\n\n const filenameToDebugId = new Map<string, string>();\n\n for (const exception of event.exception?.values || []) {\n for (const frame of exception.stacktrace?.frames || []) {\n const filename = stripFileProtocol(frame.abs_path || frame.filename);\n if (filename && normalisedDebugImages[filename]) {\n filenameToDebugId.set(filename, normalisedDebugImages[filename]);\n }\n }\n }\n\n for (const thread of event.threads?.values || []) {\n for (const frame of thread.stacktrace?.frames || []) {\n const filename = stripFileProtocol(frame.abs_path || frame.filename);\n if (filename && normalisedDebugImages[filename]) {\n filenameToDebugId.set(filename, normalisedDebugImages[filename]);\n }\n }\n }\n\n if (filenameToDebugId.size > 0) {\n const images: DebugImage[] = [];\n for (const [code_file, debug_id] of filenameToDebugId.entries()) {\n images.push({\n type: 'sourcemap',\n code_file,\n debug_id,\n });\n }\n event.debug_meta = { images };\n }\n}\n\nfunction getExceptionAndThreads(\n crashedThreadId: string,\n threads: ReturnType<typeof captureStackTrace<CurrentScopes, ThreadState>>,\n): Event {\n const crashedThread = threads[crashedThreadId];\n\n return {\n exception: {\n values: [\n {\n type: 'EventLoopBlocked',\n value: `Event Loop Blocked for at least ${threshold} ms`,\n stacktrace: { frames: prepareStackFrames(crashedThread?.frames) },\n // This ensures the UI doesn't say 'Crashed in' for the stack trace\n mechanism: { type: 'ANR' },\n thread_id: crashedThreadId,\n },\n ],\n },\n threads: {\n values: Object.entries(threads).map(([threadId, threadState]) => {\n const crashed = threadId === crashedThreadId;\n\n const thread: Thread = {\n id: threadId,\n name: threadId === '0' ? 'main' : `worker-${threadId}`,\n crashed,\n current: true,\n main: threadId === '0',\n };\n\n if (!crashed) {\n thread.stacktrace = { frames: prepareStackFrames(threadState.frames) };\n }\n\n return thread;\n }),\n },\n };\n}\n\nfunction applyScopeToEvent(event: Event, scope: ScopeData): void {\n applyScopeDataToEvent(event, scope);\n\n if (!event.contexts?.trace) {\n const { traceId, parentSpanId, propagationSpanId } = scope.propagationContext;\n event.contexts = {\n trace: {\n trace_id: traceId,\n span_id: propagationSpanId || generateSpanId(),\n parent_span_id: parentSpanId,\n },\n ...event.contexts,\n };\n }\n}\n\nasync function sendBlockEvent(crashedThreadId: string): Promise<void> {\n if (isRateLimited()) {\n return;\n }\n\n const threads = captureStackTrace<CurrentScopes, ThreadState>();\n const crashedThread = threads[crashedThreadId];\n\n if (!crashedThread) {\n log(`No thread found with ID '${crashedThreadId}'`);\n return;\n }\n\n try {\n await sendAbnormalSession(crashedThread.pollState?.session);\n } catch (error) {\n log(`Failed to send abnormal session for thread '${crashedThreadId}':`, error);\n }\n\n log('Sending event');\n\n const event: Event = {\n event_id: uuid4(),\n contexts,\n release,\n environment,\n dist,\n platform: 'node',\n level: 'error',\n tags,\n ...getExceptionAndThreads(crashedThreadId, threads),\n };\n\n const scope = crashedThread.pollState?.scope\n ? new Scope().update(crashedThread.pollState.scope).getScopeData()\n : new Scope().getScopeData();\n\n if (crashedThread?.asyncState?.isolationScope) {\n // We need to rehydrate the scope from the serialized object with properties beginning with _user, etc\n const isolationScope = Object.assign(new Scope(), crashedThread.asyncState.isolationScope).getScopeData();\n mergeScopeData(scope, isolationScope);\n }\n applyScopeToEvent(event, scope);\n\n const allDebugImages: Record<string, string> = Object.values(threads).reduce((acc, threadState) => {\n return { ...acc, ...threadState.pollState?.debugImages };\n }, {});\n\n applyDebugMeta(event, allDebugImages);\n\n const envelope = createEventEnvelope(event, dsn, sdkMetadata, tunnel);\n // Log the envelope to aid in testing\n log(JSON.stringify(envelope));\n\n await transport.send(envelope);\n await transport.flush(2000);\n}\n\nsetInterval(async () => {\n for (const [threadId, time] of Object.entries(getThreadsLastSeen())) {\n if (time > threshold) {\n if (triggeredThreads.has(threadId)) {\n continue;\n }\n\n log(`Blocked thread detected '${threadId}' last polled ${time} ms ago.`);\n triggeredThreads.add(threadId);\n\n try {\n await sendBlockEvent(threadId);\n } catch (error) {\n log(`Failed to send event for thread '${threadId}':`, error);\n }\n } else {\n triggeredThreads.delete(threadId);\n }\n }\n}, pollInterval);\n"],"names":["workerData","POLL_RATIO","maxEventsPerHour","isRateLimited","getEnvelopeEndpointWithUrlEncodedAuth","makeNodeTransport","makeSession","updateSession","createSessionEnvelope","stripSentryFramesAndReverse","filenameIsInApp","normalizeUrlToBase","applyScopeDataToEvent","generateSpanId","captureStackTrace","uuid4","Scope","mergeScopeData","createEventEnvelope","getThreadsLastSeen"],"mappings":"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"}
@@ -4,112 +4,70 @@ import { defineIntegration, debug, getIsolationScope, getFilenameToDebugIdMap, g
4
4
  import { registerThread, threadPoll } from '@sentry-internal/node-native-stacktrace';
5
5
  import { POLL_RATIO } from './common.js';
6
6
 
7
- const INTEGRATION_NAME = 'ThreadBlocked';
8
- const DEFAULT_THRESHOLD_MS = 1000;
9
-
7
+ const INTEGRATION_NAME = "ThreadBlocked";
8
+ const DEFAULT_THRESHOLD_MS = 1e3;
10
9
  function log(message, ...args) {
11
10
  debug.log(`[Sentry Event Loop Blocked] ${message}`, ...args);
12
11
  }
13
-
14
- /**
15
- * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup
16
- */
17
12
  async function getContexts(client) {
18
13
  let event = { message: INTEGRATION_NAME };
19
14
  const eventHint = {};
20
-
21
15
  for (const processor of client.getEventProcessors()) {
22
16
  if (event === null) break;
23
17
  event = await processor(event, eventHint);
24
18
  }
25
-
26
19
  return event?.contexts || {};
27
20
  }
28
-
29
21
  function getLocalScopeData() {
30
22
  const globalScope = getGlobalScope().getScopeData();
31
23
  const currentScope = getCurrentScope().getScopeData();
32
24
  mergeScopeData(globalScope, currentScope);
33
25
  return globalScope;
34
26
  }
35
-
36
27
  function poll(enabled, clientOptions) {
37
28
  try {
38
29
  const currentSession = getIsolationScope().getSession();
39
- // We need to copy the session object and remove the toJSON method so it can be sent to the worker
40
- // serialized without making it a SerializedSession
41
- const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;
30
+ const session = currentSession ? { ...currentSession, toJSON: void 0 } : void 0;
42
31
  const scope = getLocalScopeData();
43
- // message the worker to tell it the main event loop is still running
44
32
  threadPoll(enabled, { session, scope, debugImages: getFilenameToDebugIdMap(clientOptions.stackParser) });
45
33
  } catch {
46
- // we ignore all errors
47
34
  }
48
35
  }
49
-
50
- /**
51
- * Starts polling
52
- */
53
- function startPolling(
54
- client,
55
- integrationOptions,
56
- ) {
36
+ function startPolling(client, integrationOptions) {
57
37
  if (client.asyncLocalStorageLookup) {
58
38
  const { asyncLocalStorage, contextSymbol } = client.asyncLocalStorageLookup;
59
- registerThread({ asyncLocalStorage, stateLookup: ['_currentContext', contextSymbol] });
39
+ registerThread({ asyncLocalStorage, stateLookup: ["_currentContext", contextSymbol] });
60
40
  } else {
61
41
  registerThread();
62
42
  }
63
-
64
43
  let enabled = true;
65
-
66
44
  const initOptions = client.getOptions();
67
45
  const pollInterval = (integrationOptions.threshold || DEFAULT_THRESHOLD_MS) / POLL_RATIO;
68
-
69
- // unref so timer does not block exit
70
46
  setInterval(() => poll(enabled, initOptions), pollInterval).unref();
71
-
72
47
  return {
73
48
  start: () => {
74
49
  enabled = true;
75
50
  },
76
51
  stop: () => {
77
52
  enabled = false;
78
- // poll immediately because the timer above might not get a chance to run
79
- // before the event loop gets blocked
80
53
  poll(enabled, initOptions);
81
- },
54
+ }
82
55
  };
83
56
  }
84
-
85
- /**
86
- * Starts the worker thread that will monitor the other threads.
87
- *
88
- * This function is only called in the main thread.
89
- */
90
- async function startWorker(
91
- dsn,
92
- client,
93
- integrationOptions,
94
- ) {
57
+ async function startWorker(dsn, client, integrationOptions) {
95
58
  const contexts = await getContexts(client);
96
-
97
- // These will not be accurate if sent later from the worker thread
98
59
  delete contexts.app?.app_memory;
99
60
  delete contexts.device?.free_memory;
100
-
101
61
  const initOptions = client.getOptions();
102
-
103
62
  const sdkMetadata = client.getSdkMetadata() || {};
104
63
  if (sdkMetadata.sdk) {
105
- sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);
64
+ sdkMetadata.sdk.integrations = initOptions.integrations.map((i) => i.name);
106
65
  }
107
-
108
66
  const options = {
109
67
  debug: debug.isEnabled(),
110
68
  dsn,
111
69
  tunnel: initOptions.tunnel,
112
- environment: initOptions.environment || 'production',
70
+ environment: initOptions.environment || "production",
113
71
  release: initOptions.release,
114
72
  dist: initOptions.dist,
115
73
  sdkMetadata,
@@ -117,58 +75,44 @@ async function startWorker(
117
75
  threshold: integrationOptions.threshold || DEFAULT_THRESHOLD_MS,
118
76
  maxEventsPerHour: integrationOptions.maxEventsPerHour || 1,
119
77
  staticTags: integrationOptions.staticTags || {},
120
- contexts,
78
+ contexts
121
79
  };
122
-
123
- const worker = new Worker(new URL('./event-loop-block-watchdog.js', import.meta.url), {
80
+ const worker = new Worker(new URL("./event-loop-block-watchdog.js", import.meta.url), {
124
81
  workerData: options,
125
82
  // We don't want any Node args like --import to be passed to the worker
126
83
  execArgv: [],
127
- env: { ...process.env, NODE_OPTIONS: undefined },
84
+ env: { ...process.env, NODE_OPTIONS: void 0 }
128
85
  });
129
-
130
- process.on('exit', () => {
131
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
86
+ process.on("exit", () => {
132
87
  worker.terminate();
133
88
  });
134
-
135
- worker.once('error', (err) => {
136
- log('watchdog worker error', err);
137
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
89
+ worker.once("error", (err) => {
90
+ log("watchdog worker error", err);
138
91
  worker.terminate();
139
92
  });
140
-
141
- worker.once('exit', (code) => {
142
- log('watchdog worker exit', code);
93
+ worker.once("exit", (code) => {
94
+ log("watchdog worker exit", code);
143
95
  });
144
-
145
- // Ensure this thread can't block app exit
146
96
  worker.unref();
147
97
  }
148
-
149
98
  const _eventLoopBlockIntegration = ((options = {}) => {
150
99
  let polling;
151
-
152
100
  return {
153
101
  name: INTEGRATION_NAME,
154
102
  async afterAllSetup(client) {
155
103
  const dsn = client.getDsn();
156
-
157
104
  if (!dsn) {
158
- log('No DSN configured, skipping starting integration');
105
+ log("No DSN configured, skipping starting integration");
159
106
  return;
160
107
  }
161
-
162
- // Otel is not setup until after afterAllSetup returns.
163
108
  setImmediate(async () => {
164
109
  try {
165
110
  polling = startPolling(client, options);
166
-
167
111
  if (isMainThread) {
168
112
  await startWorker(dsn, client, options);
169
113
  }
170
114
  } catch (err) {
171
- log('Failed to start integration', err);
115
+ log("Failed to start integration", err);
172
116
  return;
173
117
  }
174
118
  });
@@ -178,64 +122,21 @@ const _eventLoopBlockIntegration = ((options = {}) => {
178
122
  },
179
123
  stop() {
180
124
  polling?.stop();
181
- },
182
- } ;
183
- }) ;
184
-
185
- /**
186
- * Monitors the Node.js event loop for blocking behavior and reports blocked events to Sentry.
187
- *
188
- * Uses a background worker thread to detect when the main thread is blocked for longer than
189
- * the configured threshold (default: 1 second).
190
- *
191
- * When instrumenting via the `--import` flag, this integration will
192
- * automatically monitor all worker threads as well.
193
- *
194
- * ```js
195
- * // instrument.mjs
196
- * import * as Sentry from '@sentry/node';
197
- * import { eventLoopBlockIntegration } from '@sentry/node-native';
198
- *
199
- * Sentry.init({
200
- * dsn: '__YOUR_DSN__',
201
- * integrations: [
202
- * eventLoopBlockIntegration({
203
- * threshold: 500, // Report blocks longer than 500ms
204
- * }),
205
- * ],
206
- * });
207
- * ```
208
- *
209
- * Start your application with:
210
- * ```bash
211
- * node --import instrument.mjs app.mjs
212
- * ```
213
- */
125
+ }
126
+ };
127
+ });
214
128
  const eventLoopBlockIntegration = defineIntegration(_eventLoopBlockIntegration);
215
-
216
- /**
217
- * Disables Event Loop Block detection for the current thread for the duration
218
- * of the callback.
219
- *
220
- * This utility function allows you to disable block detection during operations that
221
- * are expected to block the event loop, such as intensive computational tasks or
222
- * synchronous I/O operations.
223
- */
224
129
  function disableBlockDetectionForCallback(callback) {
225
- const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) ;
226
-
130
+ const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME);
227
131
  if (!integration) {
228
132
  return callback();
229
133
  }
230
-
231
134
  integration.stop();
232
-
233
135
  try {
234
136
  const result = callback();
235
137
  if (isPromise(result)) {
236
138
  return result.finally(() => integration.start());
237
139
  }
238
-
239
140
  integration.start();
240
141
  return result;
241
142
  } catch (error) {
@@ -243,34 +144,18 @@ function disableBlockDetectionForCallback(callback) {
243
144
  throw error;
244
145
  }
245
146
  }
246
-
247
- /**
248
- * Pauses the block detection integration.
249
- *
250
- * This function pauses event loop block detection for the current thread.
251
- */
252
147
  function pauseEventLoopBlockDetection() {
253
- const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) ;
254
-
148
+ const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME);
255
149
  if (!integration) {
256
150
  return;
257
151
  }
258
-
259
152
  integration.stop();
260
153
  }
261
-
262
- /**
263
- * Restarts the block detection integration.
264
- *
265
- * This function restarts event loop block detection for the current thread.
266
- */
267
154
  function restartEventLoopBlockDetection() {
268
- const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) ;
269
-
155
+ const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME);
270
156
  if (!integration) {
271
157
  return;
272
158
  }
273
-
274
159
  integration.start();
275
160
  }
276
161
 
@@ -1 +1 @@
1
- {"version":3,"file":"event-loop-block-integration.js","sources":["../../src/event-loop-block-integration.ts"],"sourcesContent":["import { isPromise } from 'node:util/types';\nimport { isMainThread, Worker } from 'node:worker_threads';\nimport type {\n ClientOptions,\n Contexts,\n DsnComponents,\n Event,\n EventHint,\n Integration,\n IntegrationFn,\n ScopeData,\n} from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getClient,\n getCurrentScope,\n getFilenameToDebugIdMap,\n getGlobalScope,\n getIsolationScope,\n mergeScopeData,\n} from '@sentry/core';\nimport type { NodeClient } from '@sentry/node';\nimport { registerThread, threadPoll } from '@sentry-internal/node-native-stacktrace';\nimport type { ThreadBlockedIntegrationOptions, WorkerStartData } from './common';\nimport { POLL_RATIO } from './common';\n\nconst INTEGRATION_NAME = 'ThreadBlocked';\nconst DEFAULT_THRESHOLD_MS = 1_000;\n\nfunction log(message: string, ...args: unknown[]): void {\n debug.log(`[Sentry Event Loop Blocked] ${message}`, ...args);\n}\n\n/**\n * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup\n */\nasync function getContexts(client: NodeClient): Promise<Contexts> {\n let event: Event | null = { message: INTEGRATION_NAME };\n const eventHint: EventHint = {};\n\n for (const processor of client.getEventProcessors()) {\n if (event === null) break;\n event = await processor(event, eventHint);\n }\n\n return event?.contexts || {};\n}\n\nfunction getLocalScopeData(): ScopeData {\n const globalScope = getGlobalScope().getScopeData();\n const currentScope = getCurrentScope().getScopeData();\n mergeScopeData(globalScope, currentScope);\n return globalScope;\n}\n\ntype IntegrationInternal = { start: () => void; stop: () => void };\n\nfunction poll(enabled: boolean, clientOptions: ClientOptions): void {\n try {\n const currentSession = getIsolationScope().getSession();\n // We need to copy the session object and remove the toJSON method so it can be sent to the worker\n // serialized without making it a SerializedSession\n const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;\n const scope = getLocalScopeData();\n // message the worker to tell it the main event loop is still running\n threadPoll(enabled, { session, scope, debugImages: getFilenameToDebugIdMap(clientOptions.stackParser) });\n } catch {\n // we ignore all errors\n }\n}\n\n/**\n * Starts polling\n */\nfunction startPolling(\n client: NodeClient,\n integrationOptions: Partial<ThreadBlockedIntegrationOptions>,\n): IntegrationInternal | undefined {\n if (client.asyncLocalStorageLookup) {\n const { asyncLocalStorage, contextSymbol } = client.asyncLocalStorageLookup;\n registerThread({ asyncLocalStorage, stateLookup: ['_currentContext', contextSymbol] });\n } else {\n registerThread();\n }\n\n let enabled = true;\n\n const initOptions = client.getOptions();\n const pollInterval = (integrationOptions.threshold || DEFAULT_THRESHOLD_MS) / POLL_RATIO;\n\n // unref so timer does not block exit\n setInterval(() => poll(enabled, initOptions), pollInterval).unref();\n\n return {\n start: () => {\n enabled = true;\n },\n stop: () => {\n enabled = false;\n // poll immediately because the timer above might not get a chance to run\n // before the event loop gets blocked\n poll(enabled, initOptions);\n },\n };\n}\n\n/**\n * Starts the worker thread that will monitor the other threads.\n *\n * This function is only called in the main thread.\n */\nasync function startWorker(\n dsn: DsnComponents,\n client: NodeClient,\n integrationOptions: Partial<ThreadBlockedIntegrationOptions>,\n): Promise<void> {\n const contexts = await getContexts(client);\n\n // These will not be accurate if sent later from the worker thread\n delete contexts.app?.app_memory;\n delete contexts.device?.free_memory;\n\n const initOptions = client.getOptions();\n\n const sdkMetadata = client.getSdkMetadata() || {};\n if (sdkMetadata.sdk) {\n sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);\n }\n\n const options: WorkerStartData = {\n debug: debug.isEnabled(),\n dsn,\n tunnel: initOptions.tunnel,\n environment: initOptions.environment || 'production',\n release: initOptions.release,\n dist: initOptions.dist,\n sdkMetadata,\n appRootPath: integrationOptions.appRootPath,\n threshold: integrationOptions.threshold || DEFAULT_THRESHOLD_MS,\n maxEventsPerHour: integrationOptions.maxEventsPerHour || 1,\n staticTags: integrationOptions.staticTags || {},\n contexts,\n };\n\n const worker = new Worker(new URL('./event-loop-block-watchdog.js', import.meta.url), {\n workerData: options,\n // We don't want any Node args like --import to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n worker.once('error', (err: Error) => {\n log('watchdog worker error', err);\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n worker.once('exit', (code: number) => {\n log('watchdog worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n}\n\nconst _eventLoopBlockIntegration = ((options: Partial<ThreadBlockedIntegrationOptions> = {}) => {\n let polling: IntegrationInternal | undefined;\n\n return {\n name: INTEGRATION_NAME,\n async afterAllSetup(client: NodeClient): Promise<void> {\n const dsn = client.getDsn();\n\n if (!dsn) {\n log('No DSN configured, skipping starting integration');\n return;\n }\n\n // Otel is not setup until after afterAllSetup returns.\n setImmediate(async () => {\n try {\n polling = startPolling(client, options);\n\n if (isMainThread) {\n await startWorker(dsn, client, options);\n }\n } catch (err) {\n log('Failed to start integration', err);\n return;\n }\n });\n },\n start() {\n polling?.start();\n },\n stop() {\n polling?.stop();\n },\n } as Integration & IntegrationInternal;\n}) satisfies IntegrationFn;\n\n/**\n * Monitors the Node.js event loop for blocking behavior and reports blocked events to Sentry.\n *\n * Uses a background worker thread to detect when the main thread is blocked for longer than\n * the configured threshold (default: 1 second).\n *\n * When instrumenting via the `--import` flag, this integration will\n * automatically monitor all worker threads as well.\n *\n * ```js\n * // instrument.mjs\n * import * as Sentry from '@sentry/node';\n * import { eventLoopBlockIntegration } from '@sentry/node-native';\n *\n * Sentry.init({\n * dsn: '__YOUR_DSN__',\n * integrations: [\n * eventLoopBlockIntegration({\n * threshold: 500, // Report blocks longer than 500ms\n * }),\n * ],\n * });\n * ```\n *\n * Start your application with:\n * ```bash\n * node --import instrument.mjs app.mjs\n * ```\n */\nexport const eventLoopBlockIntegration = defineIntegration(_eventLoopBlockIntegration);\n\nexport function disableBlockDetectionForCallback<T>(callback: () => T): T;\nexport function disableBlockDetectionForCallback<T>(callback: () => Promise<T>): Promise<T>;\n/**\n * Disables Event Loop Block detection for the current thread for the duration\n * of the callback.\n *\n * This utility function allows you to disable block detection during operations that\n * are expected to block the event loop, such as intensive computational tasks or\n * synchronous I/O operations.\n */\nexport function disableBlockDetectionForCallback<T>(callback: () => T | Promise<T>): T | Promise<T> {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as IntegrationInternal | undefined;\n\n if (!integration) {\n return callback();\n }\n\n integration.stop();\n\n try {\n const result = callback();\n if (isPromise(result)) {\n return result.finally(() => integration.start());\n }\n\n integration.start();\n return result;\n } catch (error) {\n integration.start();\n throw error;\n }\n}\n\n/**\n * Pauses the block detection integration.\n *\n * This function pauses event loop block detection for the current thread.\n */\nexport function pauseEventLoopBlockDetection(): void {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as IntegrationInternal | undefined;\n\n if (!integration) {\n return;\n }\n\n integration.stop();\n}\n\n/**\n * Restarts the block detection integration.\n *\n * This function restarts event loop block detection for the current thread.\n */\nexport function restartEventLoopBlockDetection(): void {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as IntegrationInternal | undefined;\n\n if (!integration) {\n return;\n }\n\n integration.start();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"event-loop-block-integration.js","sources":["../../src/event-loop-block-integration.ts"],"sourcesContent":["import { isPromise } from 'node:util/types';\nimport { isMainThread, Worker } from 'node:worker_threads';\nimport type {\n ClientOptions,\n Contexts,\n DsnComponents,\n Event,\n EventHint,\n Integration,\n IntegrationFn,\n ScopeData,\n} from '@sentry/core';\nimport {\n debug,\n defineIntegration,\n getClient,\n getCurrentScope,\n getFilenameToDebugIdMap,\n getGlobalScope,\n getIsolationScope,\n mergeScopeData,\n} from '@sentry/core';\nimport type { NodeClient } from '@sentry/node';\nimport { registerThread, threadPoll } from '@sentry-internal/node-native-stacktrace';\nimport type { ThreadBlockedIntegrationOptions, WorkerStartData } from './common';\nimport { POLL_RATIO } from './common';\n\nconst INTEGRATION_NAME = 'ThreadBlocked';\nconst DEFAULT_THRESHOLD_MS = 1_000;\n\nfunction log(message: string, ...args: unknown[]): void {\n debug.log(`[Sentry Event Loop Blocked] ${message}`, ...args);\n}\n\n/**\n * Gets contexts by calling all event processors. This shouldn't be called until all integrations are setup\n */\nasync function getContexts(client: NodeClient): Promise<Contexts> {\n let event: Event | null = { message: INTEGRATION_NAME };\n const eventHint: EventHint = {};\n\n for (const processor of client.getEventProcessors()) {\n if (event === null) break;\n event = await processor(event, eventHint);\n }\n\n return event?.contexts || {};\n}\n\nfunction getLocalScopeData(): ScopeData {\n const globalScope = getGlobalScope().getScopeData();\n const currentScope = getCurrentScope().getScopeData();\n mergeScopeData(globalScope, currentScope);\n return globalScope;\n}\n\ntype IntegrationInternal = { start: () => void; stop: () => void };\n\nfunction poll(enabled: boolean, clientOptions: ClientOptions): void {\n try {\n const currentSession = getIsolationScope().getSession();\n // We need to copy the session object and remove the toJSON method so it can be sent to the worker\n // serialized without making it a SerializedSession\n const session = currentSession ? { ...currentSession, toJSON: undefined } : undefined;\n const scope = getLocalScopeData();\n // message the worker to tell it the main event loop is still running\n threadPoll(enabled, { session, scope, debugImages: getFilenameToDebugIdMap(clientOptions.stackParser) });\n } catch {\n // we ignore all errors\n }\n}\n\n/**\n * Starts polling\n */\nfunction startPolling(\n client: NodeClient,\n integrationOptions: Partial<ThreadBlockedIntegrationOptions>,\n): IntegrationInternal | undefined {\n if (client.asyncLocalStorageLookup) {\n const { asyncLocalStorage, contextSymbol } = client.asyncLocalStorageLookup;\n registerThread({ asyncLocalStorage, stateLookup: ['_currentContext', contextSymbol] });\n } else {\n registerThread();\n }\n\n let enabled = true;\n\n const initOptions = client.getOptions();\n const pollInterval = (integrationOptions.threshold || DEFAULT_THRESHOLD_MS) / POLL_RATIO;\n\n // unref so timer does not block exit\n setInterval(() => poll(enabled, initOptions), pollInterval).unref();\n\n return {\n start: () => {\n enabled = true;\n },\n stop: () => {\n enabled = false;\n // poll immediately because the timer above might not get a chance to run\n // before the event loop gets blocked\n poll(enabled, initOptions);\n },\n };\n}\n\n/**\n * Starts the worker thread that will monitor the other threads.\n *\n * This function is only called in the main thread.\n */\nasync function startWorker(\n dsn: DsnComponents,\n client: NodeClient,\n integrationOptions: Partial<ThreadBlockedIntegrationOptions>,\n): Promise<void> {\n const contexts = await getContexts(client);\n\n // These will not be accurate if sent later from the worker thread\n delete contexts.app?.app_memory;\n delete contexts.device?.free_memory;\n\n const initOptions = client.getOptions();\n\n const sdkMetadata = client.getSdkMetadata() || {};\n if (sdkMetadata.sdk) {\n sdkMetadata.sdk.integrations = initOptions.integrations.map(i => i.name);\n }\n\n const options: WorkerStartData = {\n debug: debug.isEnabled(),\n dsn,\n tunnel: initOptions.tunnel,\n environment: initOptions.environment || 'production',\n release: initOptions.release,\n dist: initOptions.dist,\n sdkMetadata,\n appRootPath: integrationOptions.appRootPath,\n threshold: integrationOptions.threshold || DEFAULT_THRESHOLD_MS,\n maxEventsPerHour: integrationOptions.maxEventsPerHour || 1,\n staticTags: integrationOptions.staticTags || {},\n contexts,\n };\n\n const worker = new Worker(new URL('./event-loop-block-watchdog.js', import.meta.url), {\n workerData: options,\n // We don't want any Node args like --import to be passed to the worker\n execArgv: [],\n env: { ...process.env, NODE_OPTIONS: undefined },\n });\n\n process.on('exit', () => {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n worker.once('error', (err: Error) => {\n log('watchdog worker error', err);\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n worker.terminate();\n });\n\n worker.once('exit', (code: number) => {\n log('watchdog worker exit', code);\n });\n\n // Ensure this thread can't block app exit\n worker.unref();\n}\n\nconst _eventLoopBlockIntegration = ((options: Partial<ThreadBlockedIntegrationOptions> = {}) => {\n let polling: IntegrationInternal | undefined;\n\n return {\n name: INTEGRATION_NAME,\n async afterAllSetup(client: NodeClient): Promise<void> {\n const dsn = client.getDsn();\n\n if (!dsn) {\n log('No DSN configured, skipping starting integration');\n return;\n }\n\n // Otel is not setup until after afterAllSetup returns.\n setImmediate(async () => {\n try {\n polling = startPolling(client, options);\n\n if (isMainThread) {\n await startWorker(dsn, client, options);\n }\n } catch (err) {\n log('Failed to start integration', err);\n return;\n }\n });\n },\n start() {\n polling?.start();\n },\n stop() {\n polling?.stop();\n },\n } as Integration & IntegrationInternal;\n}) satisfies IntegrationFn;\n\n/**\n * Monitors the Node.js event loop for blocking behavior and reports blocked events to Sentry.\n *\n * Uses a background worker thread to detect when the main thread is blocked for longer than\n * the configured threshold (default: 1 second).\n *\n * When instrumenting via the `--import` flag, this integration will\n * automatically monitor all worker threads as well.\n *\n * ```js\n * // instrument.mjs\n * import * as Sentry from '@sentry/node';\n * import { eventLoopBlockIntegration } from '@sentry/node-native';\n *\n * Sentry.init({\n * dsn: '__YOUR_DSN__',\n * integrations: [\n * eventLoopBlockIntegration({\n * threshold: 500, // Report blocks longer than 500ms\n * }),\n * ],\n * });\n * ```\n *\n * Start your application with:\n * ```bash\n * node --import instrument.mjs app.mjs\n * ```\n */\nexport const eventLoopBlockIntegration = defineIntegration(_eventLoopBlockIntegration);\n\nexport function disableBlockDetectionForCallback<T>(callback: () => T): T;\nexport function disableBlockDetectionForCallback<T>(callback: () => Promise<T>): Promise<T>;\n/**\n * Disables Event Loop Block detection for the current thread for the duration\n * of the callback.\n *\n * This utility function allows you to disable block detection during operations that\n * are expected to block the event loop, such as intensive computational tasks or\n * synchronous I/O operations.\n */\nexport function disableBlockDetectionForCallback<T>(callback: () => T | Promise<T>): T | Promise<T> {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as IntegrationInternal | undefined;\n\n if (!integration) {\n return callback();\n }\n\n integration.stop();\n\n try {\n const result = callback();\n if (isPromise(result)) {\n return result.finally(() => integration.start());\n }\n\n integration.start();\n return result;\n } catch (error) {\n integration.start();\n throw error;\n }\n}\n\n/**\n * Pauses the block detection integration.\n *\n * This function pauses event loop block detection for the current thread.\n */\nexport function pauseEventLoopBlockDetection(): void {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as IntegrationInternal | undefined;\n\n if (!integration) {\n return;\n }\n\n integration.stop();\n}\n\n/**\n * Restarts the block detection integration.\n *\n * This function restarts event loop block detection for the current thread.\n */\nexport function restartEventLoopBlockDetection(): void {\n const integration = getClient()?.getIntegrationByName(INTEGRATION_NAME) as IntegrationInternal | undefined;\n\n if (!integration) {\n return;\n }\n\n integration.start();\n}\n"],"names":[],"mappings":";;;;;;AA2BA,MAAM,gBAAA,GAAmB,eAAA;AACzB,MAAM,oBAAA,GAAuB,GAAA;AAE7B,SAAS,GAAA,CAAI,YAAoB,IAAA,EAAuB;AACtD,EAAA,KAAA,CAAM,GAAA,CAAI,CAAA,4BAAA,EAA+B,OAAO,CAAA,CAAA,EAAI,GAAG,IAAI,CAAA;AAC7D;AAKA,eAAe,YAAY,MAAA,EAAuC;AAChE,EAAA,IAAI,KAAA,GAAsB,EAAE,OAAA,EAAS,gBAAA,EAAiB;AACtD,EAAA,MAAM,YAAuB,EAAC;AAE9B,EAAA,KAAA,MAAW,SAAA,IAAa,MAAA,CAAO,kBAAA,EAAmB,EAAG;AACnD,IAAA,IAAI,UAAU,IAAA,EAAM;AACpB,IAAA,KAAA,GAAQ,MAAM,SAAA,CAAU,KAAA,EAAO,SAAS,CAAA;AAAA,EAC1C;AAEA,EAAA,OAAO,KAAA,EAAO,YAAY,EAAC;AAC7B;AAEA,SAAS,iBAAA,GAA+B;AACtC,EAAA,MAAM,WAAA,GAAc,cAAA,EAAe,CAAE,YAAA,EAAa;AAClD,EAAA,MAAM,YAAA,GAAe,eAAA,EAAgB,CAAE,YAAA,EAAa;AACpD,EAAA,cAAA,CAAe,aAAa,YAAY,CAAA;AACxC,EAAA,OAAO,WAAA;AACT;AAIA,SAAS,IAAA,CAAK,SAAkB,aAAA,EAAoC;AAClE,EAAA,IAAI;AACF,IAAA,MAAM,cAAA,GAAiB,iBAAA,EAAkB,CAAE,UAAA,EAAW;AAGtD,IAAA,MAAM,UAAU,cAAA,GAAiB,EAAE,GAAG,cAAA,EAAgB,MAAA,EAAQ,QAAU,GAAI,KAAA,CAAA;AAC5E,IAAA,MAAM,QAAQ,iBAAA,EAAkB;AAEhC,IAAA,UAAA,CAAW,OAAA,EAAS,EAAE,OAAA,EAAS,KAAA,EAAO,aAAa,uBAAA,CAAwB,aAAA,CAAc,WAAW,CAAA,EAAG,CAAA;AAAA,EACzG,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAKA,SAAS,YAAA,CACP,QACA,kBAAA,EACiC;AACjC,EAAA,IAAI,OAAO,uBAAA,EAAyB;AAClC,IAAA,MAAM,EAAE,iBAAA,EAAmB,aAAA,EAAc,GAAI,MAAA,CAAO,uBAAA;AACpD,IAAA,cAAA,CAAe,EAAE,iBAAA,EAAmB,WAAA,EAAa,CAAC,iBAAA,EAAmB,aAAa,GAAG,CAAA;AAAA,EACvF,CAAA,MAAO;AACL,IAAA,cAAA,EAAe;AAAA,EACjB;AAEA,EAAA,IAAI,OAAA,GAAU,IAAA;AAEd,EAAA,MAAM,WAAA,GAAc,OAAO,UAAA,EAAW;AACtC,EAAA,MAAM,YAAA,GAAA,CAAgB,kBAAA,CAAmB,SAAA,IAAa,oBAAA,IAAwB,UAAA;AAG9E,EAAA,WAAA,CAAY,MAAM,IAAA,CAAK,OAAA,EAAS,WAAW,CAAA,EAAG,YAAY,EAAE,KAAA,EAAM;AAElE,EAAA,OAAO;AAAA,IACL,OAAO,MAAM;AACX,MAAA,OAAA,GAAU,IAAA;AAAA,IACZ,CAAA;AAAA,IACA,MAAM,MAAM;AACV,MAAA,OAAA,GAAU,KAAA;AAGV,MAAA,IAAA,CAAK,SAAS,WAAW,CAAA;AAAA,IAC3B;AAAA,GACF;AACF;AAOA,eAAe,WAAA,CACb,GAAA,EACA,MAAA,EACA,kBAAA,EACe;AACf,EAAA,MAAM,QAAA,GAAW,MAAM,WAAA,CAAY,MAAM,CAAA;AAGzC,EAAA,OAAO,SAAS,GAAA,EAAK,UAAA;AACrB,EAAA,OAAO,SAAS,MAAA,EAAQ,WAAA;AAExB,EAAA,MAAM,WAAA,GAAc,OAAO,UAAA,EAAW;AAEtC,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,cAAA,EAAe,IAAK,EAAC;AAChD,EAAA,IAAI,YAAY,GAAA,EAAK;AACnB,IAAA,WAAA,CAAY,IAAI,YAAA,GAAe,WAAA,CAAY,aAAa,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,IAAI,CAAA;AAAA,EACzE;AAEA,EAAA,MAAM,OAAA,GAA2B;AAAA,IAC/B,KAAA,EAAO,MAAM,SAAA,EAAU;AAAA,IACvB,GAAA;AAAA,IACA,QAAQ,WAAA,CAAY,MAAA;AAAA,IACpB,WAAA,EAAa,YAAY,WAAA,IAAe,YAAA;AAAA,IACxC,SAAS,WAAA,CAAY,OAAA;AAAA,IACrB,MAAM,WAAA,CAAY,IAAA;AAAA,IAClB,WAAA;AAAA,IACA,aAAa,kBAAA,CAAmB,WAAA;AAAA,IAChC,SAAA,EAAW,mBAAmB,SAAA,IAAa,oBAAA;AAAA,IAC3C,gBAAA,EAAkB,mBAAmB,gBAAA,IAAoB,CAAA;AAAA,IACzD,UAAA,EAAY,kBAAA,CAAmB,UAAA,IAAc,EAAC;AAAA,IAC9C;AAAA,GACF;AAEA,EAAA,MAAM,MAAA,GAAS,IAAI,MAAA,CAAO,IAAI,IAAI,gCAAA,EAAkC,MAAA,CAAA,IAAA,CAAY,GAAG,CAAA,EAAG;AAAA,IACpF,UAAA,EAAY,OAAA;AAAA;AAAA,IAEZ,UAAU,EAAC;AAAA,IACX,KAAK,EAAE,GAAG,OAAA,CAAQ,GAAA,EAAK,cAAc,MAAA;AAAU,GAChD,CAAA;AAED,EAAA,OAAA,CAAQ,EAAA,CAAG,QAAQ,MAAM;AAEvB,IAAA,MAAA,CAAO,SAAA,EAAU;AAAA,EACnB,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,IAAA,CAAK,OAAA,EAAS,CAAC,GAAA,KAAe;AACnC,IAAA,GAAA,CAAI,yBAAyB,GAAG,CAAA;AAEhC,IAAA,MAAA,CAAO,SAAA,EAAU;AAAA,EACnB,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,IAAA,CAAK,MAAA,EAAQ,CAAC,IAAA,KAAiB;AACpC,IAAA,GAAA,CAAI,wBAAwB,IAAI,CAAA;AAAA,EAClC,CAAC,CAAA;AAGD,EAAA,MAAA,CAAO,KAAA,EAAM;AACf;AAEA,MAAM,0BAAA,IAA8B,CAAC,OAAA,GAAoD,EAAC,KAAM;AAC9F,EAAA,IAAI,OAAA;AAEJ,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,gBAAA;AAAA,IACN,MAAM,cAAc,MAAA,EAAmC;AACrD,MAAA,MAAM,GAAA,GAAM,OAAO,MAAA,EAAO;AAE1B,MAAA,IAAI,CAAC,GAAA,EAAK;AACR,QAAA,GAAA,CAAI,kDAAkD,CAAA;AACtD,QAAA;AAAA,MACF;AAGA,MAAA,YAAA,CAAa,YAAY;AACvB,QAAA,IAAI;AACF,UAAA,OAAA,GAAU,YAAA,CAAa,QAAQ,OAAO,CAAA;AAEtC,UAAA,IAAI,YAAA,EAAc;AAChB,YAAA,MAAM,WAAA,CAAY,GAAA,EAAK,MAAA,EAAQ,OAAO,CAAA;AAAA,UACxC;AAAA,QACF,SAAS,GAAA,EAAK;AACZ,UAAA,GAAA,CAAI,+BAA+B,GAAG,CAAA;AACtC,UAAA;AAAA,QACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH,CAAA;AAAA,IACA,KAAA,GAAQ;AACN,MAAA,OAAA,EAAS,KAAA,EAAM;AAAA,IACjB,CAAA;AAAA,IACA,IAAA,GAAO;AACL,MAAA,OAAA,EAAS,IAAA,EAAK;AAAA,IAChB;AAAA,GACF;AACF,CAAA,CAAA;AA+BO,MAAM,yBAAA,GAA4B,kBAAkB,0BAA0B;AAY9E,SAAS,iCAAoC,QAAA,EAAgD;AAClG,EAAA,MAAM,WAAA,GAAc,SAAA,EAAU,EAAG,oBAAA,CAAqB,gBAAgB,CAAA;AAEtE,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,OAAO,QAAA,EAAS;AAAA,EAClB;AAEA,EAAA,WAAA,CAAY,IAAA,EAAK;AAEjB,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,QAAA,EAAS;AACxB,IAAA,IAAI,SAAA,CAAU,MAAM,CAAA,EAAG;AACrB,MAAA,OAAO,MAAA,CAAO,OAAA,CAAQ,MAAM,WAAA,CAAY,OAAO,CAAA;AAAA,IACjD;AAEA,IAAA,WAAA,CAAY,KAAA,EAAM;AAClB,IAAA,OAAO,MAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,WAAA,CAAY,KAAA,EAAM;AAClB,IAAA,MAAM,KAAA;AAAA,EACR;AACF;AAOO,SAAS,4BAAA,GAAqC;AACnD,EAAA,MAAM,WAAA,GAAc,SAAA,EAAU,EAAG,oBAAA,CAAqB,gBAAgB,CAAA;AAEtE,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA;AAAA,EACF;AAEA,EAAA,WAAA,CAAY,IAAA,EAAK;AACnB;AAOO,SAAS,8BAAA,GAAuC;AACrD,EAAA,MAAM,WAAA,GAAc,SAAA,EAAU,EAAG,oBAAA,CAAqB,gBAAgB,CAAA;AAEtE,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA;AAAA,EACF;AAEA,EAAA,WAAA,CAAY,KAAA,EAAM;AACpB;;;;"}