Skip to content
Open
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
9 changes: 8 additions & 1 deletion packages/nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@
"access": "public"
},
"peerDependencies": {
"nuxt": ">=3.7.0 || 4.x"
"nuxt": ">=3.7.0 || 4.x",
"nitro": "3.x"
},
"peerDependenciesMeta": {
"nitro": {
"optional": true
}
},
"dependencies": {
"@nuxt/kit": "^3.13.2",
Expand All @@ -61,6 +67,7 @@
"devDependencies": {
"@nuxt/module-builder": "^0.8.4",
"@nuxt/nitro-server": "^3.21.1",
"nitro": "^3.0.260311-beta",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I installed the beta version as the 3.0.0 version is using different types 😬

"nuxi": "^3.25.1",
"nuxt": "3.17.7",
"vite": "^5.4.11"
Expand Down
1 change: 1 addition & 0 deletions packages/nuxt/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ export default defineNuxtModule<ModuleOptions>({
const serverConfigFile = findDefaultSdkInitFile('server', nuxt);

if (serverConfigFile) {
addServerPlugin(moduleDirResolver.resolve('./runtime/plugins/handler-legacy.server'));
addServerPlugin(moduleDirResolver.resolve('./runtime/plugins/sentry.server'));
Comment on lines +83 to 84
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The new handler-legacy.server.ts plugin and the existing sentry.server.ts plugin both patch nitroApp.h3App.handler, leading to the handler being wrapped twice.
Severity: MEDIUM

Suggested Fix

The refactoring appears to be incomplete. To avoid double patching, the patching logic within sentry.server.ts should be removed, as the intent was to centralize this functionality in the new handler-legacy.server.ts plugin.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/nuxt/src/module.ts#L83-L84

Potential issue: The pull request introduces a new server plugin,
`handler-legacy.server.ts`, which patches the Nitro event handler
`nitroApp.h3App.handler`. However, the existing `sentry.server.ts` plugin, which also
performs the same patching, was not modified to remove its patching logic. Since both
plugins are registered unconditionally, the `handler-legacy.server.ts` plugin will run
first (due to alphabetical sorting), followed by `sentry.server.ts`, resulting in the
event handler being patched twice. This double-wrapping could lead to redundant
operations and potential unexpected side effects or performance degradation.

Did we get this right? 👍 / 👎 to inform future reviews.


addPlugin({
Expand Down
7 changes: 7 additions & 0 deletions packages/nuxt/src/runtime/plugins/handler-legacy.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { EventHandler } from 'h3';
import type { NitroAppPlugin } from 'nitropack';
import { patchEventHandler } from '../utils/patchEventHandler';

export default (nitroApp => {
nitroApp.h3App.handler = patchEventHandler<EventHandler>(nitroApp.h3App.handler);
}) satisfies NitroAppPlugin;
13 changes: 13 additions & 0 deletions packages/nuxt/src/runtime/plugins/handler.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { EventHandler } from 'nitro/h3';
import type { NitroAppPlugin, NitroApp } from 'nitro/types';
import { patchEventHandler } from '../utils/patchEventHandler';

/**
* This plugin patches the h3 event handler for Nuxt v5+ (Nitro v3+).
*/
export default ((nitroApp: NitroApp) => {
if (nitroApp?.h3?.handler) {
// oxlint-disable-next-line @typescript-eslint/unbound-method
nitroApp.h3.handler = patchEventHandler<EventHandler>(nitroApp.h3.handler);
}
}) satisfies NitroAppPlugin;
35 changes: 2 additions & 33 deletions packages/nuxt/src/runtime/plugins/sentry.server.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import {
debug,
flushIfServerless,
getDefaultIsolationScope,
getIsolationScope,
withIsolationScope,
} from '@sentry/core';
import type { EventHandler, H3Event } from 'h3';
import { debug } from '@sentry/core';
import type { H3Event } from 'h3';
import type { NitroAppPlugin } from 'nitropack';
import type { NuxtRenderHTMLContext } from 'nuxt/app';
import { sentryCaptureErrorHook } from '../hooks/captureErrorHook';
import { updateRouteBeforeResponse } from '../hooks/updateRouteBeforeResponse';
import { addSentryTracingMetaTags } from '../utils';

export default (nitroApp => {
nitroApp.h3App.handler = patchEventHandler(nitroApp.h3App.handler);

nitroApp.hooks.hook('beforeResponse', updateRouteBeforeResponse);

nitroApp.hooks.hook('error', sentryCaptureErrorHook);
Expand All @@ -37,26 +29,3 @@ export default (nitroApp => {
}
});
}) satisfies NitroAppPlugin;

function patchEventHandler(handler: EventHandler): EventHandler {
return new Proxy(handler, {
async apply(handlerTarget, handlerThisArg, handlerArgs: Parameters<EventHandler>) {
const isolationScope = getIsolationScope();
const newIsolationScope = isolationScope === getDefaultIsolationScope() ? isolationScope.clone() : isolationScope;

debug.log(
`Patched h3 event handler. ${
isolationScope === newIsolationScope ? 'Using existing' : 'Created new'
} isolation scope.`,
);

return withIsolationScope(newIsolationScope, async () => {
try {
return await handlerTarget.apply(handlerThisArg, handlerArgs);
} finally {
await flushIfServerless();
}
});
},
});
}
4 changes: 2 additions & 2 deletions packages/nuxt/src/runtime/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { ClientOptions, Context, SerializedTraceData } from '@sentry/core';
import { captureException, debug, getClient, getTraceMetaTags } from '@sentry/core';
import type { VueOptions } from '@sentry/vue/src/types';
import type { CapturedErrorContext } from 'nitropack/types';
import type { NuxtRenderHTMLContext } from 'nuxt/app';
import type { ComponentPublicInstance } from 'vue';
Expand Down Expand Up @@ -69,7 +68,8 @@ export function reportNuxtError(options: {

if (instance?.$props) {
const sentryClient = getClient();
const sentryOptions = sentryClient ? (sentryClient.getOptions() as ClientOptions & VueOptions) : null;
// `attachProps` is defined in the Vue integration options, but the type is not exported from @sentry/vue, as it's only used internally.
const sentryOptions = sentryClient ? (sentryClient.getOptions() as ClientOptions & { attachProps: boolean }) : null;

// `attachProps` is enabled by default and props should only not be attached if explicitly disabled (see DEFAULT_CONFIG in `vueIntegration`).
// oxlint-disable-next-line typescript/no-unsafe-member-access
Expand Down
35 changes: 35 additions & 0 deletions packages/nuxt/src/runtime/utils/patchEventHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
debug,
flushIfServerless,
getDefaultIsolationScope,
getIsolationScope,
withIsolationScope,
} from '@sentry/core';

/**
* Patches the H3 event handler of Nitro.
*
* Uses a TypeScript generic type to ensure the returned handler type fits different versions of Nitro.
*/
export function patchEventHandler<H3EventHandler extends Function>(handler: H3EventHandler): H3EventHandler {
return new Proxy(handler, {
async apply(handlerTarget, handlerThisArg, handlerArgs: unknown) {
const isolationScope = getIsolationScope();
const newIsolationScope = isolationScope === getDefaultIsolationScope() ? isolationScope.clone() : isolationScope;

debug.log(
`Patched h3 event handler. ${
isolationScope === newIsolationScope ? 'Using existing' : 'Created new'
} isolation scope.`,
);

return withIsolationScope(newIsolationScope, async () => {
try {
return await handlerTarget.apply(handlerThisArg, handlerArgs);
} finally {
await flushIfServerless();
}
});
},
});
}
3 changes: 2 additions & 1 deletion packages/nuxt/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

"compilerOptions": {
// package-specific options
"module": "esnext"
"module": "esnext",
"moduleResolution": "bundler"
}
}
Loading
Loading