diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/.gitignore b/packages/e2e-tests/test-applications/nextjs-13-app-dir/.gitignore
new file mode 100644
index 000000000000..35b1048ce099
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/.gitignore
@@ -0,0 +1,43 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+!*.d.ts
+
+# Sentry
+.sentryclirc
+
+.vscode
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/.npmrc b/packages/e2e-tests/test-applications/nextjs-13-app-dir/.npmrc
new file mode 100644
index 000000000000..c6b3ef9b3eaa
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/.npmrc
@@ -0,0 +1,2 @@
+@sentry:registry=http://localhost:4873
+@sentry-internal:registry=http://localhost:4873
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/head.tsx b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/head.tsx
new file mode 100644
index 000000000000..f11b259697ff
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/head.tsx
@@ -0,0 +1,10 @@
+export default function Head() {
+ return (
+ <>
+
Create Next App
+
+
+
+ >
+ );
+}
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/layout.tsx b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/layout.tsx
new file mode 100644
index 000000000000..f3ef34cd8b91
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/layout.tsx
@@ -0,0 +1,7 @@
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/page.tsx b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/page.tsx
new file mode 100644
index 000000000000..1caf96ed7786
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/page.tsx
@@ -0,0 +1,22 @@
+'use client';
+
+import * as Sentry from '@sentry/nextjs';
+import Link from 'next/link';
+
+export default function Home() {
+ return (
+
+ {
+ Sentry.captureException(new Error('I am a click error!'));
+ }}
+ />
+
+ navigate
+
+
+ );
+}
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/user/[id]/page.tsx b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/user/[id]/page.tsx
new file mode 100644
index 000000000000..bdb52ea5547a
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/app/user/[id]/page.tsx
@@ -0,0 +1,4 @@
+export default async function Home() {
+ const dynamid = await (await fetch('http://example.com', { cache: 'no-store' })).text(); // do a fetch request so that this server component is always rendered when requested
+ return I am a blank page :) {dynamid}
;
+}
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/globals.d.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/globals.d.ts
new file mode 100644
index 000000000000..109dbcd55648
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/globals.d.ts
@@ -0,0 +1,4 @@
+interface Window {
+ recordedTransactions?: string[];
+ capturedExceptionId?: string;
+}
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/next-env.d.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/next-env.d.ts
new file mode 100644
index 000000000000..7aa8e8ef74e1
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/next-env.d.ts
@@ -0,0 +1,6 @@
+///
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/next.config.js b/packages/e2e-tests/test-applications/nextjs-13-app-dir/next.config.js
new file mode 100644
index 000000000000..b4110295ace3
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/next.config.js
@@ -0,0 +1,33 @@
+// This file sets a custom webpack configuration to use your Next.js app
+// with Sentry.
+// https://nextjs.org/docs/api-reference/next.config.js/introduction
+// https://docs.sentry.io/platforms/javascript/guides/nextjs/
+
+const { withSentryConfig } = require('@sentry/nextjs');
+
+const moduleExports = {
+ experimental: {
+ appDir: true,
+ },
+};
+
+const sentryWebpackPluginOptions = {
+ // Additional config options for the Sentry Webpack plugin. Keep in mind that
+ // the following options are set automatically, and overriding them is not
+ // recommended:
+ // release, url, org, project, authToken, configFile, stripPrefix,
+ // urlPrefix, include, ignore
+
+ silent: true, // Suppresses all logs
+ // For all available options, see:
+ // https://github.com/getsentry/sentry-webpack-plugin#options.
+
+ // We're not testing source map uploads at the moment.
+ dryRun: true,
+};
+
+// Make sure adding Sentry options is the last code to run before exporting, to
+// ensure that your source maps include changes from all other Webpack plugins
+module.exports = withSentryConfig(moduleExports, sentryWebpackPluginOptions, {
+ hideSourceMaps: true,
+});
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/package.json b/packages/e2e-tests/test-applications/nextjs-13-app-dir/package.json
new file mode 100644
index 000000000000..2bb563d85e25
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "create-next-app",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint",
+ "test": "playwright test"
+ },
+ "dependencies": {
+ "@next/font": "13.0.7",
+ "@sentry/nextjs": "*",
+ "@types/node": "18.11.17",
+ "@types/react": "18.0.26",
+ "@types/react-dom": "18.0.9",
+ "next": "13.2.1",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
+ "typescript": "4.9.4"
+ },
+ "devDependencies": {
+ "ts-node": "10.9.1",
+ "@playwright/test": "^1.27.1"
+ }
+}
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/playwright.config.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/playwright.config.ts
new file mode 100644
index 000000000000..55e54aefaefa
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/playwright.config.ts
@@ -0,0 +1,62 @@
+import type { PlaywrightTestConfig } from '@playwright/test';
+import { devices } from '@playwright/test';
+
+/**
+ * See https://playwright.dev/docs/test-configuration.
+ */
+const config: PlaywrightTestConfig = {
+ testDir: './tests',
+ /* Maximum time one test can run for. */
+ timeout: 60 * 1000,
+ expect: {
+ /**
+ * Maximum time expect() should wait for the condition to be met.
+ * For example in `await expect(locator).toHaveText();`
+ */
+ timeout: 5000,
+ },
+ /* Run tests in files in parallel */
+ fullyParallel: true,
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: !!process.env.CI,
+ /* Retry on CI only */
+ retries: 0,
+ /* Opt out of parallel tests on CI. */
+ workers: 1,
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: 'dot',
+ /* 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:3000',
+
+ /* 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: 'yarn start',
+ port: 3000,
+ },
+ {
+ command: 'yarn ts-node-script start-event-proxy.ts',
+ port: 27496,
+ },
+ ],
+};
+
+export default config;
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.client.config.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.client.config.ts
new file mode 100644
index 000000000000..af39dd76f384
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.client.config.ts
@@ -0,0 +1,7 @@
+import * as Sentry from '@sentry/nextjs';
+
+Sentry.init({
+ dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
+ tunnel: 'http://localhost:27496/', // proxy server
+ tracesSampleRate: 1.0,
+});
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.edge.config.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.edge.config.ts
new file mode 100644
index 000000000000..af39dd76f384
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.edge.config.ts
@@ -0,0 +1,7 @@
+import * as Sentry from '@sentry/nextjs';
+
+Sentry.init({
+ dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
+ tunnel: 'http://localhost:27496/', // proxy server
+ tracesSampleRate: 1.0,
+});
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.server.config.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.server.config.ts
new file mode 100644
index 000000000000..af39dd76f384
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/sentry.server.config.ts
@@ -0,0 +1,7 @@
+import * as Sentry from '@sentry/nextjs';
+
+Sentry.init({
+ dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
+ tunnel: 'http://localhost:27496/', // proxy server
+ tracesSampleRate: 1.0,
+});
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/start-event-proxy.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/start-event-proxy.ts
new file mode 100644
index 000000000000..9c959e1e180e
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/start-event-proxy.ts
@@ -0,0 +1,6 @@
+import { startEventProxyServer, waitForTransaction } from '../../test-utils/event-proxy-server';
+
+startEventProxyServer({
+ port: 27496,
+ proxyServerName: 'nextjs-13-app-dir',
+});
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/test-recipe.json b/packages/e2e-tests/test-applications/nextjs-13-app-dir/test-recipe.json
new file mode 100644
index 000000000000..bfdef9508d7b
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/test-recipe.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "../../test-recipe-schema.json",
+ "testApplicationName": "nextjs-13-app-dir",
+ "buildCommand": "yarn install --pure-lockfile && npx playwright install && yarn build",
+ "tests": [
+ {
+ "testName": "Playwright tests",
+ "testCommand": "yarn test"
+ }
+ ]
+}
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/exceptions.test.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/exceptions.test.ts
new file mode 100644
index 000000000000..d1b88470fd37
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/exceptions.test.ts
@@ -0,0 +1,50 @@
+import { test, expect } from '@playwright/test';
+import { waitForError } from '../../../test-utils/event-proxy-server';
+import axios, { AxiosError } from 'axios';
+
+const authToken = process.env.E2E_TEST_AUTH_TOKEN;
+const sentryTestOrgSlug = process.env.E2E_TEST_SENTRY_ORG_SLUG;
+const sentryTestProject = process.env.E2E_TEST_SENTRY_TEST_PROJECT;
+const EVENT_POLLING_TIMEOUT = 30_000;
+
+test('Sends a client-side exception to Sentry', async ({ page }) => {
+ await page.goto('/');
+
+ const errorEventPromise = waitForError('nextjs-13-app-dir', errorEvent => {
+ return errorEvent?.exception?.values?.[0]?.value === 'I am a click error!';
+ });
+
+ const exceptionButton = page.locator('id=exception-button');
+ await exceptionButton.click();
+
+ const errorEvent = await errorEventPromise;
+ const exceptionEventId = errorEvent.event_id;
+
+ await expect
+ .poll(
+ async () => {
+ try {
+ const response = await axios.get(
+ `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${exceptionEventId}/`,
+ { headers: { Authorization: `Bearer ${authToken}` } },
+ );
+
+ return response.status;
+ } catch (e) {
+ if (e instanceof AxiosError && e.response) {
+ if (e.response.status !== 404) {
+ throw e;
+ } else {
+ return e.response.status;
+ }
+ } else {
+ throw e;
+ }
+ }
+ },
+ {
+ timeout: EVENT_POLLING_TIMEOUT,
+ },
+ )
+ .toBe(200);
+});
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/transactions.test.ts b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/transactions.test.ts
new file mode 100644
index 000000000000..c52ab35475bd
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tests/transactions.test.ts
@@ -0,0 +1,89 @@
+import { test, expect } from '@playwright/test';
+import { waitForTransaction } from '../../../test-utils/event-proxy-server';
+import axios, { AxiosError } from 'axios';
+
+const authToken = process.env.E2E_TEST_AUTH_TOKEN;
+const sentryTestOrgSlug = process.env.E2E_TEST_SENTRY_ORG_SLUG;
+const sentryTestProject = process.env.E2E_TEST_SENTRY_TEST_PROJECT;
+const EVENT_POLLING_TIMEOUT = 30_000;
+
+test('Sends a pageload transaction', async ({ page }) => {
+ const pageloadTransactionEventPromise = waitForTransaction('nextjs-13-app-dir', transactionEvent => {
+ return transactionEvent?.contexts?.trace?.op === 'pageload' && transactionEvent?.transaction === '/';
+ });
+
+ await page.goto('/');
+
+ const transactionEvent = await pageloadTransactionEventPromise;
+ const transactionEventId = transactionEvent.event_id;
+
+ await expect
+ .poll(
+ async () => {
+ try {
+ const response = await axios.get(
+ `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${transactionEventId}/`,
+ { headers: { Authorization: `Bearer ${authToken}` } },
+ );
+
+ return response.status;
+ } catch (e) {
+ if (e instanceof AxiosError && e.response) {
+ if (e.response.status !== 404) {
+ throw e;
+ } else {
+ return e.response.status;
+ }
+ } else {
+ throw e;
+ }
+ }
+ },
+ {
+ timeout: EVENT_POLLING_TIMEOUT,
+ },
+ )
+ .toBe(200);
+});
+
+test('Sends a transaction for a server component', async ({ page }) => {
+ const serverComponentTransactionPromise = waitForTransaction('nextjs-13-app-dir', transactionEvent => {
+ return (
+ transactionEvent?.contexts?.trace?.op === 'function.nextjs' &&
+ transactionEvent?.transaction === 'Page Server Component (/user/[id])'
+ );
+ });
+
+ await page.goto('/user/4');
+
+ const transactionEvent = await serverComponentTransactionPromise;
+ const transactionEventId = transactionEvent.event_id;
+
+ await expect
+ .poll(
+ async () => {
+ try {
+ const response = await axios.get(
+ `https://sentry.io/api/0/projects/${sentryTestOrgSlug}/${sentryTestProject}/events/${transactionEventId}/`,
+ { headers: { Authorization: `Bearer ${authToken}` } },
+ );
+
+ return response.status;
+ } catch (e) {
+ if (e instanceof AxiosError && e.response) {
+ if (e.response.status !== 404) {
+ throw e;
+ } else {
+ return e.response.status;
+ }
+ } else {
+ throw e;
+ }
+ }
+ },
+ {
+ timeout: EVENT_POLLING_TIMEOUT,
+ },
+ )
+ .toBe(200);
+});
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/tsconfig.json b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tsconfig.json
new file mode 100644
index 000000000000..bacd391b697e
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/tsconfig.json
@@ -0,0 +1,30 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ]
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next.config.js", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"],
+ "ts-node": {
+ "compilerOptions": {
+ "module": "CommonJS"
+ }
+ }
+}
diff --git a/packages/e2e-tests/test-applications/nextjs-13-app-dir/yarn.lock b/packages/e2e-tests/test-applications/nextjs-13-app-dir/yarn.lock
new file mode 100644
index 000000000000..14727ded24f5
--- /dev/null
+++ b/packages/e2e-tests/test-applications/nextjs-13-app-dir/yarn.lock
@@ -0,0 +1,369 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@cspotcode/source-map-support@^0.8.0":
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
+ integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
+ dependencies:
+ "@jridgewell/trace-mapping" "0.3.9"
+
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+ integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.4.14"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+ integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@0.3.9":
+ version "0.3.9"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
+ integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@next/env@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.1.tgz#082d42cfc0c794e9185d7b4133d71440ba2e795d"
+ integrity sha512-Hq+6QZ6kgmloCg8Kgrix+4F0HtvLqVK3FZAnlAoS0eonaDemHe1Km4kwjSWRE3JNpJNcKxFHF+jsZrYo0SxWoQ==
+
+"@next/font@13.0.7":
+ version "13.0.7"
+ resolved "https://registry.yarnpkg.com/@next/font/-/font-13.0.7.tgz#e0046376edb0ce592d9cfddea8f4ab321eb1515a"
+ integrity sha512-39SzuoMI6jbrIzPs3KtXdKX03OrVp6Y7kRHcoVmOg69spiBzruPJ5x5DQSfN+OXqznbvVBNZBXnmdnSqs3qXiA==
+
+"@next/swc-android-arm-eabi@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.1.tgz#67f2580fbbe05ee006220688972c5e3a555fc741"
+ integrity sha512-Yua7mUpEd1wzIT6Jjl3dpRizIfGp9NR4F2xeRuQv+ae+SDI1Em2WyM9m46UL+oeW5GpMiEHoaBagr47RScZFmQ==
+
+"@next/swc-android-arm64@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.1.tgz#460a02b69eb23bb5f402266bcea9cadae59415c1"
+ integrity sha512-Bifcr2f6VwInOdq1uH/9lp8fH7Nf7XGkIx4XceVd32LPJqG2c6FZU8ZRBvTdhxzXVpt5TPtuXhOP4Ij9UPqsVw==
+
+"@next/swc-darwin-arm64@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.1.tgz#8b8530ff417802027471aee2419f78a58a863ccb"
+ integrity sha512-gvqm+fGMYxAkwBapH0Vvng5yrb6HTkIvZfY4oEdwwYrwuLdkjqnJygCMgpNqIFmAHSXgtlWxfYv1VC8sjN81Kw==
+
+"@next/swc-darwin-x64@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.1.tgz#80aebb3329a1e4568a28de1ee177780b3d50330c"
+ integrity sha512-HGqVqmaZWj6zomqOZUVbO5NhlABL0iIaxTmd0O5B0MoMa5zpDGoaHSG+fxgcWMXcGcxmUNchv1NfNOYiTKoHOg==
+
+"@next/swc-freebsd-x64@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.1.tgz#250ea2ab7e1734f22d11c677c463fab9ac33a516"
+ integrity sha512-N/a4JarAq+E+g+9K2ywJUmDIgU2xs2nA+BBldH0oq4zYJMRiUhL0iaN9G4e72VmGOJ61L/3W6VN8RIUOwTLoqQ==
+
+"@next/swc-linux-arm-gnueabihf@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.1.tgz#fe6bb29ed348a5f8ecae3740df22a8d8130c474a"
+ integrity sha512-WaFoerF/eRbhbE57TaIGJXbQAERADZ/RZ45u6qox9beb5xnWsyYgzX+WuN7Tkhyvga0/aMuVYFzS9CEay7D+bw==
+
+"@next/swc-linux-arm64-gnu@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.1.tgz#4781b927fc5e421f3cea2b29e5d38e5e4837b198"
+ integrity sha512-R+Jhc1/RJTnncE9fkePboHDNOCm1WJ8daanWbjKhfPySMyeniKYRwGn5SLYW3S8YlRS0QVdZaaszDSZWgUcsmA==
+
+"@next/swc-linux-arm64-musl@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.1.tgz#c2ba0a121b0255ba62450916bc70e6d0e26cbc98"
+ integrity sha512-oI1UfZPidGAVddlL2eOTmfsuKV9EaT1aktIzVIxIAgxzQSdwsV371gU3G55ggkurzfdlgF3GThFePDWF0d8dmw==
+
+"@next/swc-linux-x64-gnu@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.1.tgz#573c220f8b087e5d131d1fba58d3e1a670b220ad"
+ integrity sha512-PCygPwrQmS+7WUuAWWioWMZCzZm4PG91lfRxToLDg7yIm/3YfAw5N2EK2TaM9pzlWdvHQAqRMX/oLvv027xUiA==
+
+"@next/swc-linux-x64-musl@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.1.tgz#950b5bb920b322ca7b447efbd12a9c7a10c3a642"
+ integrity sha512-sUAKxo7CFZYGHNxheGh9nIBElLYBM6md/liEGfOTwh/xna4/GTTcmkGWkF7PdnvaYNgcPIQgHIMYiAa6yBKAVw==
+
+"@next/swc-win32-arm64-msvc@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.1.tgz#dbff3c4f5a3812a7059dac05804148a0f98682db"
+ integrity sha512-qDmyEjDBpl/vBXxuOOKKWmPQOcARcZIMach1s7kjzaien0SySut/PHRlj56sosa81Wt4hTGhfhZ1R7g1n7+B8w==
+
+"@next/swc-win32-ia32-msvc@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.1.tgz#7d2c17be7b8d9963984f5c15cc2588127101f620"
+ integrity sha512-2joqFQ81ZYPg6DcikIzQn3DgjKglNhPAozx6dL5sCNkr1CPMD0YIkJgT3CnYyMHQ04Qi3Npv0XX3MD6LJO8OCA==
+
+"@next/swc-win32-x64-msvc@13.2.1":
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.1.tgz#09713c6a925461f414e89422851326d1625bd4d2"
+ integrity sha512-r3+0fSaIZT6N237iMzwUhfNwjhAFvXjqB+4iuW+wcpxW+LHm1g/IoxN8eSRcb8jPItC86JxjAxpke0QL97qd6g==
+
+"@playwright/test@^1.27.1":
+ version "1.31.1"
+ resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.31.1.tgz#39d6873dc46af135f12451d79707db7d1357455d"
+ integrity sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA==
+ dependencies:
+ "@types/node" "*"
+ playwright-core "1.31.1"
+ optionalDependencies:
+ fsevents "2.3.2"
+
+"@swc/helpers@0.4.14":
+ version "0.4.14"
+ resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74"
+ integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==
+ dependencies:
+ tslib "^2.4.0"
+
+"@tsconfig/node10@^1.0.7":
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
+ integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
+
+"@tsconfig/node12@^1.0.7":
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d"
+ integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
+
+"@tsconfig/node14@^1.0.0":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1"
+ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
+
+"@tsconfig/node16@^1.0.2":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
+ integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
+
+"@types/node@*":
+ version "18.14.1"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.14.1.tgz#90dad8476f1e42797c49d6f8b69aaf9f876fc69f"
+ integrity sha512-QH+37Qds3E0eDlReeboBxfHbX9omAcBCXEzswCu6jySP642jiM3cYSIkU/REqwhCUqXdonHFuBfJDiAJxMNhaQ==
+
+"@types/node@18.11.17":
+ version "18.11.17"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.17.tgz#5c009e1d9c38f4a2a9d45c0b0c493fe6cdb4bcb5"
+ integrity sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==
+
+"@types/prop-types@*":
+ version "15.7.5"
+ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
+ integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
+
+"@types/react-dom@18.0.9":
+ version "18.0.9"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.9.tgz#ffee5e4bfc2a2f8774b15496474f8e7fe8d0b504"
+ integrity sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==
+ dependencies:
+ "@types/react" "*"
+
+"@types/react@*":
+ version "18.0.28"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065"
+ integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
+"@types/react@18.0.26":
+ version "18.0.26"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917"
+ integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
+"@types/scheduler@*":
+ version "0.16.2"
+ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
+ integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
+
+acorn-walk@^8.1.1:
+ version "8.2.0"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
+ integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
+
+acorn@^8.4.1:
+ version "8.8.2"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
+ integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
+
+arg@^4.1.0:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
+ integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
+
+caniuse-lite@^1.0.30001406:
+ version "1.0.30001457"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001457.tgz#6af34bb5d720074e2099432aa522c21555a18301"
+ integrity sha512-SDIV6bgE1aVbK6XyxdURbUE89zY7+k1BBBaOwYwkNCglXlel/E7mELiHC64HQ+W0xSKlqWhV9Wh7iHxUjMs4fA==
+
+client-only@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
+ integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
+
+create-require@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
+ integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
+
+csstype@^3.0.2:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9"
+ integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==
+
+diff@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
+ integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+
+fsevents@2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+"js-tokens@^3.0.0 || ^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+loose-envify@^1.1.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
+make-error@^1.1.1:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
+ integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
+
+nanoid@^3.3.4:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
+ integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
+
+next@13.2.1:
+ version "13.2.1"
+ resolved "https://registry.yarnpkg.com/next/-/next-13.2.1.tgz#34d823f518632b36379863228ed9f861c335b9c0"
+ integrity sha512-qhgJlDtG0xidNViJUPeQHLGJJoT4zDj/El7fP3D3OzpxJDUfxsm16cK4WTMyvSX1ciIfAq05u+0HqFAa+VJ+Hg==
+ dependencies:
+ "@next/env" "13.2.1"
+ "@swc/helpers" "0.4.14"
+ caniuse-lite "^1.0.30001406"
+ postcss "8.4.14"
+ styled-jsx "5.1.1"
+ optionalDependencies:
+ "@next/swc-android-arm-eabi" "13.2.1"
+ "@next/swc-android-arm64" "13.2.1"
+ "@next/swc-darwin-arm64" "13.2.1"
+ "@next/swc-darwin-x64" "13.2.1"
+ "@next/swc-freebsd-x64" "13.2.1"
+ "@next/swc-linux-arm-gnueabihf" "13.2.1"
+ "@next/swc-linux-arm64-gnu" "13.2.1"
+ "@next/swc-linux-arm64-musl" "13.2.1"
+ "@next/swc-linux-x64-gnu" "13.2.1"
+ "@next/swc-linux-x64-musl" "13.2.1"
+ "@next/swc-win32-arm64-msvc" "13.2.1"
+ "@next/swc-win32-ia32-msvc" "13.2.1"
+ "@next/swc-win32-x64-msvc" "13.2.1"
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+playwright-core@1.31.1:
+ version "1.31.1"
+ resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.31.1.tgz#4deeebbb8fb73b512593fe24bea206d8fd85ff7f"
+ integrity sha512-JTyX4kV3/LXsvpHkLzL2I36aCdml4zeE35x+G5aPc4bkLsiRiQshU5lWeVpHFAuC8xAcbI6FDcw/8z3q2xtJSQ==
+
+postcss@8.4.14:
+ version "8.4.14"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
+ integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
+ dependencies:
+ nanoid "^3.3.4"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
+react-dom@18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+ integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
+ dependencies:
+ loose-envify "^1.1.0"
+ scheduler "^0.23.0"
+
+react@18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
+ integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
+ dependencies:
+ loose-envify "^1.1.0"
+
+scheduler@^0.23.0:
+ version "0.23.0"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
+ integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
+ dependencies:
+ loose-envify "^1.1.0"
+
+source-map-js@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+styled-jsx@5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f"
+ integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==
+ dependencies:
+ client-only "0.0.1"
+
+ts-node@10.9.1:
+ version "10.9.1"
+ resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
+ integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
+ dependencies:
+ "@cspotcode/source-map-support" "^0.8.0"
+ "@tsconfig/node10" "^1.0.7"
+ "@tsconfig/node12" "^1.0.7"
+ "@tsconfig/node14" "^1.0.0"
+ "@tsconfig/node16" "^1.0.2"
+ acorn "^8.4.1"
+ acorn-walk "^8.1.1"
+ arg "^4.1.0"
+ create-require "^1.1.0"
+ diff "^4.0.1"
+ make-error "^1.1.1"
+ v8-compile-cache-lib "^3.0.1"
+ yn "3.1.1"
+
+tslib@^2.4.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
+ integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
+
+typescript@4.9.4:
+ version "4.9.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78"
+ integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
+
+v8-compile-cache-lib@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
+ integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
+
+yn@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
+ integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
diff --git a/packages/nextjs/rollup.npm.config.js b/packages/nextjs/rollup.npm.config.js
index 63f420f466ad..5b23aa2fa0a4 100644
--- a/packages/nextjs/rollup.npm.config.js
+++ b/packages/nextjs/rollup.npm.config.js
@@ -16,7 +16,7 @@ export default [
// prevent this internal nextjs code from ending up in our built package (this doesn't happen automatially because
// the name doesn't match an SDK dependency)
- packageSpecificConfig: { external: ['next/router', 'next/constants'] },
+ packageSpecificConfig: { external: ['next/router', 'next/constants', 'next/headers'] },
}),
),
...makeNPMConfigVariants(
@@ -41,7 +41,7 @@ export default [
// make it so Rollup calms down about the fact that we're combining default and named exports
exports: 'named',
},
- external: ['@sentry/nextjs', '__SENTRY_WRAPPING_TARGET_FILE__'],
+ external: ['@sentry/nextjs', 'next/headers', '__SENTRY_WRAPPING_TARGET_FILE__'],
},
}),
),
diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts
index 38700f1a02b4..9894067d69e2 100644
--- a/packages/nextjs/src/client/index.ts
+++ b/packages/nextjs/src/client/index.ts
@@ -148,5 +148,3 @@ export {
withSentryGetStaticProps,
wrapGetStaticPropsWithSentry,
} from './wrapGetStaticPropsWithSentry';
-
-export { wrapAppDirComponentWithSentry } from './wrapAppDirComponentWithSentry';
diff --git a/packages/nextjs/src/client/wrapAppDirComponentWithSentry.ts b/packages/nextjs/src/client/wrapAppDirComponentWithSentry.ts
deleted file mode 100644
index 767f2beb5be7..000000000000
--- a/packages/nextjs/src/client/wrapAppDirComponentWithSentry.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- * Currently just a pass-through to provide isomorphism for the client. May be used in the future to add instrumentation
- * for client components.
- */
-export function wrapAppDirComponentWithSentry(wrappingTarget: any): any {
- return wrappingTarget;
-}
diff --git a/packages/nextjs/src/common/types.ts b/packages/nextjs/src/common/types.ts
new file mode 100644
index 000000000000..d21f3fa92880
--- /dev/null
+++ b/packages/nextjs/src/common/types.ts
@@ -0,0 +1,4 @@
+export type ServerComponentContext = {
+ componentRoute: string;
+ componentType: string;
+};
diff --git a/packages/nextjs/src/config/loaders/wrappingLoader.ts b/packages/nextjs/src/config/loaders/wrappingLoader.ts
index eba33dfb7ac3..879855cafa08 100644
--- a/packages/nextjs/src/config/loaders/wrappingLoader.ts
+++ b/packages/nextjs/src/config/loaders/wrappingLoader.ts
@@ -34,7 +34,7 @@ type LoaderOptions = {
appDir: string;
pageExtensionRegex: string;
excludeServerRoutes: Array;
- wrappingTargetKind: 'page' | 'api-route' | 'middleware' | 'page-server-component';
+ wrappingTargetKind: 'page' | 'api-route' | 'middleware' | 'server-component';
};
/**
@@ -95,14 +95,14 @@ export default function wrappingLoader(
// Inject the route and the path to the file we're wrapping into the template
templateCode = templateCode.replace(/__ROUTE__/g, parameterizedPagesRoute.replace(/\\/g, '\\\\'));
- } else if (wrappingTargetKind === 'page-server-component') {
+ } else if (wrappingTargetKind === 'server-component') {
// Get the parameterized route name from this page's filepath
const parameterizedPagesRoute = path.posix
.normalize(path.relative(appDir, this.resourcePath))
// Add a slash at the beginning
.replace(/(.*)/, '/$1')
// Pull off the file name
- .replace(/\/page\.(js|jsx|tsx)$/, '')
+ .replace(/\/[^/]+\.(js|jsx|tsx)$/, '')
// Remove routing groups: https://beta.nextjs.org/docs/routing/defining-routes#example-creating-multiple-root-layouts
.replace(/\/(\(.*?\)\/)+/g, '/')
// In case all of the above have left us with an empty string (which will happen if we're dealing with the
@@ -125,6 +125,36 @@ export default function wrappingLoader(
}
templateCode = serverComponentWrapperTemplateCode;
+
+ templateCode = templateCode.replace(/__ROUTE__/g, parameterizedPagesRoute.replace(/\\/g, '\\\\'));
+
+ const componentTypeMatch = path.posix
+ .normalize(path.relative(appDir, this.resourcePath))
+ .match(/\/?([^/]+)\.(?:js|jsx|tsx)$/);
+
+ if (componentTypeMatch && componentTypeMatch[1]) {
+ let componentType;
+ switch (componentTypeMatch[1]) {
+ case 'page':
+ componentType = 'Page';
+ break;
+ case 'layout':
+ componentType = 'Layout';
+ break;
+ case 'head':
+ componentType = 'Head';
+ break;
+ case 'not-found':
+ componentType = 'Not-found';
+ break;
+ default:
+ componentType = 'Unknown';
+ }
+
+ templateCode = templateCode.replace(/__COMPONENT_TYPE__/g, componentType);
+ } else {
+ templateCode = templateCode.replace(/__COMPONENT_TYPE__/g, 'Unknown');
+ }
} else if (wrappingTargetKind === 'middleware') {
templateCode = middlewareWrapperTemplateCode;
} else {
diff --git a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts
index 4ba89238aad8..74e8e1a5b1c3 100644
--- a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts
+++ b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts
@@ -22,7 +22,10 @@ const serverComponent = serverComponentModule.default;
let wrappedServerComponent;
if (typeof serverComponent === 'function') {
- wrappedServerComponent = Sentry.wrapAppDirComponentWithSentry(serverComponent);
+ wrappedServerComponent = Sentry.wrapServerComponentWithSentry(serverComponent, {
+ componentRoute: '__ROUTE__',
+ componentType: '__COMPONENT_TYPE__',
+ });
} else {
wrappedServerComponent = serverComponent;
}
diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts
index 0849ff761ea4..7fd142fc8aa8 100644
--- a/packages/nextjs/src/config/webpack.ts
+++ b/packages/nextjs/src/config/webpack.ts
@@ -87,12 +87,18 @@ export function constructWebpackConfigFunction(
});
let pagesDirPath: string;
- let appDirPath: string;
- if (fs.existsSync(path.join(projectDir, 'pages')) && fs.lstatSync(path.join(projectDir, 'pages')).isDirectory()) {
+ const maybePagesDirPath = path.join(projectDir, 'pages');
+ if (fs.existsSync(maybePagesDirPath) && fs.lstatSync(maybePagesDirPath).isDirectory()) {
pagesDirPath = path.join(projectDir, 'pages');
- appDirPath = path.join(projectDir, 'app');
} else {
pagesDirPath = path.join(projectDir, 'src', 'pages');
+ }
+
+ let appDirPath: string;
+ const maybeAppDirPath = path.join(projectDir, 'app');
+ if (fs.existsSync(maybeAppDirPath) && fs.lstatSync(maybeAppDirPath).isDirectory()) {
+ appDirPath = path.join(projectDir, 'app');
+ } else {
appDirPath = path.join(projectDir, 'src', 'app');
}
@@ -199,7 +205,7 @@ export function constructWebpackConfigFunction(
// https://beta.nextjs.org/docs/routing/pages-and-layouts#pages:~:text=.js%2C%20.jsx%2C%20or%20.tsx%20file%20extensions%20can%20be%20used%20for%20Pages.
return (
normalizedAbsoluteResourcePath.startsWith(appDirPath) &&
- !!normalizedAbsoluteResourcePath.match(/[\\/]page\.(js|jsx|tsx)$/)
+ !!normalizedAbsoluteResourcePath.match(/[\\/](page|layout|loading|head|not-found)\.(js|jsx|tsx)$/)
);
},
use: [
@@ -207,7 +213,7 @@ export function constructWebpackConfigFunction(
loader: path.resolve(__dirname, 'loaders', 'wrappingLoader.js'),
options: {
...staticWrappingLoaderOptions,
- wrappingTargetKind: 'page-server-component',
+ wrappingTargetKind: 'server-component',
},
},
],
diff --git a/packages/nextjs/src/edge/index.ts b/packages/nextjs/src/edge/index.ts
index 087e0a1482d5..6f8cd2f42cc4 100644
--- a/packages/nextjs/src/edge/index.ts
+++ b/packages/nextjs/src/edge/index.ts
@@ -140,4 +140,4 @@ export {
export { wrapMiddlewareWithSentry } from './wrapMiddlewareWithSentry';
-export { wrapAppDirComponentWithSentry } from './wrapAppDirComponentWithSentry';
+export { wrapServerComponentWithSentry } from './wrapServerComponentWithSentry';
diff --git a/packages/nextjs/src/edge/wrapAppDirComponentWithSentry.ts b/packages/nextjs/src/edge/wrapServerComponentWithSentry.ts
similarity index 82%
rename from packages/nextjs/src/edge/wrapAppDirComponentWithSentry.ts
rename to packages/nextjs/src/edge/wrapServerComponentWithSentry.ts
index 116df75a4caa..349207e7b039 100644
--- a/packages/nextjs/src/edge/wrapAppDirComponentWithSentry.ts
+++ b/packages/nextjs/src/edge/wrapServerComponentWithSentry.ts
@@ -1,9 +1,14 @@
import { captureException } from '@sentry/core';
+import type { ServerComponentContext } from '../common/types';
+
/**
* Wraps an `app` directory server component with Sentry error instrumentation.
*/
-export function wrapAppDirComponentWithSentry any>(appDirComponent: F): F {
+export function wrapServerComponentWithSentry any>(
+ appDirComponent: F,
+ _context: ServerComponentContext,
+): F {
// Even though users may define server components as async functions, for the client bundles
// Next.js will turn them into synchronous functions and it will transform any`await`s into instances of the`use`
// hook. 🤯
diff --git a/packages/nextjs/src/index.types.ts b/packages/nextjs/src/index.types.ts
index eb5fb9ca258f..47c24e44751f 100644
--- a/packages/nextjs/src/index.types.ts
+++ b/packages/nextjs/src/index.types.ts
@@ -10,6 +10,7 @@ export * from './edge';
import type { Integration, Options, StackParser } from '@sentry/types';
import type * as clientSdk from './client';
+import type { ServerComponentContext } from './common/types';
import type * as edgeSdk from './edge';
import type * as serverSdk from './server';
@@ -168,6 +169,9 @@ export declare function withSentryGetStaticProps a
): (...args: Parameters) => ReturnType extends Promise ? ReturnType : Promise>;
/**
- * Wraps an `app` directory component with Sentry error instrumentation. (Currently only reports errors for server components)
+ * Wraps an `app` directory server component with Sentry error and performance instrumentation.
*/
-export declare function wrapAppDirComponentWithSentry any>(WrappingTarget: F): F;
+export declare function wrapServerComponentWithSentry any>(
+ WrappingTarget: F,
+ context: ServerComponentContext,
+): F;
diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts
index 348eff6778d9..8c16195aa570 100644
--- a/packages/nextjs/src/server/index.ts
+++ b/packages/nextjs/src/server/index.ts
@@ -223,4 +223,4 @@ export {
wrapApiHandlerWithSentry,
} from './wrapApiHandlerWithSentry';
-export { wrapAppDirComponentWithSentry } from './wrapAppDirComponentWithSentry';
+export { wrapServerComponentWithSentry } from './wrapServerComponentWithSentry';
diff --git a/packages/nextjs/src/server/wrapAppDirComponentWithSentry.ts b/packages/nextjs/src/server/wrapAppDirComponentWithSentry.ts
deleted file mode 100644
index 116df75a4caa..000000000000
--- a/packages/nextjs/src/server/wrapAppDirComponentWithSentry.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { captureException } from '@sentry/core';
-
-/**
- * Wraps an `app` directory server component with Sentry error instrumentation.
- */
-export function wrapAppDirComponentWithSentry any>(appDirComponent: F): F {
- // Even though users may define server components as async functions, for the client bundles
- // Next.js will turn them into synchronous functions and it will transform any`await`s into instances of the`use`
- // hook. 🤯
- return new Proxy(appDirComponent, {
- apply: (originalFunction, thisArg, args) => {
- let maybePromiseResult;
-
- try {
- maybePromiseResult = originalFunction.apply(thisArg, args);
- } catch (e) {
- captureException(e);
- throw e;
- }
-
- if (typeof maybePromiseResult === 'object' && maybePromiseResult !== null && 'then' in maybePromiseResult) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- return maybePromiseResult.then(null, (e: Error) => {
- captureException(e);
- throw e;
- });
- } else {
- return maybePromiseResult;
- }
- },
- });
-}
diff --git a/packages/nextjs/src/server/wrapServerComponentWithSentry.ts b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts
new file mode 100644
index 000000000000..05c17c6bede3
--- /dev/null
+++ b/packages/nextjs/src/server/wrapServerComponentWithSentry.ts
@@ -0,0 +1,67 @@
+import { captureException, getCurrentHub, startTransaction } from '@sentry/core';
+import * as domain from 'domain';
+
+import type { ServerComponentContext } from '../common/types';
+
+/**
+ * Wraps an `app` directory server component with Sentry error instrumentation.
+ */
+export function wrapServerComponentWithSentry any>(
+ appDirComponent: F,
+ context: ServerComponentContext,
+): F {
+ const { componentRoute, componentType } = context;
+
+ // Even though users may define server components as async functions, for the client bundles
+ // Next.js will turn them into synchronous functions and it will transform any `await`s into instances of the `use`
+ // hook. 🤯
+ return new Proxy(appDirComponent, {
+ apply: (originalFunction, thisArg, args) => {
+ return domain.create().bind(() => {
+ let maybePromiseResult;
+
+ const transaction = startTransaction({
+ op: 'function.nextjs',
+ name: `${componentType} Server Component (${componentRoute})`,
+ status: 'ok',
+ metadata: {
+ source: 'component',
+ },
+ });
+
+ const currentScope = getCurrentHub().getScope();
+ if (currentScope) {
+ currentScope.setSpan(transaction);
+ }
+
+ try {
+ maybePromiseResult = originalFunction.apply(thisArg, args);
+ } catch (e) {
+ transaction.setStatus('internal_error');
+ captureException(e);
+ transaction.finish();
+ throw e;
+ }
+
+ if (typeof maybePromiseResult === 'object' && maybePromiseResult !== null && 'then' in maybePromiseResult) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ return maybePromiseResult.then(
+ (res: unknown) => {
+ transaction.finish();
+ return res;
+ },
+ (e: Error) => {
+ transaction.setStatus('internal_error');
+ captureException(e);
+ transaction.finish();
+ throw e;
+ },
+ );
+ } else {
+ transaction.finish();
+ return maybePromiseResult;
+ }
+ })();
+ },
+ });
+}
diff --git a/packages/nextjs/test/config/loaders.test.ts b/packages/nextjs/test/config/loaders.test.ts
index 84fb39ea80da..94a6a5ec0654 100644
--- a/packages/nextjs/test/config/loaders.test.ts
+++ b/packages/nextjs/test/config/loaders.test.ts
@@ -157,11 +157,11 @@ describe('webpack loaders', () => {
},
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/page.js',
- expectedWrappingTargetKind: 'page-server-component',
+ expectedWrappingTargetKind: 'server-component',
},
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/page.js',
- expectedWrappingTargetKind: 'page-server-component',
+ expectedWrappingTargetKind: 'server-component',
},
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/page.ts', // ts is not a valid file ending for pages in the app dir
@@ -169,7 +169,7 @@ describe('webpack loaders', () => {
},
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/(group)/nested/page.tsx',
- expectedWrappingTargetKind: 'page-server-component',
+ expectedWrappingTargetKind: 'server-component',
},
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/(group)/nested/loading.ts',
@@ -177,7 +177,7 @@ describe('webpack loaders', () => {
},
{
resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/layout.js',
- expectedWrappingTargetKind: undefined,
+ expectedWrappingTargetKind: 'server-component',
},
])(
'should apply the right wrappingTargetKind with wrapping loader ($resourcePath)',