Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/astro/src/server/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
filterCollectedUrl,
filterCollectedUrlQuery,
} from '@sentry/core';
import { flushIfServerless } from '@sentry/core/server';
import { flushIfServerless } from '@sentry/server-utils';
import {
captureException,
continueTrace,
Expand Down
3 changes: 2 additions & 1 deletion packages/cloudflare/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ export {
withStreamedSpan,
spanStreamingIntegration,
} from '@sentry/core';
export { instrumentPostgresJsSql, trpcMiddleware, wrapMcpServerWithSentry } from '@sentry/core/server';
export { instrumentPostgresJsSql, wrapMcpServerWithSentry } from '@sentry/core/server';
export { trpcMiddleware } from '@sentry/server-utils';

export { withSentry } from './withSentry';
export { defineCloudflareOptions } from './defineCloudflareOptions';
Expand Down
6 changes: 1 addition & 5 deletions packages/core/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,9 @@
export type { ServerRuntimeClientOptions } from './server-runtime-client';
export { ServerRuntimeClient } from './server-runtime-client';
export type { ServerRuntimeOptions } from './types/options';
export { trpcMiddleware } from './trpc';
export { wrapMcpServerWithSentry } from './integrations/mcp-server';
export { isNodeEnv, loadModule } from './utils/node';
Comment thread
cursor[bot] marked this conversation as resolved.
export { isNodeEnv } from './utils/node';
export { filenameIsInApp, node, nodeStackLineParser } from './utils/node-stack-trace';
export { vercelWaitUntil } from './utils/vercelWaitUntil';
export { flushIfServerless } from './utils/flushIfServerless';
export { callFrameToStackFrame, watchdogTimer } from './utils/anr';
export { safeUnref as _INTERNAL_safeUnref } from './utils/timer';
/* oxlint-disable typescript/no-deprecated -- deprecated Express exports, kept until the next major */
export { patchExpressModule } from './integrations/express/index';
Expand Down
47 changes: 0 additions & 47 deletions packages/core/src/utils/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,50 +18,3 @@ export function isNodeEnv(): boolean {
Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'
);
}

/**
* Requires a module which is protected against bundler minification.
*
* @param request The module path to resolve
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function dynamicRequire(mod: any, request: string): any {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return mod.require(request);
}

/**
* Helper for dynamically loading module that should work with linked dependencies.
* The problem is that we _should_ be using `require(require.resolve(moduleName, { paths: [cwd()] }))`
* However it's _not possible_ to do that with Webpack, as it has to know all the dependencies during
* build time. `require.resolve` is also not available in any other way, so we cannot create,
* a fake helper like we do with `dynamicRequire`.
*
* We always prefer to use local package, thus the value is not returned early from each `try/catch` block.
* That is to mimic the behavior of `require.resolve` exactly.
*
* @param moduleName module name to require
* @param existingModule module to use for requiring
* @returns possibly required module
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function loadModule<T>(moduleName: string, existingModule: any = module): T | undefined {
let mod: T | undefined;

try {
mod = dynamicRequire(existingModule, moduleName);
} catch {
// no-empty
}

if (!mod) {
try {
const { cwd } = dynamicRequire(existingModule, 'process');
mod = dynamicRequire(existingModule, `${cwd()}/node_modules/${moduleName}`) as T;
} catch {
// no-empty
}
}

return mod;
}
2 changes: 1 addition & 1 deletion packages/nextjs/src/common/utils/responseEnd.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Span } from '@sentry/core';
import { debug, fill, flush, GLOBAL_OBJ, setHttpStatus } from '@sentry/core';
import { vercelWaitUntil } from '@sentry/core/server';
import { vercelWaitUntil } from '@sentry/server-utils';
import type { ServerResponse } from 'http';
import { DEBUG_BUILD } from '../debug-build';
import type { ResponseEndMethod, WrappedResponseEndMethod } from '../types';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { createSentryBuildPluginManager as createSentryBuildPluginManagerType } from '@sentry/bundler-plugins/core';
import { loadModule } from '@sentry/core/server';
import { loadModule } from '@sentry/server-utils';
import * as fs from 'fs';
import * as path from 'path';
import { getBuildLogger } from './buildLogger';
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/src/config/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/* eslint-disable max-lines */

import { debug, escapeStringForRegex, parseSemver } from '@sentry/core';
import { loadModule } from '@sentry/core/server';
import { loadModule } from '@sentry/server-utils';
import * as fs from 'fs';
import { createRequire } from 'module';
import * as path from 'path';
Expand Down
18 changes: 7 additions & 11 deletions packages/nextjs/test/common/utils/responseEnd.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,9 @@ vi.mock('@sentry/core', async () => {
};
});

vi.mock('@sentry/core/server', async () => {
const actual = await vi.importActual('@sentry/core/server');
return {
...actual,
vercelWaitUntil: vi.fn(),
};
});
vi.mock('@sentry/server-utils', () => ({
vercelWaitUntil: vi.fn(),
}));

describe('responseEnd utils', () => {
beforeEach(() => {
Expand Down Expand Up @@ -49,12 +45,12 @@ describe('responseEnd utils', () => {
expect(cfWaitUntilMock).toHaveBeenCalledTimes(1);

// Should not call vercelWaitUntil when Cloudflare is available
const { vercelWaitUntil } = await import('@sentry/core/server');
const { vercelWaitUntil } = await import('@sentry/server-utils');
expect(vercelWaitUntil).not.toHaveBeenCalled();
});

it('should use vercelWaitUntil when Cloudflare context is not available', async () => {
const { vercelWaitUntil } = await import('@sentry/core/server');
const { vercelWaitUntil } = await import('@sentry/server-utils');
const testTask = Promise.resolve('test');

waitUntil(testTask);
Expand Down Expand Up @@ -87,12 +83,12 @@ describe('responseEnd utils', () => {
expect(cfWaitUntilMock).toHaveBeenCalledTimes(1);

// Should not use Vercel
const { vercelWaitUntil } = await import('@sentry/core/server');
const { vercelWaitUntil } = await import('@sentry/server-utils');
expect(vercelWaitUntil).not.toHaveBeenCalled();
});

it('should handle errors gracefully when waitUntil is called with a rejected promise', async () => {
const { vercelWaitUntil } = await import('@sentry/core/server');
const { vercelWaitUntil } = await import('@sentry/server-utils');
const testTask = Promise.reject(new Error('test error'));

// Should not throw synchronously
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadModule } from '@sentry/core/server';
import { loadModule } from '@sentry/server-utils';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
Expand All @@ -11,7 +11,7 @@ import {
} from '../../src/config/handleRunAfterProductionCompile';
import type { SentryBuildOptions } from '../../src/config/types';

vi.mock('@sentry/core/server', () => ({
vi.mock('@sentry/server-utils', () => ({
loadModule: vi.fn(),
}));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// mock helper functions not tested directly in this file
import '../mocks';
import * as coreServer from '@sentry/core/server';
import * as serverUtils from '@sentry/server-utils';
import { describe, expect, it, vi } from 'vitest';
import * as getBuildPluginOptionsModule from '../../../src/config/getBuildPluginOptions';
import {
Expand All @@ -24,7 +24,7 @@ vi.mock('@sentry/server-utils/orchestrion/webpack', async importOriginal => ({

describe('constructWebpackConfigFunction()', () => {
it('includes expected properties', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -62,7 +62,7 @@ describe('constructWebpackConfigFunction()', () => {

it('automatically enables deleteSourcemapsAfterUpload for client builds when not explicitly set', async () => {
const getBuildPluginOptionsSpy = vi.spyOn(getBuildPluginOptionsModule, 'getBuildPluginOptions');
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -116,7 +116,7 @@ describe('constructWebpackConfigFunction()', () => {

it('passes useRunAfterProductionCompileHook to getBuildPluginOptions when enabled', async () => {
const getBuildPluginOptionsSpy = vi.spyOn(getBuildPluginOptionsModule, 'getBuildPluginOptions');
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand All @@ -142,7 +142,7 @@ describe('constructWebpackConfigFunction()', () => {

it('passes useRunAfterProductionCompileHook to getBuildPluginOptions when disabled', async () => {
const getBuildPluginOptionsSpy = vi.spyOn(getBuildPluginOptionsModule, 'getBuildPluginOptions');
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand All @@ -168,7 +168,7 @@ describe('constructWebpackConfigFunction()', () => {

it('passes useRunAfterProductionCompileHook as undefined when not specified', async () => {
const getBuildPluginOptionsSpy = vi.spyOn(getBuildPluginOptionsModule, 'getBuildPluginOptions');
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -230,7 +230,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('uses `hidden-source-map` as `devtool` value for client-side builds', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -294,7 +294,7 @@ describe('constructWebpackConfigFunction()', () => {

describe('treeshaking flags', () => {
it('does not add DefinePlugin when treeshake option is not set', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -322,7 +322,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('does not add DefinePlugin when treeshake option is empty object', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -354,7 +354,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('adds __SENTRY_DEBUG__ flag when debugLogging is true', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -382,7 +382,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('adds __SENTRY_TRACING__ flag when tracing is true', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -410,7 +410,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('adds __RRWEB_EXCLUDE_IFRAME__ flag when excludeReplayIframe is true', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -439,7 +439,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('adds __RRWEB_EXCLUDE_SHADOW_DOM__ flag when excludeReplayShadowDOM is true', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -468,7 +468,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('adds __SENTRY_EXCLUDE_REPLAY_WORKER__ flag when excludeReplayCompressionWorker is true', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -498,7 +498,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('adds all flags when all treeshake options are enabled', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -544,7 +544,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('does not add flags when treeshake options are false', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -582,7 +582,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('works for client builds', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -617,7 +617,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('works for edge builds', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down Expand Up @@ -653,7 +653,7 @@ describe('constructWebpackConfigFunction()', () => {
});

it('only adds flags for enabled options', async () => {
vi.spyOn(coreServer, 'loadModule').mockImplementation(() => ({
vi.spyOn(serverUtils, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
Expand Down
2 changes: 1 addition & 1 deletion packages/nitro/src/runtime/hooks/captureErrorHook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { captureException, getClient, parseUrl } from '@sentry/core';
import { flushIfServerless } from '@sentry/core/server';
import { flushIfServerless } from '@sentry/server-utils';
import { HTTPError } from 'h3';
import type { CapturedErrorContext } from 'nitro/types';

Expand Down
2 changes: 1 addition & 1 deletion packages/nitro/src/runtime/hooks/captureStorageEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
startInactiveSpan,
} from '@sentry/core';
import { flushIfServerless } from '@sentry/core/server';
import { flushIfServerless } from '@sentry/server-utils';
import { bindTracingChannelToSpan } from '@sentry/server-utils';
import type { TraceContext } from 'unstorage/tracing';

Expand Down
16 changes: 6 additions & 10 deletions packages/nitro/test/runtime/hooks/captureErrorHook.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as SentryCore from '@sentry/core';
import * as SentryCoreServer from '@sentry/core/server';
import * as serverUtils from '@sentry/server-utils';
import { HTTPError } from 'h3';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { captureErrorHook } from '../../../src/runtime/hooks/captureErrorHook';
Expand All @@ -16,13 +16,9 @@ vi.mock('@sentry/core', async importOriginal => {
};
});

vi.mock('@sentry/core/server', async importOriginal => {
const mod = await importOriginal();
return {
...(mod as any),
flushIfServerless: vi.fn(),
};
});
vi.mock('@sentry/server-utils', () => ({
flushIfServerless: vi.fn(),
}));

describe('captureErrorHook', () => {
const mockErrorContext = {
Expand All @@ -36,7 +32,7 @@ describe('captureErrorHook', () => {
(SentryCore.getClient as any).mockReturnValue({
getOptions: () => ({}),
});
(SentryCoreServer.flushIfServerless as any).mockResolvedValue(undefined);
(serverUtils.flushIfServerless as any).mockResolvedValue(undefined);
});

it('should capture regular errors', async () => {
Expand Down Expand Up @@ -115,7 +111,7 @@ describe('captureErrorHook', () => {

await captureErrorHook(error, mockErrorContext);

expect(SentryCoreServer.flushIfServerless).toHaveBeenCalled();
expect(serverUtils.flushIfServerless).toHaveBeenCalled();
});

it('should handle missing event in error context', async () => {
Expand Down
Loading
Loading