Skip to content

Commit 06b3455

Browse files
authored
fix(node): Handle node build without inspector in LocalVariables integration (#6780)
1 parent 81ef0ca commit 06b3455

File tree

2 files changed

+54
-12
lines changed

2 files changed

+54
-12
lines changed

packages/node/src/integrations/localvariables.ts

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import type {
88
StackFrame,
99
StackParser,
1010
} from '@sentry/types';
11-
import type { Debugger, InspectorNotification, Runtime } from 'inspector';
12-
import { Session } from 'inspector';
11+
import type { Debugger, InspectorNotification, Runtime, Session } from 'inspector';
1312
import { LRUMap } from 'lru_map';
1413

1514
export interface DebugSession {
@@ -28,14 +27,24 @@ export interface DebugSession {
2827
* https://nodejs.org/docs/latest-v19.x/api/inspector.html#promises-api
2928
* https://nodejs.org/docs/latest-v14.x/api/inspector.html
3029
*/
31-
class AsyncSession extends Session implements DebugSession {
30+
class AsyncSession implements DebugSession {
31+
private readonly _session: Session;
32+
33+
/** Throws is inspector API is not available */
34+
public constructor() {
35+
// Node can be build without inspector support so this can throw
36+
// eslint-disable-next-line @typescript-eslint/no-var-requires
37+
const { Session } = require('inspector');
38+
this._session = new Session();
39+
}
40+
3241
/** @inheritdoc */
3342
public configureAndConnect(onPause: (message: InspectorNotification<Debugger.PausedEventDataType>) => void): void {
34-
this.connect();
35-
this.on('Debugger.paused', onPause);
36-
this.post('Debugger.enable');
43+
this._session.connect();
44+
this._session.on('Debugger.paused', onPause);
45+
this._session.post('Debugger.enable');
3746
// We only want to pause on uncaught exceptions
38-
this.post('Debugger.setPauseOnExceptions', { state: 'uncaught' });
47+
this._session.post('Debugger.setPauseOnExceptions', { state: 'uncaught' });
3948
}
4049

4150
/** @inheritdoc */
@@ -61,7 +70,7 @@ class AsyncSession extends Session implements DebugSession {
6170
*/
6271
private _getProperties(objectId: string): Promise<Runtime.PropertyDescriptor[]> {
6372
return new Promise((resolve, reject) => {
64-
this.post(
73+
this._session.post(
6574
'Runtime.getProperties',
6675
{
6776
objectId,
@@ -103,6 +112,18 @@ class AsyncSession extends Session implements DebugSession {
103112
}
104113
}
105114

115+
/**
116+
* When using Vercel pkg, the inspector module is not available.
117+
* https://github.com/getsentry/sentry-javascript/issues/6769
118+
*/
119+
function tryNewAsyncSession(): AsyncSession | undefined {
120+
try {
121+
return new AsyncSession();
122+
} catch (e) {
123+
return undefined;
124+
}
125+
}
126+
106127
// Add types for the exception event data
107128
type PausedExceptionEvent = Debugger.PausedEventDataType & {
108129
data: {
@@ -162,7 +183,10 @@ export class LocalVariables implements Integration {
162183

163184
private readonly _cachedFrames: LRUMap<string, Promise<FrameVariables[]>> = new LRUMap(20);
164185

165-
public constructor(_options: Options = {}, private readonly _session: DebugSession = new AsyncSession()) {}
186+
public constructor(
187+
_options: Options = {},
188+
private readonly _session: DebugSession | undefined = tryNewAsyncSession(),
189+
) {}
166190

167191
/**
168192
* @inheritDoc
@@ -176,7 +200,7 @@ export class LocalVariables implements Integration {
176200
addGlobalEventProcessor: (callback: EventProcessor) => void,
177201
clientOptions: ClientOptions | undefined,
178202
): void {
179-
if (clientOptions?._experiments?.includeStackLocals) {
203+
if (this._session && clientOptions?._experiments?.includeStackLocals) {
180204
this._session.configureAndConnect(ev =>
181205
this._handlePaused(clientOptions.stackParser, ev as InspectorNotification<PausedExceptionEvent>),
182206
);
@@ -212,7 +236,7 @@ export class LocalVariables implements Integration {
212236
return { function: fn };
213237
}
214238

215-
const vars = await this._session.getLocalVariables(localScope.object.objectId);
239+
const vars = await this._session?.getLocalVariables(localScope.object.objectId);
216240

217241
return { function: fn, vars };
218242
});

packages/node/test/integrations/localvariables.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,25 @@ describe('LocalVariables', () => {
244244
expect(eventProcessor).toBeUndefined();
245245
});
246246

247-
it.only('Should cache identical uncaught exception events', async () => {
247+
it('Should not initialize when inspector not loaded', async () => {
248+
expect.assertions(1);
249+
250+
const localVariables = new LocalVariables({}, undefined);
251+
const options = getDefaultNodeClientOptions({
252+
stackParser: defaultStackParser,
253+
_experiments: { includeStackLocals: false },
254+
});
255+
256+
let eventProcessor: EventProcessor | undefined;
257+
258+
(localVariables as unknown as LocalVariablesPrivate)._setup(callback => {
259+
eventProcessor = callback;
260+
}, options);
261+
262+
expect(eventProcessor).toBeUndefined();
263+
});
264+
265+
it('Should cache identical uncaught exception events', async () => {
248266
expect.assertions(1);
249267

250268
const session = new MockDebugSession({

0 commit comments

Comments
 (0)