diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index c75ffd6ed968..3912546a772f 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -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, diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index d114590262bf..4c0ac2123e04 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -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'; diff --git a/packages/core/src/server.ts b/packages/core/src/server.ts index 2d2d76127f6a..a391b2de003d 100644 --- a/packages/core/src/server.ts +++ b/packages/core/src/server.ts @@ -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'; +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'; diff --git a/packages/core/src/utils/node.ts b/packages/core/src/utils/node.ts index 6060700c2b03..80704595213e 100644 --- a/packages/core/src/utils/node.ts +++ b/packages/core/src/utils/node.ts @@ -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(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; -} diff --git a/packages/nextjs/src/common/utils/responseEnd.ts b/packages/nextjs/src/common/utils/responseEnd.ts index 31e3fa698433..e8ae35f37696 100644 --- a/packages/nextjs/src/common/utils/responseEnd.ts +++ b/packages/nextjs/src/common/utils/responseEnd.ts @@ -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'; diff --git a/packages/nextjs/src/config/handleRunAfterProductionCompile.ts b/packages/nextjs/src/config/handleRunAfterProductionCompile.ts index ae4b0a91d84a..c742aba3dde1 100644 --- a/packages/nextjs/src/config/handleRunAfterProductionCompile.ts +++ b/packages/nextjs/src/config/handleRunAfterProductionCompile.ts @@ -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'; diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 9f594973b41c..5e005ac1d27a 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -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'; diff --git a/packages/nextjs/test/common/utils/responseEnd.test.ts b/packages/nextjs/test/common/utils/responseEnd.test.ts index 8b5c7a98dc19..8cb5bc792a88 100644 --- a/packages/nextjs/test/common/utils/responseEnd.test.ts +++ b/packages/nextjs/test/common/utils/responseEnd.test.ts @@ -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(() => { @@ -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); @@ -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 diff --git a/packages/nextjs/test/config/handleRunAfterProductionCompile.test.ts b/packages/nextjs/test/config/handleRunAfterProductionCompile.test.ts index ebff37a09c5a..d66a0d083663 100644 --- a/packages/nextjs/test/config/handleRunAfterProductionCompile.test.ts +++ b/packages/nextjs/test/config/handleRunAfterProductionCompile.test.ts @@ -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'; @@ -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(), })); diff --git a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts b/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts index 3e224c76947f..c4f17e5641f5 100644 --- a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts +++ b/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts @@ -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 { @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), @@ -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', }), diff --git a/packages/nitro/src/runtime/hooks/captureErrorHook.ts b/packages/nitro/src/runtime/hooks/captureErrorHook.ts index 2f7a602f3af1..d8366d813c35 100644 --- a/packages/nitro/src/runtime/hooks/captureErrorHook.ts +++ b/packages/nitro/src/runtime/hooks/captureErrorHook.ts @@ -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'; diff --git a/packages/nitro/src/runtime/hooks/captureStorageEvents.ts b/packages/nitro/src/runtime/hooks/captureStorageEvents.ts index a47ef1a30b6a..ccce9796cd9c 100644 --- a/packages/nitro/src/runtime/hooks/captureStorageEvents.ts +++ b/packages/nitro/src/runtime/hooks/captureStorageEvents.ts @@ -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'; diff --git a/packages/nitro/test/runtime/hooks/captureErrorHook.test.ts b/packages/nitro/test/runtime/hooks/captureErrorHook.test.ts index f83395b6c7db..9b07a9e9b9f4 100644 --- a/packages/nitro/test/runtime/hooks/captureErrorHook.test.ts +++ b/packages/nitro/test/runtime/hooks/captureErrorHook.test.ts @@ -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'; @@ -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 = { @@ -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 () => { @@ -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 () => { diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 3b18ac957f07..b06b02576e4e 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -154,7 +154,8 @@ export { featureFlagsIntegration, spanStreamingIntegration, } from '@sentry/core'; -export { trpcMiddleware, wrapMcpServerWithSentry } from '@sentry/core/server'; +export { wrapMcpServerWithSentry } from '@sentry/core/server'; +export { trpcMiddleware } from '@sentry/server-utils'; export type { Breadcrumb, diff --git a/packages/node/src/integrations/anr/worker.ts b/packages/node/src/integrations/anr/worker.ts index fa6054c850e6..f9c671190979 100644 --- a/packages/node/src/integrations/anr/worker.ts +++ b/packages/node/src/integrations/anr/worker.ts @@ -13,7 +13,7 @@ import { updateSession, uuid4, } from '@sentry/core'; -import { callFrameToStackFrame, watchdogTimer } from '@sentry/core/server'; +import { callFrameToStackFrame, watchdogTimer } from '@sentry/server-utils'; import { makeNodeTransport } from '../../transports'; import { createGetModuleFromFilename } from '../../utils/module'; import type { WorkerStartData } from './common'; diff --git a/packages/nuxt/src/runtime/hooks/captureErrorHook.ts b/packages/nuxt/src/runtime/hooks/captureErrorHook.ts index 50d5a61a2828..111f7c8d7bbd 100644 --- a/packages/nuxt/src/runtime/hooks/captureErrorHook.ts +++ b/packages/nuxt/src/runtime/hooks/captureErrorHook.ts @@ -1,5 +1,5 @@ import { captureException, getClient, getCurrentScope } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; // eslint-disable-next-line import/no-extraneous-dependencies import { H3Error } from 'h3'; import type { CapturedErrorContext } from 'nitropack/types'; diff --git a/packages/nuxt/src/runtime/hooks/wrapMiddlewareHandler.ts b/packages/nuxt/src/runtime/hooks/wrapMiddlewareHandler.ts index f5adf02ea0eb..d54f2f8a6658 100644 --- a/packages/nuxt/src/runtime/hooks/wrapMiddlewareHandler.ts +++ b/packages/nuxt/src/runtime/hooks/wrapMiddlewareHandler.ts @@ -11,7 +11,7 @@ import { type SpanAttributes, startSpan, } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import type { _ResponseMiddleware as ResponseMiddleware, EventHandler, diff --git a/packages/nuxt/src/runtime/utils/instrumentDatabase.ts b/packages/nuxt/src/runtime/utils/instrumentDatabase.ts index 9bb3d25e4fe1..8c1946c228fc 100644 --- a/packages/nuxt/src/runtime/utils/instrumentDatabase.ts +++ b/packages/nuxt/src/runtime/utils/instrumentDatabase.ts @@ -13,7 +13,8 @@ import { startSpan, type StartSpanOptions, } from '@sentry/core'; -import { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery, flushIfServerless } from '@sentry/core/server'; +import { _INTERNAL_getSqlQuerySummary, _INTERNAL_sanitizeSqlQuery } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import type { Database, PreparedStatement } from 'db0'; import { type DatabaseConnectionConfig, type DatabaseSpanData, getDatabaseSpanData } from './database-span-data'; import { DB_NAMESPACE, DB_QUERY_SUMMARY, DB_QUERY_TEXT, DB_SYSTEM_NAME } from '@sentry/conventions/attributes'; diff --git a/packages/nuxt/src/runtime/utils/instrumentStorage.ts b/packages/nuxt/src/runtime/utils/instrumentStorage.ts index e44f4699b9af..6859913df5c9 100644 --- a/packages/nuxt/src/runtime/utils/instrumentStorage.ts +++ b/packages/nuxt/src/runtime/utils/instrumentStorage.ts @@ -13,7 +13,7 @@ import { startSpan, type StartSpanOptions, } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import type { Driver, Storage } from 'unstorage'; /** diff --git a/packages/nuxt/src/runtime/utils/patchEventHandler.ts b/packages/nuxt/src/runtime/utils/patchEventHandler.ts index 9349e29d1add..77a050dacf42 100644 --- a/packages/nuxt/src/runtime/utils/patchEventHandler.ts +++ b/packages/nuxt/src/runtime/utils/patchEventHandler.ts @@ -1,5 +1,5 @@ import { debug, getDefaultIsolationScope, getIsolationScope, withIsolationScope } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; /** * Patches the H3 event handler of Nitro. diff --git a/packages/nuxt/test/runtime/hooks/captureErrorHook.test.ts b/packages/nuxt/test/runtime/hooks/captureErrorHook.test.ts index 8e166a5ff4cc..1591a5acc671 100644 --- a/packages/nuxt/test/runtime/hooks/captureErrorHook.test.ts +++ b/packages/nuxt/test/runtime/hooks/captureErrorHook.test.ts @@ -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 { H3Error } from 'h3'; import type { CapturedErrorContext } from 'nitropack/types'; import { beforeEach, describe, expect, it, vi } from 'vitest'; @@ -17,13 +17,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(), +})); vi.mock('../../../src/runtime/utils', () => ({ extractErrorContext: vi.fn(() => ({ test: 'context' })), @@ -42,7 +38,7 @@ describe('sentryCaptureErrorHook', () => { (SentryCore.getClient as any).mockReturnValue({ getOptions: () => ({}), }); - (SentryCoreServer.flushIfServerless as any).mockResolvedValue(undefined); + (serverUtils.flushIfServerless as any).mockResolvedValue(undefined); }); it('should capture regular errors', async () => { diff --git a/packages/nuxt/test/runtime/hooks/wrapMiddlewareHandler.test.ts b/packages/nuxt/test/runtime/hooks/wrapMiddlewareHandler.test.ts index 04d61994579e..b691c2d9bd5a 100644 --- a/packages/nuxt/test/runtime/hooks/wrapMiddlewareHandler.test.ts +++ b/packages/nuxt/test/runtime/hooks/wrapMiddlewareHandler.test.ts @@ -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 type { EventHandler, EventHandlerRequest, H3Event } from 'h3'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { wrapMiddlewareHandlerWithSentry } from '../../../src/runtime/hooks/wrapMiddlewareHandler'; @@ -17,13 +17,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('wrapMiddlewareHandlerWithSentry', () => { const mockEvent: H3Event = { @@ -63,7 +59,7 @@ describe('wrapMiddlewareHandlerWithSentry', () => { }), }); (SentryCore.httpHeadersToSpanAttributes as any).mockReturnValue({ 'http.request.header.user_agent': 'test-agent' }); - (SentryCoreServer.flushIfServerless as any).mockResolvedValue(undefined); + (serverUtils.flushIfServerless as any).mockResolvedValue(undefined); }); describe('function handler wrapping', () => { diff --git a/packages/react-router/src/server/createSentryHandleError.ts b/packages/react-router/src/server/createSentryHandleError.ts index 481cbfab1b92..ff3893da60c7 100644 --- a/packages/react-router/src/server/createSentryHandleError.ts +++ b/packages/react-router/src/server/createSentryHandleError.ts @@ -1,5 +1,5 @@ import { captureException } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import type { HandleErrorFunction } from 'react-router'; export type SentryHandleErrorOptions = { diff --git a/packages/react-router/src/server/createServerInstrumentation.ts b/packages/react-router/src/server/createServerInstrumentation.ts index 436ade2dd6d9..18839cae0f6e 100644 --- a/packages/react-router/src/server/createServerInstrumentation.ts +++ b/packages/react-router/src/server/createServerInstrumentation.ts @@ -22,7 +22,7 @@ import { updateSpanName, filterCollectedUrl, } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import type { ServerInstrumentation } from 'react-router'; import { DEBUG_BUILD } from '../common/debug-build'; import { captureInstrumentationError, getPathFromRequest, getPattern, normalizeRoutePath } from '../common/utils'; diff --git a/packages/react-router/src/server/wrapSentryHandleRequest.ts b/packages/react-router/src/server/wrapSentryHandleRequest.ts index 8a4af55ce62e..ae4df4083148 100644 --- a/packages/react-router/src/server/wrapSentryHandleRequest.ts +++ b/packages/react-router/src/server/wrapSentryHandleRequest.ts @@ -6,7 +6,7 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, updateSpanName, } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import type { AppLoadContext, EntryContext, RouterContextProvider } from 'react-router'; import { isInstrumentationApiUsed } from './serverGlobals'; diff --git a/packages/react-router/test/server/createSentryHandleError.test.ts b/packages/react-router/test/server/createSentryHandleError.test.ts index e93e60228684..3af557288249 100644 --- a/packages/react-router/test/server/createSentryHandleError.test.ts +++ b/packages/react-router/test/server/createSentryHandleError.test.ts @@ -1,5 +1,5 @@ import * as core from '@sentry/core'; -import * as coreServer from '@sentry/core/server'; +import * as serverUtils from '@sentry/server-utils'; import type { ActionFunctionArgs, LoaderFunctionArgs } from 'react-router'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createSentryHandleError } from '../../src/server/createSentryHandleError'; @@ -8,7 +8,7 @@ vi.mock('@sentry/core', () => ({ captureException: vi.fn(), })); -vi.mock('@sentry/core/server', () => ({ +vi.mock('@sentry/server-utils', () => ({ flushIfServerless: vi.fn().mockResolvedValue(undefined), })); @@ -19,7 +19,7 @@ const mechanism = { describe('createSentryHandleError', () => { const mockCaptureException = vi.mocked(core.captureException); - const mockFlushIfServerless = vi.mocked(coreServer.flushIfServerless); + const mockFlushIfServerless = vi.mocked(serverUtils.flushIfServerless); const mockConsoleError = vi.spyOn(console, 'error').mockImplementation(() => {}); const mockError = new Error('Test error'); diff --git a/packages/react-router/test/server/createServerInstrumentation.test.ts b/packages/react-router/test/server/createServerInstrumentation.test.ts index c7b82679430e..d3e4ccf1b3e7 100644 --- a/packages/react-router/test/server/createServerInstrumentation.test.ts +++ b/packages/react-router/test/server/createServerInstrumentation.test.ts @@ -1,6 +1,6 @@ import { URL_FULL, URL_PATH } from '@sentry/conventions/attributes'; import * as core from '@sentry/core'; -import * as coreServer from '@sentry/core/server'; +import * as serverUtils from '@sentry/server-utils'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createSentryServerInstrumentation, @@ -24,7 +24,7 @@ vi.mock('@sentry/core', async () => { }; }); -vi.mock('@sentry/core/server', () => ({ +vi.mock('@sentry/server-utils', () => ({ flushIfServerless: vi.fn(), })); @@ -150,7 +150,7 @@ describe('createSentryServerInstrumentation', () => { [URL_PATH]: '/test-path', }); expect(mockHandleRequest).toHaveBeenCalled(); - expect(coreServer.flushIfServerless).toHaveBeenCalled(); + expect(serverUtils.flushIfServerless).toHaveBeenCalled(); }); it('should create own root span when no active span exists', async () => { @@ -185,7 +185,7 @@ describe('createSentryServerInstrumentation', () => { expect.any(Function), ); expect(mockHandleRequest).toHaveBeenCalled(); - expect(coreServer.flushIfServerless).toHaveBeenCalled(); + expect(serverUtils.flushIfServerless).toHaveBeenCalled(); }); it('should capture errors and set span status when root span exists', async () => { @@ -258,7 +258,7 @@ describe('createSentryServerInstrumentation', () => { // Handler should still be called even if URL parsing fails expect(mockHandleRequest).toHaveBeenCalled(); - expect(coreServer.flushIfServerless).toHaveBeenCalled(); + expect(serverUtils.flushIfServerless).toHaveBeenCalled(); }); it('should handle relative URLs by using a dummy base', async () => { diff --git a/packages/react-router/test/server/wrapSentryHandleRequest.test.ts b/packages/react-router/test/server/wrapSentryHandleRequest.test.ts index c48bedef8181..849679b1380a 100644 --- a/packages/react-router/test/server/wrapSentryHandleRequest.test.ts +++ b/packages/react-router/test/server/wrapSentryHandleRequest.test.ts @@ -1,7 +1,7 @@ import { PassThrough } from 'node:stream'; import { SENTRY_SEGMENT_NAME_SOURCE, HTTP_ROUTE } from '@sentry/conventions/attributes'; import { getActiveSpan, getRootSpan, getTraceMetaTags, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import { beforeEach, describe, expect, test, vi } from 'vitest'; import { getMetaTagTransformer } from '../../src/server/getMetaTagTransformer'; import { wrapSentryHandleRequest } from '../../src/server/wrapSentryHandleRequest'; @@ -16,7 +16,7 @@ vi.mock('@sentry/core', () => ({ GLOBAL_OBJ: globalThis, })); -vi.mock('@sentry/core/server', () => ({ +vi.mock('@sentry/server-utils', () => ({ flushIfServerless: vi.fn(), })); diff --git a/packages/remix/src/cloudflare/index.ts b/packages/remix/src/cloudflare/index.ts index 3436abae61f2..a0f4276f0c95 100644 --- a/packages/remix/src/cloudflare/index.ts +++ b/packages/remix/src/cloudflare/index.ts @@ -120,4 +120,4 @@ export { withStreamedSpan, featureFlagsIntegration, } from '@sentry/core'; -export { trpcMiddleware } from '@sentry/core/server'; +export { trpcMiddleware } from '@sentry/server-utils'; diff --git a/packages/remix/src/server/instrumentServer.ts b/packages/remix/src/server/instrumentServer.ts index a336a7f72c2a..9010010900c7 100644 --- a/packages/remix/src/server/instrumentServer.ts +++ b/packages/remix/src/server/instrumentServer.ts @@ -34,7 +34,8 @@ import { withIsolationScope, filterCollectedUrl, } from '@sentry/core'; -import { isNodeEnv, loadModule } from '@sentry/core/server'; +import { isNodeEnv } from '@sentry/core/server'; +import { loadModule } from '@sentry/server-utils'; import { DEBUG_BUILD } from '../utils/debug-build'; import { createRoutes, getTransactionName, isCloudflareEnv } from '../utils/utils'; import { extractData, isResponse, json } from '../utils/vendor/response'; diff --git a/packages/server-utils/src/exports.ts b/packages/server-utils/src/exports.ts index 9baca0c266f6..8a4bedfde8b2 100644 --- a/packages/server-utils/src/exports.ts +++ b/packages/server-utils/src/exports.ts @@ -1,5 +1,10 @@ // Shared exports not using diagnostics channels export { setHttpServerSpanRouteAttribute } from './utils/setHttpServerSpanRouteAttribute'; +export { flushIfServerless } from './utils/flushIfServerless'; +export { vercelWaitUntil } from './utils/vercelWaitUntil'; +export { loadModule } from './utils/loadModule'; +export { callFrameToStackFrame, watchdogTimer } from './utils/anr'; +export { trpcMiddleware } from './trpc'; export { setAsyncLocalStorageAsyncContextStrategy } from './async-context'; export { otlpIntegration, getOtlpTracesEndpoint } from './otlp'; export * from './ai'; diff --git a/packages/core/src/trpc.ts b/packages/server-utils/src/trpc.ts similarity index 90% rename from packages/core/src/trpc.ts rename to packages/server-utils/src/trpc.ts index 55e428f60458..318e5de81795 100644 --- a/packages/core/src/trpc.ts +++ b/packages/server-utils/src/trpc.ts @@ -7,12 +7,15 @@ import { TRPC_PROCEDURE_TYPE, } from '@sentry/conventions/attributes'; import { RPC } from '@sentry/conventions/op'; -import { getClient, withIsolationScope } from './currentScopes'; -import { captureException } from './exports'; -import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from './semanticAttributes'; -import { startSpanManual } from './tracing/trace'; -import { normalize } from './utils/normalize'; -import { setNormalizationDepthOverrideHint } from './utils/normalizationHints'; +import { + captureException, + getClient, + normalize, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + setNormalizationDepthOverrideHint, + startSpanManual, + withIsolationScope, +} from '@sentry/core'; interface SentryTrpcMiddlewareOptions { /** Whether to include procedure inputs in reported events. Defaults to `false`. */ diff --git a/packages/core/src/utils/anr.ts b/packages/server-utils/src/utils/anr.ts similarity index 93% rename from packages/core/src/utils/anr.ts rename to packages/server-utils/src/utils/anr.ts index ad6db13a1bf9..57c5e0cd5d56 100644 --- a/packages/core/src/utils/anr.ts +++ b/packages/server-utils/src/utils/anr.ts @@ -1,6 +1,6 @@ -import type { StackFrame } from '../types/stackframe'; -import { filenameIsInApp } from './node-stack-trace'; -import { UNKNOWN_FUNCTION } from './stacktrace'; +import type { StackFrame } from '@sentry/core'; +import { filenameIsInApp } from '@sentry/core/server'; +import { UNKNOWN_FUNCTION } from '@sentry/core'; type WatchdogReturn = { /** Resets the watchdog timer */ diff --git a/packages/core/src/utils/flushIfServerless.ts b/packages/server-utils/src/utils/flushIfServerless.ts similarity index 96% rename from packages/core/src/utils/flushIfServerless.ts rename to packages/server-utils/src/utils/flushIfServerless.ts index 5ffd86612243..6491cc5af277 100644 --- a/packages/core/src/utils/flushIfServerless.ts +++ b/packages/server-utils/src/utils/flushIfServerless.ts @@ -1,7 +1,5 @@ -import { flush } from '../exports'; -import { debug } from './debug-logger'; +import { debug, flush, GLOBAL_OBJ } from '@sentry/core'; import { vercelWaitUntil } from './vercelWaitUntil'; -import { GLOBAL_OBJ } from './worldwide'; type MinimalCloudflareContext = { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/server-utils/src/utils/loadModule.ts b/packages/server-utils/src/utils/loadModule.ts new file mode 100644 index 000000000000..fb5585f25fa9 --- /dev/null +++ b/packages/server-utils/src/utils/loadModule.ts @@ -0,0 +1,46 @@ +/** + * 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(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; +} diff --git a/packages/core/src/utils/vercelWaitUntil.ts b/packages/server-utils/src/utils/vercelWaitUntil.ts similarity index 95% rename from packages/core/src/utils/vercelWaitUntil.ts rename to packages/server-utils/src/utils/vercelWaitUntil.ts index 32d801a6723c..76d26ecbd5a0 100644 --- a/packages/core/src/utils/vercelWaitUntil.ts +++ b/packages/server-utils/src/utils/vercelWaitUntil.ts @@ -1,4 +1,4 @@ -import { GLOBAL_OBJ } from './worldwide'; +import { GLOBAL_OBJ } from '@sentry/core'; declare const EdgeRuntime: string | undefined; diff --git a/packages/core/test/lib/trpc.test.ts b/packages/server-utils/test/trpc.test.ts similarity index 74% rename from packages/core/test/lib/trpc.test.ts rename to packages/server-utils/test/trpc.test.ts index ecd0382bbdbe..5acf1f7ab52f 100644 --- a/packages/core/test/lib/trpc.test.ts +++ b/packages/server-utils/test/trpc.test.ts @@ -1,11 +1,8 @@ +import { type Client, setCurrentClient, type Span } from '@sentry/core'; +import * as SentryCore from '@sentry/core'; import { beforeEach, describe, expect, test, vi } from 'vitest'; -import { type Client, setCurrentClient, type Span } from '../../src'; -import { trpcMiddleware } from '../../src/server'; -import * as currentScopes from '../../src/currentScopes'; -import * as exports from '../../src/exports'; -import * as tracing from '../../src/tracing/trace'; -import { resolveDataCollectionOptions } from '../../src/utils/data-collection/resolveDataCollectionOptions'; -import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; +import { trpcMiddleware } from '../src/trpc'; +import { getDefaultTestClientOptions, TestClient } from './mocks/client'; describe('trpcMiddleware', () => { let client: Client; @@ -15,9 +12,7 @@ describe('trpcMiddleware', () => { normalizeDepth: 3, dataCollection: { httpBodies: [] }, }), - getDataCollectionOptions: vi - .fn() - .mockReturnValue(resolveDataCollectionOptions({ dataCollection: { httpBodies: [] } })), + getDataCollectionOptions: vi.fn().mockReturnValue({ httpBodies: [] }), captureException: vi.fn(), } as unknown as Client; @@ -41,10 +36,10 @@ describe('trpcMiddleware', () => { client = new TestClient(options); setCurrentClient(client); client.init(); - vi.spyOn(currentScopes, 'getClient').mockReturnValue(mockClient); - vi.spyOn(tracing, 'startSpanManual').mockImplementation((name, callback) => callback(mockSpan, () => {})); - vi.spyOn(currentScopes, 'withIsolationScope').mockImplementation(withIsolationScope); - vi.spyOn(exports, 'captureException').mockImplementation(() => 'mock-event-id'); + vi.spyOn(SentryCore, 'getClient').mockReturnValue(mockClient); + vi.spyOn(SentryCore, 'startSpanManual').mockImplementation((name, callback) => callback(mockSpan, () => {})); + vi.spyOn(SentryCore, 'withIsolationScope').mockImplementation(withIsolationScope); + vi.spyOn(SentryCore, 'captureException').mockImplementation(() => 'mock-event-id'); }); test('creates span with correct attributes', async () => { @@ -57,7 +52,7 @@ describe('trpcMiddleware', () => { next, }); - expect(tracing.startSpanManual).toHaveBeenCalledWith( + expect(SentryCore.startSpanManual).toHaveBeenCalledWith( { name: 'trpc/test.procedure', attributes: { @@ -81,7 +76,7 @@ describe('trpcMiddleware', () => { await middleware({ path: 'test.procedure', type: 'query', next }); - expect(tracing.startSpanManual).toHaveBeenCalledWith( + expect(SentryCore.startSpanManual).toHaveBeenCalledWith( expect.objectContaining({ attributes: expect.objectContaining({ 'sentry.segment.name.source': 'route', @@ -102,7 +97,7 @@ describe('trpcMiddleware', () => { next, }); - expect(exports.captureException).toHaveBeenCalledWith(error, { + expect(SentryCore.captureException).toHaveBeenCalledWith(error, { mechanism: { handled: false, type: 'auto.rpc.trpc.middleware' }, }); }); @@ -139,7 +134,7 @@ describe('trpcMiddleware', () => { }), ).rejects.toThrow(error); - expect(exports.captureException).toHaveBeenCalledWith(error, { + expect(SentryCore.captureException).toHaveBeenCalledWith(error, { mechanism: { handled: false, type: 'auto.rpc.trpc.middleware' }, }); }); @@ -154,7 +149,7 @@ describe('trpcMiddleware', () => { next, }); - expect(tracing.startSpanManual).toHaveBeenCalledWith( + expect(SentryCore.startSpanManual).toHaveBeenCalledWith( expect.objectContaining({ forceTransaction: true, }), diff --git a/packages/core/test/lib/utils/flushIfServerless.test.ts b/packages/server-utils/test/utils/flushIfServerless.test.ts similarity index 81% rename from packages/core/test/lib/utils/flushIfServerless.test.ts rename to packages/server-utils/test/utils/flushIfServerless.test.ts index aa0314f183dc..4650bfce1735 100644 --- a/packages/core/test/lib/utils/flushIfServerless.test.ts +++ b/packages/server-utils/test/utils/flushIfServerless.test.ts @@ -1,8 +1,8 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; -import * as flushModule from '../../../src/exports'; -import { flushIfServerless } from '../../../src/utils/flushIfServerless'; -import * as vercelWaitUntilModule from '../../../src/utils/vercelWaitUntil'; -import { GLOBAL_OBJ } from '../../../src/utils/worldwide'; +import * as SentryCore from '@sentry/core'; +import { flushIfServerless } from '../../src/utils/flushIfServerless'; +import * as vercelWaitUntilModule from '../../src/utils/vercelWaitUntil'; +import { GLOBAL_OBJ } from '@sentry/core'; describe('flushIfServerless', () => { let originalProcess: typeof process; @@ -17,7 +17,7 @@ describe('flushIfServerless', () => { }); test('should bind context (preserve `this`) when calling waitUntil from the Cloudflare execution context', async () => { - const flushMock = vi.spyOn(flushModule, 'flush').mockResolvedValue(true); + const flushMock = vi.spyOn(SentryCore, 'flush').mockResolvedValue(true); // Mock Cloudflare context with `waitUntil` (which should be called if `this` is bound correctly) const mockCloudflareCtx = { @@ -38,7 +38,7 @@ describe('flushIfServerless', () => { }); test('should use cloudflare waitUntil when valid cloudflare context is provided', async () => { - const flushMock = vi.spyOn(flushModule, 'flush').mockResolvedValue(true); + const flushMock = vi.spyOn(SentryCore, 'flush').mockResolvedValue(true); const mockCloudflareCtx = { waitUntil: vi.fn(), }; @@ -50,7 +50,7 @@ describe('flushIfServerless', () => { }); test('should use cloudflare waitUntil when Cloudflare `waitUntil` is provided', async () => { - const flushMock = vi.spyOn(flushModule, 'flush').mockResolvedValue(true); + const flushMock = vi.spyOn(SentryCore, 'flush').mockResolvedValue(true); const mockCloudflareCtx = { waitUntil: vi.fn(), }; @@ -62,7 +62,7 @@ describe('flushIfServerless', () => { }); test('should ignore cloudflare context when waitUntil is not a function (and use Vercel waitUntil instead)', async () => { - const flushMock = vi.spyOn(flushModule, 'flush').mockResolvedValue(true); + const flushMock = vi.spyOn(SentryCore, 'flush').mockResolvedValue(true); const vercelWaitUntilSpy = vi.spyOn(vercelWaitUntilModule, 'vercelWaitUntil').mockImplementation(() => {}); // Mock Vercel environment @@ -81,7 +81,7 @@ describe('flushIfServerless', () => { }); test('should handle multiple serverless environment variables simultaneously', async () => { - const flushMock = vi.spyOn(flushModule, 'flush').mockResolvedValue(true); + const flushMock = vi.spyOn(SentryCore, 'flush').mockResolvedValue(true); global.process = { ...originalProcess, @@ -100,7 +100,7 @@ describe('flushIfServerless', () => { }); test('should use default timeout when not specified', async () => { - const flushMock = vi.spyOn(flushModule, 'flush').mockResolvedValue(true); + const flushMock = vi.spyOn(SentryCore, 'flush').mockResolvedValue(true); const mockCloudflareCtx = { waitUntil: vi.fn(), }; @@ -111,7 +111,7 @@ describe('flushIfServerless', () => { }); test('should handle zero timeout value', async () => { - const flushMock = vi.spyOn(flushModule, 'flush').mockResolvedValue(true); + const flushMock = vi.spyOn(SentryCore, 'flush').mockResolvedValue(true); global.process = { ...originalProcess, diff --git a/packages/core/test/lib/utils/vercelWaitUntil.test.ts b/packages/server-utils/test/utils/vercelWaitUntil.test.ts similarity index 95% rename from packages/core/test/lib/utils/vercelWaitUntil.test.ts rename to packages/server-utils/test/utils/vercelWaitUntil.test.ts index 1f6be3b7924f..a520d5af1cfe 100644 --- a/packages/core/test/lib/utils/vercelWaitUntil.test.ts +++ b/packages/server-utils/test/utils/vercelWaitUntil.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { vercelWaitUntil } from '../../../src/utils/vercelWaitUntil'; -import { GLOBAL_OBJ } from '../../../src/utils/worldwide'; +import { vercelWaitUntil } from '../../src/utils/vercelWaitUntil'; +import { GLOBAL_OBJ } from '@sentry/core'; describe('vercelWaitUntil', () => { const VERCEL_REQUEST_CONTEXT_SYMBOL = Symbol.for('@vercel/request-context'); diff --git a/packages/solidstart/src/server/withServerActionInstrumentation.ts b/packages/solidstart/src/server/withServerActionInstrumentation.ts index 8951d440637d..810fb7b66759 100644 --- a/packages/solidstart/src/server/withServerActionInstrumentation.ts +++ b/packages/solidstart/src/server/withServerActionInstrumentation.ts @@ -1,5 +1,5 @@ import { handleCallbackErrors, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SPAN_STATUS_ERROR } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import { captureException, getActiveSpan, spanToJSON, startSpan } from '@sentry/node'; import { isRedirect } from './utils'; import { diff --git a/packages/solidstart/test/server/withServerActionInstrumentation.test.ts b/packages/solidstart/test/server/withServerActionInstrumentation.test.ts index 32d197d1bba1..94ab6eb5902d 100644 --- a/packages/solidstart/test/server/withServerActionInstrumentation.test.ts +++ b/packages/solidstart/test/server/withServerActionInstrumentation.test.ts @@ -1,6 +1,6 @@ import { SENTRY_SEGMENT_NAME_SOURCE } from '@sentry/conventions/attributes'; import * as SentryCore from '@sentry/core'; -import * as SentryCoreServer from '@sentry/core/server'; +import * as serverUtils from '@sentry/server-utils'; import * as SentryNode from '@sentry/node'; import { createTransport, @@ -15,7 +15,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { withServerActionInstrumentation } from '../../src/server'; const mockCaptureException = vi.spyOn(SentryNode, 'captureException').mockImplementation(() => ''); -const mockFlush = vi.spyOn(SentryCoreServer, 'flushIfServerless').mockImplementation(async () => {}); +const mockFlush = vi.spyOn(serverUtils, 'flushIfServerless').mockImplementation(async () => {}); const mockGetActiveSpan = vi.spyOn(SentryCore, 'getActiveSpan'); const mockGetRequestEvent = vi.fn(); diff --git a/packages/sveltekit/src/server-common/handle.ts b/packages/sveltekit/src/server-common/handle.ts index a7f5645c2747..c37f3565ae9c 100644 --- a/packages/sveltekit/src/server-common/handle.ts +++ b/packages/sveltekit/src/server-common/handle.ts @@ -20,7 +20,7 @@ import { withIsolationScope, filterCollectedUrl, } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import type { Handle, ResolveOptions } from '@sveltejs/kit'; import { DEBUG_BUILD } from '../common/debug-build'; import { getTracePropagationData, sendErrorToSentry } from './utils'; diff --git a/packages/sveltekit/src/server-common/handleError.ts b/packages/sveltekit/src/server-common/handleError.ts index ddf18dd80396..be4ad20f7fa0 100644 --- a/packages/sveltekit/src/server-common/handleError.ts +++ b/packages/sveltekit/src/server-common/handleError.ts @@ -1,5 +1,5 @@ import { captureException, consoleSandbox } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import type { AnyErrorHandler, SentryHandleServerErrorInput } from '../common/handleErrorTypes'; import { shouldCaptureError } from '../common/handleErrorTypes'; import { getCloudflareExecutionContext } from './utils'; diff --git a/packages/sveltekit/src/server-common/load.ts b/packages/sveltekit/src/server-common/load.ts index 64106c47d157..e1e2748b3f47 100644 --- a/packages/sveltekit/src/server-common/load.ts +++ b/packages/sveltekit/src/server-common/load.ts @@ -1,5 +1,5 @@ import { addNonEnumerableProperty, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import { SENTRY_SEGMENT_NAME_SOURCE, CODE_FUNCTION_NAME, diff --git a/packages/sveltekit/src/server-common/serverRoute.ts b/packages/sveltekit/src/server-common/serverRoute.ts index 268556d0b96b..5c410779df63 100644 --- a/packages/sveltekit/src/server-common/serverRoute.ts +++ b/packages/sveltekit/src/server-common/serverRoute.ts @@ -1,5 +1,5 @@ import { addNonEnumerableProperty, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import { CODE_FUNCTION_NAME, HTTP_REQUEST_METHOD, SENTRY_OP } from '@sentry/conventions/attributes'; import { FUNCTION } from '@sentry/conventions/op'; import type { RequestEvent } from '@sveltejs/kit'; diff --git a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts index 935c03fb76e0..144a7d8ab21d 100644 --- a/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts +++ b/packages/tanstackstart-react/src/server/wrapFetchWithSentry.ts @@ -1,5 +1,5 @@ import { getTraceMetaTags } from '@sentry/core'; -import { flushIfServerless } from '@sentry/core/server'; +import { flushIfServerless } from '@sentry/server-utils'; import { captureException, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, startSpan } from '@sentry/node'; import { SENTRY_OP } from '@sentry/conventions/attributes'; import { FUNCTION } from '@sentry/conventions/op'; diff --git a/packages/tanstackstart-react/test/server/wrapFetchWithSentry.test.ts b/packages/tanstackstart-react/test/server/wrapFetchWithSentry.test.ts index cb1a809cafb2..49232b6d4a39 100644 --- a/packages/tanstackstart-react/test/server/wrapFetchWithSentry.test.ts +++ b/packages/tanstackstart-react/test/server/wrapFetchWithSentry.test.ts @@ -28,13 +28,9 @@ vi.mock('@sentry/core', async importOriginal => { }; }); -vi.mock('@sentry/core/server', async importOriginal => { - const original = await importOriginal(); - return { - ...original, - flushIfServerless: (...args: unknown[]) => flushIfServerlessSpy(...args), - }; -}); +vi.mock('@sentry/server-utils', () => ({ + flushIfServerless: (...args: unknown[]) => flushIfServerlessSpy(...args), +})); // Import after mocks are set up const { wrapFetchWithSentry } = await import('../../src/server/wrapFetchWithSentry'); diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index 4a38cd639cc4..6c143621064f 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -101,7 +101,8 @@ export { withStreamedSpan, spanStreamingIntegration, } from '@sentry/core'; -export { trpcMiddleware, wrapMcpServerWithSentry } from '@sentry/core/server'; +export { wrapMcpServerWithSentry } from '@sentry/core/server'; +export { trpcMiddleware } from '@sentry/server-utils'; export { otlpIntegration, getOtlpTracesEndpoint,