diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3e000957653d..9bcf44c39024 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1008,6 +1008,7 @@ jobs:
'standard-frontend-react',
'sveltekit',
'sveltekit-2',
+ 'sveltekit-2-svelte-5',
'generic-ts3.8',
'node-fastify-app',
# TODO(v8): Re-enable hapi tests
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.gitignore b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.gitignore
new file mode 100644
index 000000000000..6635cf554275
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.gitignore
@@ -0,0 +1,10 @@
+.DS_Store
+node_modules
+/build
+/.svelte-kit
+/package
+.env
+.env.*
+!.env.example
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc
new file mode 100644
index 000000000000..070f80f05092
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc
@@ -0,0 +1,2 @@
+@sentry:registry=http://127.0.0.1:4873
+@sentry-internal:registry=http://127.0.0.1:4873
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md
new file mode 100644
index 000000000000..684cabccfe02
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md
@@ -0,0 +1,41 @@
+# create-svelte
+
+Everything you need to build a Svelte project, powered by
+[`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
+
+## Creating a project
+
+If you're seeing this, you've probably already done this step. Congrats!
+
+```bash
+# create a new project in the current directory
+npm create svelte@latest
+
+# create a new project in my-app
+npm create svelte@latest my-app
+```
+
+## Developing
+
+Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a
+development server:
+
+```bash
+npm run dev
+
+# or start the server and open the app in a new browser tab
+npm run dev -- --open
+```
+
+## Building
+
+To create a production version of your app:
+
+```bash
+npm run build
+```
+
+You can preview the production build with `npm run preview`.
+
+> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target
+> environment.
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json
new file mode 100644
index 000000000000..8929cfecad10
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "sveltekit-2-svelte-5",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "dev": "vite dev",
+ "build": "vite build",
+ "preview": "vite preview",
+ "proxy": "node start-event-proxy.cjs",
+ "clean": "npx rimraf node_modules,pnpm-lock.yaml",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
+ "test:prod": "TEST_ENV=production playwright test",
+ "test:build": "pnpm install && npx playwright install && pnpm build",
+ "test:assert": "pnpm test:prod"
+ },
+ "dependencies": {
+ "@sentry/sveltekit": "latest || *",
+ "@spotlightjs/spotlight": "2.0.0-alpha.1"
+ },
+ "devDependencies": {
+ "@playwright/test": "^1.36.2",
+ "@sentry-internal/event-proxy-server": "link:../../../event-proxy-server",
+ "@sentry/types": "latest || *",
+ "@sentry/utils": "latest || *",
+ "@sveltejs/adapter-auto": "^3.0.0",
+ "@sveltejs/kit": "^2.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "svelte": "^5.0.0-next.115",
+ "svelte-check": "^3.6.0",
+ "ts-node": "10.9.1",
+ "tslib": "^2.4.1",
+ "typescript": "^5.0.0",
+ "vite": "^5.0.3",
+ "wait-port": "1.0.4"
+ },
+ "type": "module"
+}
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/playwright.config.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/playwright.config.ts
new file mode 100644
index 000000000000..6265e4154cf3
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/playwright.config.ts
@@ -0,0 +1,77 @@
+import type { PlaywrightTestConfig } from '@playwright/test';
+import { devices } from '@playwright/test';
+
+// Fix urls not resolving to localhost on Node v17+
+// See: https://github.com/axios/axios/issues/3821#issuecomment-1413727575
+import { setDefaultResultOrder } from 'dns';
+setDefaultResultOrder('ipv4first');
+
+const testEnv = process.env.TEST_ENV;
+
+if (!testEnv) {
+ throw new Error('No test env defined');
+}
+
+const svelteKitPort = 3030;
+const eventProxyPort = 3031;
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+const config: PlaywrightTestConfig = {
+ testDir: './test',
+ /* Maximum time one test can run for. */
+ timeout: 150_000,
+ expect: {
+ /**
+ * Maximum time expect() should wait for the condition to be met.
+ * For example in `await expect(locator).toHaveText();`
+ */
+ timeout: 10000,
+ },
+ /* Run tests in files in parallel */
+ fullyParallel: false,
+ workers: 1,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* `next dev` is incredibly buggy with the app dir */
+ retries: testEnv === 'development' ? 3 : 0,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'list',
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
+ actionTimeout: 0,
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ baseURL: `http://localhost:${svelteKitPort}`,
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+ use: {
+ ...devices['Desktop Chrome'],
+ },
+ },
+ ],
+
+ /* Run your local dev server before starting the tests */
+ webServer: [
+ {
+ command: 'node start-event-proxy.cjs',
+ port: eventProxyPort,
+ reuseExistingServer: false,
+ },
+ {
+ command: `pnpm wait-port ${eventProxyPort} && pnpm preview --port ${svelteKitPort}`,
+ port: svelteKitPort,
+ reuseExistingServer: false,
+ },
+ ],
+};
+
+export default config;
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts
new file mode 100644
index 000000000000..ede601ab93e2
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts
@@ -0,0 +1,13 @@
+// See https://kit.svelte.dev/docs/types#app
+// for information about these interfaces
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.html b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.html
new file mode 100644
index 000000000000..77a5ff52c923
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+ %sveltekit.head%
+
+
+ %sveltekit.body%
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.client.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.client.ts
new file mode 100644
index 000000000000..606379b59760
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.client.ts
@@ -0,0 +1,23 @@
+import { env } from '$env/dynamic/public';
+import * as Sentry from '@sentry/sveltekit';
+import * as Spotlight from '@spotlightjs/spotlight';
+
+Sentry.init({
+ environment: 'qa', // dynamic sampling bias to keep transactions
+ dsn: env.PUBLIC_E2E_TEST_DSN,
+ debug: true,
+ tunnel: `http://localhost:3031/`, // proxy server
+ tracesSampleRate: 1.0,
+});
+
+const myErrorHandler = ({ error, event }: any) => {
+ console.error('An error occurred on the client side:', error, event);
+};
+
+export const handleError = Sentry.handleErrorWithSentry(myErrorHandler);
+
+if (import.meta.env.DEV) {
+ Spotlight.init({
+ injectImmediately: true,
+ });
+}
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.server.ts
new file mode 100644
index 000000000000..3fedded42858
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.server.ts
@@ -0,0 +1,21 @@
+import { E2E_TEST_DSN } from '$env/static/private';
+import * as Sentry from '@sentry/sveltekit';
+import { setupSidecar } from '@spotlightjs/spotlight/sidecar';
+
+Sentry.init({
+ environment: 'qa', // dynamic sampling bias to keep transactions
+ dsn: E2E_TEST_DSN,
+ debug: true,
+ tunnel: `http://localhost:3031/`, // proxy server
+ tracesSampleRate: 1.0,
+ spotlight: import.meta.env.DEV,
+});
+
+// not logging anything to console to avoid noise in the test output
+export const handleError = Sentry.handleErrorWithSentry(() => {});
+
+export const handle = Sentry.sentryHandle();
+
+if (import.meta.env.DEV) {
+ setupSidecar();
+}
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+layout.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+layout.svelte
new file mode 100644
index 000000000000..d1fadd2ea5a3
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+layout.svelte
@@ -0,0 +1,15 @@
+
+
+Sveltekit E2E Test app
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+page.svelte
new file mode 100644
index 000000000000..575e6827693f
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+page.svelte
@@ -0,0 +1,41 @@
+Welcome to SvelteKit 2 with Svelte 5!
+Visit kit.svelte.dev to read the documentation
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/api/users/+server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/api/users/+server.ts
new file mode 100644
index 000000000000..d0e4371c594b
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/api/users/+server.ts
@@ -0,0 +1,3 @@
+export const GET = () => {
+ return new Response(JSON.stringify({ users: ['alice', 'bob', 'carol'] }));
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.server.ts
new file mode 100644
index 000000000000..b07376ba97c9
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.server.ts
@@ -0,0 +1,5 @@
+import type { PageServerLoad } from './$types';
+
+export const load = (async _event => {
+ return { name: 'building (server)' };
+}) satisfies PageServerLoad;
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.svelte
new file mode 100644
index 000000000000..b27edb70053d
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.svelte
@@ -0,0 +1,21 @@
+
+
+Check Build
+
+
+ This route only exists to check that Typescript definitions
+ and auto instrumentation are working when the project is built.
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.ts
new file mode 100644
index 000000000000..049acdc1fafa
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.ts
@@ -0,0 +1,5 @@
+import type { PageLoad } from './$types';
+
+export const load = (async _event => {
+ return { name: 'building' };
+}) satisfies PageLoad;
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/client-error/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/client-error/+page.svelte
new file mode 100644
index 000000000000..ba6b464e9324
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/client-error/+page.svelte
@@ -0,0 +1,9 @@
+
+
+Client error
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/+page.svelte
new file mode 100644
index 000000000000..eff3fa3f2e8d
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/+page.svelte
@@ -0,0 +1,15 @@
+
+Demonstrating Component Tracking
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component1.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component1.svelte
new file mode 100644
index 000000000000..a675711e4b68
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component1.svelte
@@ -0,0 +1,10 @@
+
+Howdy, I'm component 1
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component2.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component2.svelte
new file mode 100644
index 000000000000..2b2f38308077
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component2.svelte
@@ -0,0 +1,9 @@
+
+Howdy, I'm component 2
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component3.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component3.svelte
new file mode 100644
index 000000000000..9b4e028f78e7
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component3.svelte
@@ -0,0 +1,6 @@
+
+
+Howdy, I'm component 3
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav1/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav1/+page.svelte
new file mode 100644
index 000000000000..31abffc512a2
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav1/+page.svelte
@@ -0,0 +1 @@
+Navigation 1
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav2/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav2/+page.svelte
new file mode 100644
index 000000000000..20b44bb32da9
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav2/+page.svelte
@@ -0,0 +1 @@
+Navigation 2
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect1/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect1/+page.ts
new file mode 100644
index 000000000000..3f462bf810fd
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect1/+page.ts
@@ -0,0 +1,5 @@
+import { redirect } from '@sveltejs/kit';
+
+export const load = async () => {
+ redirect(301, '/redirect2');
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect2/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect2/+page.ts
new file mode 100644
index 000000000000..99a810761d18
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect2/+page.ts
@@ -0,0 +1,5 @@
+import { redirect } from '@sveltejs/kit';
+
+export const load = async () => {
+ redirect(301, '/users/789');
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.server.ts
new file mode 100644
index 000000000000..17dd53fb5bbb
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.server.ts
@@ -0,0 +1,6 @@
+export const load = async () => {
+ throw new Error('Server Load Error');
+ return {
+ msg: 'Hello World',
+ };
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.svelte
new file mode 100644
index 000000000000..3a0942971d06
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.svelte
@@ -0,0 +1,9 @@
+
+
+Server load error
+
+
+ Message: {data.msg}
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.server.ts
new file mode 100644
index 000000000000..709e52bcf351
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.server.ts
@@ -0,0 +1,5 @@
+export const load = async ({ fetch }) => {
+ const res = await fetch('/api/users');
+ const data = await res.json();
+ return { data };
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.svelte
new file mode 100644
index 000000000000..f7f814d31b4d
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.svelte
@@ -0,0 +1,8 @@
+
+
+
+ Server Load Fetch
+ {JSON.stringify(data, null, 2)}
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.svelte
new file mode 100644
index 000000000000..3d682e7e3462
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.svelte
@@ -0,0 +1,9 @@
+
+
+Server Route error
+
+
+ Message: {data.msg}
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.ts
new file mode 100644
index 000000000000..298240827714
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.ts
@@ -0,0 +1,7 @@
+export const load = async ({ fetch }) => {
+ const res = await fetch('/server-route-error');
+ const data = await res.json();
+ return {
+ msg: data,
+ };
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+server.ts
new file mode 100644
index 000000000000..f1a4b94b7706
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+server.ts
@@ -0,0 +1,6 @@
+export const GET = async () => {
+ throw new Error('Server Route Error');
+ return {
+ msg: 'Hello World',
+ };
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.svelte
new file mode 100644
index 000000000000..dc2d311a0ece
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.svelte
@@ -0,0 +1,17 @@
+
+
+Universal load error
+
+
+ To trigger from client: Load on another route, then navigate to this route.
+
+
+
+ To trigger from server: Load on this route
+
+
+
+ Message: {data.msg}
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.ts
new file mode 100644
index 000000000000..3d72bf4a890f
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.ts
@@ -0,0 +1,8 @@
+import { browser } from '$app/environment';
+
+export const load = async () => {
+ throw new Error(`Universal Load Error (${browser ? 'browser' : 'server'})`);
+ return {
+ msg: 'Hello World',
+ };
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.svelte
new file mode 100644
index 000000000000..563c51e8c850
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.svelte
@@ -0,0 +1,14 @@
+
+
+Fetching in universal load
+
+Here's a list of a few users:
+
+
+ {#each data.users as user}
+ - {user}
+ {/each}
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.ts
new file mode 100644
index 000000000000..63c1ee68e1cb
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.ts
@@ -0,0 +1,5 @@
+export const load = async ({ fetch }) => {
+ const usersRes = await fetch('/api/users');
+ const data = await usersRes.json();
+ return { users: data.users };
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.server.ts
new file mode 100644
index 000000000000..a34c5450f682
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.server.ts
@@ -0,0 +1,5 @@
+export const load = async () => {
+ return {
+ msg: 'Hi everyone!',
+ };
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.svelte
new file mode 100644
index 000000000000..aa804a4518fa
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.svelte
@@ -0,0 +1,10 @@
+
+
+ All Users:
+
+
+
+ message: {data.msg}
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.server.ts
new file mode 100644
index 000000000000..9388f3927018
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.server.ts
@@ -0,0 +1,5 @@
+export const load = async ({ params }) => {
+ return {
+ msg: `This is a special message for user ${params.id}`,
+ };
+};
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.svelte
new file mode 100644
index 000000000000..d348a8c57dad
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.svelte
@@ -0,0 +1,14 @@
+
+
+Route with dynamic params
+
+
+ User id: {$page.params.id}
+
+
+
+ Secret message for user: {data.msg}
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/start-event-proxy.cjs b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/start-event-proxy.cjs
new file mode 100644
index 000000000000..1ac8b9604239
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/start-event-proxy.cjs
@@ -0,0 +1,6 @@
+const { startEventProxyServer } = require('@sentry-internal/event-proxy-server');
+
+startEventProxyServer({
+ port: 3031,
+ proxyServerName: 'sveltekit-2-svelte-5',
+});
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/static/favicon.png b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/static/favicon.png
new file mode 100644
index 000000000000..825b9e65af7c
Binary files /dev/null and b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/static/favicon.png differ
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/svelte.config.js b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/svelte.config.js
new file mode 100644
index 000000000000..8a8cae634785
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/svelte.config.js
@@ -0,0 +1,18 @@
+import adapter from '@sveltejs/adapter-auto';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ // Consult https://kit.svelte.dev/docs/integrations#preprocessors
+ // for more information about preprocessors
+ preprocess: vitePreprocess(),
+
+ kit: {
+ // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
+ // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
+ // See https://kit.svelte.dev/docs/adapters for more information about adapters.
+ adapter: adapter(),
+ },
+};
+
+export default config;
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.client.test.ts
new file mode 100644
index 000000000000..c70b9a93dbbb
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.client.test.ts
@@ -0,0 +1,59 @@
+import { expect, test } from '@playwright/test';
+import { waitForError } from '@sentry-internal/event-proxy-server';
+import { waitForInitialPageload } from './utils';
+
+test.describe('client-side errors', () => {
+ test('captures error thrown on click', async ({ page }) => {
+ await waitForInitialPageload(page, { route: '/client-error' });
+
+ const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'Click Error';
+ });
+
+ await page.getByText('Throw error').click();
+
+ await expect(errorEventPromise).resolves.toBeDefined();
+
+ const errorEvent = await errorEventPromise;
+
+ const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames;
+
+ expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
+ expect.objectContaining({
+ function: expect.stringContaining('HTMLButtonElement'),
+ lineno: 1,
+ in_app: true,
+ }),
+ );
+
+ expect(errorEvent.tags).toMatchObject({ runtime: 'browser' });
+
+ expect(errorEvent.transaction).toEqual('/client-error');
+ });
+
+ test('captures universal load error', async ({ page }) => {
+ await waitForInitialPageload(page);
+ await page.reload();
+
+ const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'Universal Load Error (browser)';
+ });
+
+ // navigating triggers the error on the client
+ await page.getByText('Universal Load error').click();
+
+ const errorEvent = await errorEventPromise;
+ const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames;
+
+ const lastFrame = errorEventFrames?.[errorEventFrames?.length - 1];
+ expect(lastFrame).toEqual(
+ expect.objectContaining({
+ lineno: 1,
+ in_app: true,
+ }),
+ );
+
+ expect(errorEvent.tags).toMatchObject({ runtime: 'browser' });
+ expect(errorEvent.transaction).toEqual('/universal-load-error');
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.server.test.ts
new file mode 100644
index 000000000000..f169b0bf6b68
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.server.test.ts
@@ -0,0 +1,65 @@
+import { expect, test } from '@playwright/test';
+import { waitForError } from '@sentry-internal/event-proxy-server';
+
+test.describe('server-side errors', () => {
+ test('captures universal load error', async ({ page }) => {
+ const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'Universal Load Error (server)';
+ });
+
+ await page.goto('/universal-load-error');
+
+ const errorEvent = await errorEventPromise;
+ const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames;
+
+ expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
+ expect.objectContaining({
+ function: 'load$1',
+ in_app: true,
+ }),
+ );
+
+ expect(errorEvent.tags).toMatchObject({ runtime: 'node' });
+ });
+
+ test('captures server load error', async ({ page }) => {
+ const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'Server Load Error';
+ });
+
+ await page.goto('/server-load-error');
+
+ const errorEvent = await errorEventPromise;
+ const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames;
+
+ expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
+ expect.objectContaining({
+ function: 'load$1',
+ in_app: true,
+ }),
+ );
+
+ expect(errorEvent.tags).toMatchObject({ runtime: 'node' });
+ });
+
+ test('captures server route (GET) error', async ({ page }) => {
+ const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'Server Route Error';
+ });
+
+ await page.goto('/server-route-error');
+
+ const errorEvent = await errorEventPromise;
+ const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames;
+
+ expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
+ expect.objectContaining({
+ filename: expect.stringContaining('app:///_server.ts'),
+ function: 'GET',
+ in_app: true,
+ }),
+ );
+
+ expect(errorEvent.transaction).toEqual('GET /server-route-error');
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.client.test.ts
new file mode 100644
index 000000000000..324b83977ba1
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.client.test.ts
@@ -0,0 +1,139 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/event-proxy-server';
+import { waitForInitialPageload } from './utils';
+
+test.describe('client-specific performance events', () => {
+ test('multiple navigations have distinct traces', async ({ page }) => {
+ const navigationTxn1EventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === '/nav1' && txnEvent.contexts?.trace?.op === 'navigation';
+ });
+
+ const navigationTxn2EventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === '/' && txnEvent.contexts?.trace?.op === 'navigation';
+ });
+
+ const navigationTxn3EventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === '/nav2' && txnEvent.contexts?.trace?.op === 'navigation';
+ });
+
+ await waitForInitialPageload(page);
+
+ await page.getByText('Nav 1').click();
+ const navigationTxn1Event = await navigationTxn1EventPromise;
+
+ await page.goBack();
+ const navigationTxn2Event = await navigationTxn2EventPromise;
+
+ await page.getByText('Nav 2').click();
+ const navigationTxn3Event = await navigationTxn3EventPromise;
+
+ expect(navigationTxn1Event).toMatchObject({
+ transaction: '/nav1',
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.sveltekit',
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ });
+
+ expect(navigationTxn2Event).toMatchObject({
+ transaction: '/',
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.sveltekit',
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ });
+
+ expect(navigationTxn3Event).toMatchObject({
+ transaction: '/nav2',
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.sveltekit',
+ trace_id: expect.stringMatching(/[a-f0-9]{32}/),
+ },
+ },
+ });
+
+ // traces should NOT be connected
+ expect(navigationTxn1Event.contexts?.trace?.trace_id).not.toBe(navigationTxn2Event.contexts?.trace?.trace_id);
+ expect(navigationTxn2Event.contexts?.trace?.trace_id).not.toBe(navigationTxn3Event.contexts?.trace?.trace_id);
+ expect(navigationTxn1Event.contexts?.trace?.trace_id).not.toBe(navigationTxn3Event.contexts?.trace?.trace_id);
+ });
+
+ test('records manually added component tracking spans', async ({ page }) => {
+ const componentTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === '/components';
+ });
+
+ await waitForInitialPageload(page);
+
+ await page.getByText('Component Tracking').click();
+
+ const componentTxnEvent = await componentTxnEventPromise;
+
+ expect(componentTxnEvent.spans).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ ]),
+ );
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.server.test.ts
new file mode 100644
index 000000000000..d43941f2a0c2
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.server.test.ts
@@ -0,0 +1,35 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/event-proxy-server';
+
+test('server pageload request span has nested request span for sub request', async ({ page }) => {
+ const serverTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === 'GET /server-load-fetch';
+ });
+
+ await page.goto('/server-load-fetch');
+
+ const serverTxnEvent = await serverTxnEventPromise;
+ const spans = serverTxnEvent.spans;
+
+ expect(serverTxnEvent).toMatchObject({
+ transaction: 'GET /server-load-fetch',
+ tags: { runtime: 'node' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'http.server',
+ origin: 'auto.http.sveltekit',
+ },
+ },
+ });
+
+ expect(spans).toEqual(
+ expect.arrayContaining([
+ // load span where the server load function initiates the sub request:
+ expect.objectContaining({ op: 'function.sveltekit.server.load', description: '/server-load-fetch' }),
+ // sub request span:
+ expect.objectContaining({ op: 'http.server', description: 'GET /api/users' }),
+ ]),
+ );
+});
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.test.ts
new file mode 100644
index 000000000000..8ef420eb8eb1
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.test.ts
@@ -0,0 +1,404 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/event-proxy-server';
+import { waitForInitialPageload } from './utils';
+
+test.describe('performance events', () => {
+ test('capture a distributed pageload trace', async ({ page }) => {
+ const clientTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === '/users/[id]';
+ });
+
+ const serverTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === 'GET /users/[id]';
+ });
+
+ const [_, clientTxnEvent, serverTxnEvent] = await Promise.all([
+ page.goto('/users/123xyz'),
+ clientTxnEventPromise,
+ serverTxnEventPromise,
+ expect(page.getByText('User id: 123xyz')).toBeVisible(),
+ ]);
+
+ expect(clientTxnEvent).toMatchObject({
+ transaction: '/users/[id]',
+ tags: { runtime: 'browser' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'pageload',
+ origin: 'auto.pageload.sveltekit',
+ },
+ },
+ });
+
+ expect(serverTxnEvent).toMatchObject({
+ transaction: 'GET /users/[id]',
+ tags: { runtime: 'node' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'http.server',
+ origin: 'auto.http.sveltekit',
+ },
+ },
+ });
+
+ expect(clientTxnEvent.spans?.length).toBeGreaterThan(5);
+
+ // connected trace
+ expect(clientTxnEvent.contexts?.trace?.trace_id).toBe(serverTxnEvent.contexts?.trace?.trace_id);
+
+ // weird but server txn is parent of client txn
+ expect(clientTxnEvent.contexts?.trace?.parent_span_id).toBe(serverTxnEvent.contexts?.trace?.span_id);
+ });
+
+ test('capture a distributed navigation trace', async ({ page }) => {
+ const clientNavigationTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === '/users' && txnEvent.contexts?.trace?.op === 'navigation';
+ });
+
+ const serverTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === 'GET /users';
+ });
+
+ await waitForInitialPageload(page);
+
+ // navigation to page
+ const clickPromise = page.getByText('Route with Server Load').click();
+
+ const [clientTxnEvent, serverTxnEvent, _1, _2] = await Promise.all([
+ clientNavigationTxnEventPromise,
+ serverTxnEventPromise,
+ clickPromise,
+ expect(page.getByText('Hi everyone')).toBeVisible(),
+ ]);
+
+ expect(clientTxnEvent).toMatchObject({
+ transaction: '/users',
+ tags: { runtime: 'browser' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.sveltekit',
+ },
+ },
+ });
+
+ expect(serverTxnEvent).toMatchObject({
+ transaction: 'GET /users',
+ tags: { runtime: 'node' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'http.server',
+ origin: 'auto.http.sveltekit',
+ },
+ },
+ });
+
+ // trace is connected
+ expect(clientTxnEvent.contexts?.trace?.trace_id).toBe(serverTxnEvent.contexts?.trace?.trace_id);
+ });
+
+ test('record client-side universal load fetch span and trace', async ({ page }) => {
+ await waitForInitialPageload(page);
+
+ const clientNavigationTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === '/universal-load-fetch' && txnEvent.contexts?.trace?.op === 'navigation';
+ });
+
+ // this transaction should be created because of the fetch call
+ // it should also be part of the trace
+ const serverTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.transaction === 'GET /api/users';
+ });
+
+ // navigation to page
+ const clickPromise = page.getByText('Route with fetch in universal load').click();
+
+ const [clientTxnEvent, serverTxnEvent, _1, _2] = await Promise.all([
+ clientNavigationTxnEventPromise,
+ serverTxnEventPromise,
+ clickPromise,
+ expect(page.getByText('alice')).toBeVisible(),
+ ]);
+
+ expect(clientTxnEvent).toMatchObject({
+ transaction: '/universal-load-fetch',
+ tags: { runtime: 'browser' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.sveltekit',
+ },
+ },
+ });
+
+ expect(serverTxnEvent).toMatchObject({
+ transaction: 'GET /api/users',
+ tags: { runtime: 'node' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'http.server',
+ origin: 'auto.http.sveltekit',
+ },
+ },
+ });
+
+ // trace is connected
+ expect(clientTxnEvent.contexts?.trace?.trace_id).toBe(serverTxnEvent.contexts?.trace?.trace_id);
+
+ const clientFetchSpan = clientTxnEvent.spans?.find(s => s.op === 'http.client');
+
+ expect(clientFetchSpan).toMatchObject({
+ description: expect.stringMatching(/^GET.*\/api\/users/),
+ op: 'http.client',
+ origin: 'auto.http.browser',
+ data: {
+ url: expect.stringContaining('/api/users'),
+ type: 'fetch',
+ 'http.method': 'GET',
+ 'http.response.status_code': 200,
+ 'network.protocol.version': '1.1',
+ 'network.protocol.name': 'http',
+ 'http.request.redirect_start': expect.any(Number),
+ 'http.request.fetch_start': expect.any(Number),
+ 'http.request.domain_lookup_start': expect.any(Number),
+ 'http.request.domain_lookup_end': expect.any(Number),
+ 'http.request.connect_start': expect.any(Number),
+ 'http.request.secure_connection_start': expect.any(Number),
+ 'http.request.connection_end': expect.any(Number),
+ 'http.request.request_start': expect.any(Number),
+ 'http.request.response_start': expect.any(Number),
+ 'http.request.response_end': expect.any(Number),
+ },
+ });
+ });
+
+ test('captures a navigation transaction directly after pageload', async ({ page }) => {
+ await page.goto('/');
+
+ const clientPageloadTxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.contexts?.trace?.op === 'pageload' && txnEvent?.tags?.runtime === 'browser';
+ });
+
+ const clientNavigationTxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return txnEvent?.contexts?.trace?.op === 'navigation' && txnEvent?.tags?.runtime === 'browser';
+ });
+
+ const navigationClickPromise = page.locator('#routeWithParamsLink').click();
+
+ const [pageloadTxnEvent, navigationTxnEvent, _] = await Promise.all([
+ clientPageloadTxnPromise,
+ clientNavigationTxnPromise,
+ navigationClickPromise,
+ ]);
+
+ expect(pageloadTxnEvent).toMatchObject({
+ transaction: '/',
+ tags: { runtime: 'browser' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'pageload',
+ origin: 'auto.pageload.sveltekit',
+ },
+ },
+ });
+
+ expect(navigationTxnEvent).toMatchObject({
+ transaction: '/users/[id]',
+ tags: { runtime: 'browser' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.sveltekit',
+ data: {
+ 'sentry.sveltekit.navigation.from': '/',
+ 'sentry.sveltekit.navigation.to': '/users/[id]',
+ 'sentry.sveltekit.navigation.type': 'link',
+ },
+ },
+ },
+ });
+
+ const routingSpans = navigationTxnEvent.spans?.filter(s => s.op === 'ui.sveltekit.routing');
+ expect(routingSpans).toHaveLength(1);
+
+ const routingSpan = routingSpans && routingSpans[0];
+ expect(routingSpan).toMatchObject({
+ op: 'ui.sveltekit.routing',
+ description: 'SvelteKit Route Change',
+ data: {
+ 'sentry.op': 'ui.sveltekit.routing',
+ 'sentry.origin': 'auto.ui.sveltekit',
+ 'sentry.sveltekit.navigation.from': '/',
+ 'sentry.sveltekit.navigation.to': '/users/[id]',
+ 'sentry.sveltekit.navigation.type': 'link',
+ },
+ });
+ });
+
+ test('captures one navigation transaction per redirect', async ({ page }) => {
+ await page.goto('/');
+
+ const clientNavigationRedirect1TxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return (
+ txnEvent?.contexts?.trace?.op === 'navigation' &&
+ txnEvent?.tags?.runtime === 'browser' &&
+ txnEvent?.transaction === '/redirect1'
+ );
+ });
+
+ const clientNavigationRedirect2TxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return (
+ txnEvent?.contexts?.trace?.op === 'navigation' &&
+ txnEvent?.tags?.runtime === 'browser' &&
+ txnEvent?.transaction === '/redirect2'
+ );
+ });
+
+ const clientNavigationRedirect3TxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ return (
+ txnEvent?.contexts?.trace?.op === 'navigation' &&
+ txnEvent?.tags?.runtime === 'browser' &&
+ txnEvent?.transaction === '/users/[id]'
+ );
+ });
+
+ const navigationClickPromise = page.locator('#redirectLink').click();
+
+ const [redirect1TxnEvent, redirect2TxnEvent, redirect3TxnEvent, _] = await Promise.all([
+ clientNavigationRedirect1TxnPromise,
+ clientNavigationRedirect2TxnPromise,
+ clientNavigationRedirect3TxnPromise,
+ navigationClickPromise,
+ ]);
+
+ expect(redirect1TxnEvent).toMatchObject({
+ transaction: '/redirect1',
+ tags: { runtime: 'browser' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.sveltekit',
+ data: {
+ 'sentry.origin': 'auto.navigation.sveltekit',
+ 'sentry.op': 'navigation',
+ 'sentry.source': 'route',
+ 'sentry.sveltekit.navigation.type': 'link',
+ 'sentry.sveltekit.navigation.from': '/',
+ 'sentry.sveltekit.navigation.to': '/redirect1',
+ 'sentry.sample_rate': 1,
+ },
+ },
+ },
+ });
+
+ const redirect1Spans = redirect1TxnEvent.spans?.filter(s => s.op === 'ui.sveltekit.routing');
+ expect(redirect1Spans).toHaveLength(1);
+
+ const redirect1Span = redirect1Spans && redirect1Spans[0];
+ expect(redirect1Span).toMatchObject({
+ op: 'ui.sveltekit.routing',
+ description: 'SvelteKit Route Change',
+ data: {
+ 'sentry.op': 'ui.sveltekit.routing',
+ 'sentry.origin': 'auto.ui.sveltekit',
+ 'sentry.sveltekit.navigation.from': '/',
+ 'sentry.sveltekit.navigation.to': '/redirect1',
+ 'sentry.sveltekit.navigation.type': 'link',
+ },
+ });
+
+ expect(redirect2TxnEvent).toMatchObject({
+ transaction: '/redirect2',
+ tags: { runtime: 'browser' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.sveltekit',
+ data: {
+ 'sentry.origin': 'auto.navigation.sveltekit',
+ 'sentry.op': 'navigation',
+ 'sentry.source': 'route',
+ 'sentry.sveltekit.navigation.type': 'goto',
+ 'sentry.sveltekit.navigation.from': '/',
+ 'sentry.sveltekit.navigation.to': '/redirect2',
+ 'sentry.sample_rate': 1,
+ },
+ },
+ },
+ });
+
+ const redirect2Spans = redirect2TxnEvent.spans?.filter(s => s.op === 'ui.sveltekit.routing');
+ expect(redirect2Spans).toHaveLength(1);
+
+ const redirect2Span = redirect2Spans && redirect2Spans[0];
+ expect(redirect2Span).toMatchObject({
+ op: 'ui.sveltekit.routing',
+ description: 'SvelteKit Route Change',
+ data: {
+ 'sentry.op': 'ui.sveltekit.routing',
+ 'sentry.origin': 'auto.ui.sveltekit',
+ 'sentry.sveltekit.navigation.from': '/',
+ 'sentry.sveltekit.navigation.to': '/redirect2',
+ 'sentry.sveltekit.navigation.type': 'goto',
+ },
+ });
+
+ expect(redirect3TxnEvent).toMatchObject({
+ transaction: '/users/[id]',
+ tags: { runtime: 'browser' },
+ transaction_info: { source: 'route' },
+ type: 'transaction',
+ contexts: {
+ trace: {
+ op: 'navigation',
+ origin: 'auto.navigation.sveltekit',
+ data: {
+ 'sentry.origin': 'auto.navigation.sveltekit',
+ 'sentry.op': 'navigation',
+ 'sentry.source': 'route',
+ 'sentry.sveltekit.navigation.type': 'goto',
+ 'sentry.sveltekit.navigation.from': '/',
+ 'sentry.sveltekit.navigation.to': '/users/[id]',
+ 'sentry.sample_rate': 1,
+ },
+ },
+ },
+ });
+
+ const redirect3Spans = redirect3TxnEvent.spans?.filter(s => s.op === 'ui.sveltekit.routing');
+ expect(redirect3Spans).toHaveLength(1);
+
+ const redirect3Span = redirect3Spans && redirect3Spans[0];
+ expect(redirect3Span).toMatchObject({
+ op: 'ui.sveltekit.routing',
+ description: 'SvelteKit Route Change',
+ data: {
+ 'sentry.op': 'ui.sveltekit.routing',
+ 'sentry.origin': 'auto.ui.sveltekit',
+ 'sentry.sveltekit.navigation.from': '/',
+ 'sentry.sveltekit.navigation.to': '/users/[id]',
+ 'sentry.sveltekit.navigation.type': 'goto',
+ },
+ });
+ });
+});
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/utils.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/utils.ts
new file mode 100644
index 000000000000..5dcc01bbe7f8
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/utils.ts
@@ -0,0 +1,49 @@
+import { Page } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/event-proxy-server';
+
+/**
+ * Helper function that waits for the initial pageload to complete.
+ *
+ * This function
+ * - loads the given route ("/" by default)
+ * - waits for SvelteKit's hydration
+ * - waits for the pageload transaction to be sent (doesn't assert on it though)
+ *
+ * Useful for tests that test outcomes of _navigations_ after an initial pageload.
+ * Waiting on the pageload transaction excludes edge cases where navigations occur
+ * so quickly that the pageload idle transaction is still active. This might lead
+ * to cases where the routing span would be attached to the pageload transaction
+ * and hence eliminates a lot of flakiness.
+ *
+ */
+export async function waitForInitialPageload(
+ page: Page,
+ opts?: { route?: string; parameterizedRoute?: string; debug?: boolean },
+) {
+ const route = opts?.route ?? '/';
+ const txnName = opts?.parameterizedRoute ?? route;
+ const debug = opts?.debug ?? false;
+
+ const clientPageloadTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
+ debug &&
+ console.log({
+ txn: txnEvent?.transaction,
+ op: txnEvent.contexts?.trace?.op,
+ trace: txnEvent.contexts?.trace?.trace_id,
+ span: txnEvent.contexts?.trace?.span_id,
+ parent: txnEvent.contexts?.trace?.parent_span_id,
+ });
+
+ return txnEvent?.transaction === txnName && txnEvent.contexts?.trace?.op === 'pageload';
+ });
+
+ await Promise.all([
+ page.goto(route),
+ // the test app adds the "hydrated" class to the body when hydrating
+ page.waitForSelector('body.hydrated'),
+ // also waiting for the initial pageload txn so that later navigations don't interfere
+ clientPageloadTxnEventPromise,
+ ]);
+
+ debug && console.log('hydrated');
+}
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tsconfig.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tsconfig.json
new file mode 100644
index 000000000000..ba6aa4e6610a
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "./.svelte-kit/tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "moduleResolution": "bundler"
+ },
+ // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
+ // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
+ //
+ // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
+ // from the referenced tsconfig.json - TypeScript does not merge them in
+}
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/vite.config.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/vite.config.ts
new file mode 100644
index 000000000000..1a410bee7e11
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/vite.config.ts
@@ -0,0 +1,12 @@
+import { sentrySvelteKit } from '@sentry/sveltekit';
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ plugins: [
+ sentrySvelteKit({
+ autoUploadSourceMaps: false,
+ }),
+ sveltekit(),
+ ],
+});
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/+page.svelte
index 71a0ce872185..e7788b6433cd 100644
--- a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/+page.svelte
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/+page.svelte
@@ -35,4 +35,7 @@
Nav 2
+
+ Component Tracking
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/+page.svelte
new file mode 100644
index 000000000000..eff3fa3f2e8d
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/+page.svelte
@@ -0,0 +1,15 @@
+
+Demonstrating Component Tracking
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component1.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component1.svelte
new file mode 100644
index 000000000000..a675711e4b68
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component1.svelte
@@ -0,0 +1,10 @@
+
+Howdy, I'm component 1
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component2.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component2.svelte
new file mode 100644
index 000000000000..2b2f38308077
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component2.svelte
@@ -0,0 +1,9 @@
+
+Howdy, I'm component 2
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component3.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component3.svelte
new file mode 100644
index 000000000000..9b4e028f78e7
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component3.svelte
@@ -0,0 +1,6 @@
+
+
+Howdy, I'm component 3
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/test/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2/test/performance.client.test.ts
index 11e647ff07ff..403701a075be 100644
--- a/dev-packages/e2e-tests/test-applications/sveltekit-2/test/performance.client.test.ts
+++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/test/performance.client.test.ts
@@ -66,4 +66,69 @@ test.describe('client-specific performance events', () => {
expect(navigationTxn2Event.contexts?.trace?.trace_id).not.toBe(navigationTxn3Event.contexts?.trace?.trace_id);
expect(navigationTxn1Event.contexts?.trace?.trace_id).not.toBe(navigationTxn3Event.contexts?.trace?.trace_id);
});
+
+ test('records manually added component tracking spans', async ({ page }) => {
+ const componentTxnEventPromise = waitForTransaction('sveltekit-2', txnEvent => {
+ return txnEvent?.transaction === '/components';
+ });
+
+ await waitForInitialPageload(page);
+
+ await page.getByText('Component Tracking').click();
+
+ const componentTxnEvent = await componentTxnEventPromise;
+
+ expect(componentTxnEvent.spans).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ ]),
+ );
+ });
});
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/+page.svelte
index 62dbf7856ab7..31f6cb802950 100644
--- a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/+page.svelte
+++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/+page.svelte
@@ -26,4 +26,7 @@
Route with nested fetch in server load
+
+ Component Tracking
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/+page.svelte
new file mode 100644
index 000000000000..eff3fa3f2e8d
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/+page.svelte
@@ -0,0 +1,15 @@
+
+Demonstrating Component Tracking
+
+
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component1.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component1.svelte
new file mode 100644
index 000000000000..a675711e4b68
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component1.svelte
@@ -0,0 +1,10 @@
+
+Howdy, I'm component 1
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component2.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component2.svelte
new file mode 100644
index 000000000000..2b2f38308077
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component2.svelte
@@ -0,0 +1,9 @@
+
+Howdy, I'm component 2
+
+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component3.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component3.svelte
new file mode 100644
index 000000000000..9b4e028f78e7
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component3.svelte
@@ -0,0 +1,6 @@
+
+
+Howdy, I'm component 3
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/test/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit/test/performance.client.test.ts
new file mode 100644
index 000000000000..d7107d5e7744
--- /dev/null
+++ b/dev-packages/e2e-tests/test-applications/sveltekit/test/performance.client.test.ts
@@ -0,0 +1,68 @@
+import { expect, test } from '@playwright/test';
+import { waitForTransaction } from '@sentry-internal/event-proxy-server';
+import { waitForInitialPageload } from '../utils.js';
+
+test('records manually added component tracking spans', async ({ page }) => {
+ const componentTxnEventPromise = waitForTransaction('sveltekit', txnEvent => {
+ return txnEvent?.transaction === '/components';
+ });
+
+ await waitForInitialPageload(page);
+
+ await page.getByText('Component Tracking').click();
+
+ const componentTxnEvent = await componentTxnEventPromise;
+
+ expect(componentTxnEvent.spans).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.init',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ expect.objectContaining({
+ data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' },
+ description: '',
+ op: 'ui.svelte.update',
+ origin: 'auto.ui.svelte',
+ }),
+ ]),
+ );
+});
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 2117691bfbf7..dfa9be17d144 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -49,7 +49,7 @@
"magic-string": "^0.30.0"
},
"peerDependencies": {
- "svelte": "3.x || 4.x"
+ "svelte": "3.x || 4.x || 5.x"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "1.4.0",
diff --git a/packages/svelte/src/performance.ts b/packages/svelte/src/performance.ts
index 100f9f732ddc..acbf2576b540 100644
--- a/packages/svelte/src/performance.ts
+++ b/packages/svelte/src/performance.ts
@@ -1,7 +1,6 @@
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getActiveSpan } from '@sentry/browser';
import type { Span } from '@sentry/types';
import { afterUpdate, beforeUpdate, onMount } from 'svelte';
-import { current_component } from 'svelte/internal';
import { startInactiveSpan } from '@sentry/core';
import { DEFAULT_COMPONENT_NAME, UI_SVELTE_INIT, UI_SVELTE_UPDATE } from './constants';
@@ -17,7 +16,7 @@ const defaultTrackComponentOptions: {
};
/**
- * Tracks the Svelte component's intialization and mounting operation as well as
+ * Tracks the Svelte component's initialization and mounting operation as well as
* updates and records them as spans.
*
* This function is injected automatically into your Svelte components' code
@@ -30,9 +29,7 @@ export function trackComponent(options?: TrackComponentOptions): void {
const customComponentName = mergedOptions.componentName;
- // current_component.ctor.name is likely to give us the component's name automatically
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- const componentName = `<${customComponentName || current_component.constructor.name || DEFAULT_COMPONENT_NAME}>`;
+ const componentName = `<${customComponentName || DEFAULT_COMPONENT_NAME}>`;
if (mergedOptions.trackInit) {
recordInitSpan(componentName);
diff --git a/packages/svelte/test/performance.test.ts b/packages/svelte/test/performance.test.ts
index 53cf80d04d17..99ac99698682 100644
--- a/packages/svelte/test/performance.test.ts
+++ b/packages/svelte/test/performance.test.ts
@@ -53,7 +53,7 @@ describe('Sentry.trackComponent()', () => {
'sentry.op': 'ui.svelte.init',
'sentry.origin': 'auto.ui.svelte',
},
- description: '',
+ description: '',
op: 'ui.svelte.init',
origin: 'auto.ui.svelte',
parent_span_id: rootSpanId,
@@ -68,7 +68,7 @@ describe('Sentry.trackComponent()', () => {
'sentry.op': 'ui.svelte.update',
'sentry.origin': 'auto.ui.svelte',
},
- description: '',
+ description: '',
op: 'ui.svelte.update',
origin: 'auto.ui.svelte',
parent_span_id: rootSpanId,
@@ -108,7 +108,7 @@ describe('Sentry.trackComponent()', () => {
'sentry.op': 'ui.svelte.init',
'sentry.origin': 'auto.ui.svelte',
},
- description: '',
+ description: '',
op: 'ui.svelte.init',
origin: 'auto.ui.svelte',
parent_span_id: rootSpanId,
@@ -123,7 +123,7 @@ describe('Sentry.trackComponent()', () => {
'sentry.op': 'ui.svelte.update',
'sentry.origin': 'auto.ui.svelte',
},
- description: '',
+ description: '',
op: 'ui.svelte.update',
origin: 'auto.ui.svelte',
parent_span_id: rootSpanId,
@@ -138,7 +138,7 @@ describe('Sentry.trackComponent()', () => {
'sentry.op': 'ui.svelte.update',
'sentry.origin': 'auto.ui.svelte',
},
- description: '',
+ description: '',
op: 'ui.svelte.update',
origin: 'auto.ui.svelte',
parent_span_id: rootSpanId,
diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts
index 18e734d01677..81d1ac96aa2e 100644
--- a/packages/sveltekit/src/index.types.ts
+++ b/packages/sveltekit/src/index.types.ts
@@ -52,3 +52,5 @@ export declare function flush(timeout?: number | undefined): PromiseLike;
diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts
index 9a6c22b2dfe2..07c297de608b 100644
--- a/packages/sveltekit/src/server/index.ts
+++ b/packages/sveltekit/src/server/index.ts
@@ -83,3 +83,12 @@ export { init } from './sdk';
export { handleErrorWithSentry } from './handleError';
export { wrapLoadWithSentry, wrapServerLoadWithSentry } from './load';
export { sentryHandle } from './handle';
+
+/**
+ * Tracks the Svelte component's initialization and mounting operation as well as
+ * updates and records them as spans. These spans are only recorded on the client-side.
+ * Sever-side, during SSR, this function will not record any spans.
+ */
+export function trackComponent(_options?: unknown): void {
+ // no-op on the server side
+}