diff --git a/.prettierignore b/.prettierignore index cd4dd6cc00..29baea3bab 100644 --- a/.prettierignore +++ b/.prettierignore @@ -9,3 +9,5 @@ yarn.lock /docs /developer-extension/.output /developer-extension/.wxt +/test/apps/nextjs-app-router/.next +/test/apps/nextjs-app-router/next-env.d.ts diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index a3ac2d3aa6..cc4f97c3a9 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -57,6 +57,7 @@ dev,karma-spec-reporter,MIT,Copyright 2015 Michael Lex dev,karma-webpack,MIT,Copyright JS Foundation and other contributors dev,lerna,MIT,Copyright 2015-present Lerna Contributors dev,minimatch,ISC,Copyright (c) Isaac Z. Schlueter and Contributors +dev,next,MIT,Copyright (c) 2025 Vercel, Inc. dev,node-forge,BSD,Copyright (c) 2010, Digital Bazaar, Inc. dev,pako,MIT,(C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin dev,prettier,MIT,Copyright James Long and contributors diff --git a/eslint.config.mjs b/eslint.config.mjs index fe6928b3dd..3cc2c7d64c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -35,6 +35,7 @@ export default tseslint.config( 'docs', 'developer-extension/.wxt', 'developer-extension/dist', + 'test/apps/nextjs-app-router', ], }, diff --git a/packages/core/src/tools/instrumentMethod.spec.ts b/packages/core/src/tools/instrumentMethod.spec.ts index 097f665bc4..37dc605b3d 100644 --- a/packages/core/src/tools/instrumentMethod.spec.ts +++ b/packages/core/src/tools/instrumentMethod.spec.ts @@ -266,7 +266,7 @@ describe('instrumentSetter', () => { it('does not use the Zone.js setTimeout function', () => { const zoneJsSetTimeoutSpy = jasmine.createSpy() - zoneJs.replaceProperty(window, 'setTimeout', zoneJsSetTimeoutSpy) + zoneJs.replaceProperty(window, 'setTimeout', zoneJsSetTimeoutSpy as unknown as typeof setTimeout) const object = {} as { foo: number } Object.defineProperty(object, 'foo', { set: noop, configurable: true }) diff --git a/packages/rum-core/src/domain/tracing/identifier.spec.ts b/packages/rum-core/src/domain/tracing/identifier.spec.ts index 6835e2cbc3..7194422728 100644 --- a/packages/rum-core/src/domain/tracing/identifier.spec.ts +++ b/packages/rum-core/src/domain/tracing/identifier.spec.ts @@ -38,7 +38,7 @@ describe('toPaddedHexadecimalString', () => { }) function mockRandomValues(cb: (buffer: Uint8Array) => void) { - spyOn(window.crypto, 'getRandomValues').and.callFake((bufferView) => { + spyOn(window.crypto, 'getRandomValues').and.callFake((bufferView: ArrayBufferView) => { cb(new Uint8Array(bufferView.buffer)) return bufferView }) diff --git a/packages/rum-react/.npmignore b/packages/rum-react/.npmignore index 4049b9b2d8..c239794535 100644 --- a/packages/rum-react/.npmignore +++ b/packages/rum-react/.npmignore @@ -5,3 +5,4 @@ /src/**/*.spec.ts /src/**/*.specHelper.ts !/react-router-v[6-7]/* +!/nextjs/* diff --git a/packages/rum-react/nextjs/package.json b/packages/rum-react/nextjs/package.json new file mode 100644 index 0000000000..38b6e4229f --- /dev/null +++ b/packages/rum-react/nextjs/package.json @@ -0,0 +1,7 @@ +{ + "name": "@datadog/browser-rum-react/nextjs", + "private": true, + "main": "../cjs/entries/nextjs.js", + "module": "../esm/entries/nextjs.js", + "types": "../cjs/entries/nextjs.d.ts" +} diff --git a/packages/rum-react/package.json b/packages/rum-react/package.json index 5aaeaf9776..5163558ea9 100644 --- a/packages/rum-react/package.json +++ b/packages/rum-react/package.json @@ -14,6 +14,7 @@ "@datadog/browser-rum-core": "6.27.1" }, "peerDependencies": { + "next": ">=13", "react": "18 || 19", "react-router": "6 || 7", "react-router-dom": "6 || 7" @@ -25,6 +26,9 @@ "@datadog/browser-rum-slim": { "optional": true }, + "next": { + "optional": true + }, "react": { "optional": true }, @@ -38,6 +42,7 @@ "devDependencies": { "@types/react": "19.2.11", "@types/react-dom": "19.2.3", + "next": "16.1.6", "react": "19.2.4", "react-dom": "19.2.4", "react-router": "7.13.0", diff --git a/packages/rum-react/src/domain/nextjs/datadogRumProvider.spec.tsx b/packages/rum-react/src/domain/nextjs/datadogRumProvider.spec.tsx new file mode 100644 index 0000000000..aed01954d5 --- /dev/null +++ b/packages/rum-react/src/domain/nextjs/datadogRumProvider.spec.tsx @@ -0,0 +1,83 @@ +import React from 'react' +import { replaceMockable } from '@datadog/browser-core/test' +import { usePathname, useParams } from 'next/navigation' +import { appendComponent } from '../../../test/appendComponent' +import { initializeReactPlugin } from '../../../test/initializeReactPlugin' +import { initReactOldBrowsersSupport } from '../../../test/reactOldBrowsersSupport' +import { DatadogRumProvider } from './datadogRumProvider' + +describe('DatadogRumProvider', () => { + let startViewSpy: jasmine.Spy<(name?: string | object) => void> + + beforeEach(() => { + initReactOldBrowsersSupport() + startViewSpy = jasmine.createSpy() + initializeReactPlugin({ + configuration: { + nextjs: true, + }, + publicApi: { + startView: startViewSpy, + }, + }) + }) + + it('renders children correctly', () => { + replaceMockable(usePathname, () => '/') + replaceMockable(useParams, () => ({})) + + const container = appendComponent( + +
Test Content
+
+ ) + + const child = container.querySelector('[data-testid="test-child"]') + expect(child).not.toBeNull() + expect(child!.textContent).toBe('Test Content') + expect(child!.parentElement).toBe(container) + }) + + it('starts initial view on mount', () => { + replaceMockable(usePathname, () => '/home') + replaceMockable(useParams, () => ({})) + + appendComponent( + +
Content
+
+ ) + + expect(startViewSpy).toHaveBeenCalledWith('/home') + }) + + it('starts initial view with normalized name using params', () => { + replaceMockable(usePathname, () => '/user/42') + replaceMockable(useParams, () => ({ id: '42' })) + + appendComponent( + +
Content
+
+ ) + + expect(startViewSpy).toHaveBeenCalledWith('/user/:id') + }) + + it('renders multiple children', () => { + replaceMockable(usePathname, () => '/') + replaceMockable(useParams, () => ({})) + + const container = appendComponent( + +
Child 1
+
Child 2
+
Child 3
+
+ ) + + expect(container.querySelector('[data-testid="child-1"]')!.textContent).toBe('Child 1') + expect(container.querySelector('[data-testid="child-2"]')!.textContent).toBe('Child 2') + expect(container.querySelector('[data-testid="child-3"]')!.textContent).toBe('Child 3') + }) +}) diff --git a/packages/rum-react/src/domain/nextjs/datadogRumProvider.tsx b/packages/rum-react/src/domain/nextjs/datadogRumProvider.tsx new file mode 100644 index 0000000000..4f6eda9880 --- /dev/null +++ b/packages/rum-react/src/domain/nextjs/datadogRumProvider.tsx @@ -0,0 +1,31 @@ +'use client' + +import React, { type ReactNode, useEffect, useRef } from 'react' +import { usePathname, useParams } from 'next/navigation' +import { mockable } from '@datadog/browser-core' +import { computeViewName, startNextjsView } from './viewTracking' + +export interface DatadogRumProviderProps { + /** + * The children components to render. + */ + children: ReactNode +} + +export function DatadogRumProvider({ children }: DatadogRumProviderProps) { + const pathname = mockable(usePathname)() + const params = mockable(useParams)() ?? {} + const previousPathnameRef = useRef(undefined) + + useEffect(() => { + if (previousPathnameRef.current === pathname) { + return + } + previousPathnameRef.current = pathname + + const viewName = computeViewName(pathname, params) + startNextjsView(viewName) + }, [pathname, params]) + + return <>{children} +} diff --git a/packages/rum-react/src/domain/nextjs/index.ts b/packages/rum-react/src/domain/nextjs/index.ts new file mode 100644 index 0000000000..39e3e4e34a --- /dev/null +++ b/packages/rum-react/src/domain/nextjs/index.ts @@ -0,0 +1,3 @@ +export { DatadogRumProvider } from './datadogRumProvider' +export type { DatadogRumProviderProps } from './datadogRumProvider' +export { computeViewName } from './viewTracking' diff --git a/packages/rum-react/src/domain/nextjs/viewTracking.spec.tsx b/packages/rum-react/src/domain/nextjs/viewTracking.spec.tsx new file mode 100644 index 0000000000..53c6d38f53 --- /dev/null +++ b/packages/rum-react/src/domain/nextjs/viewTracking.spec.tsx @@ -0,0 +1,98 @@ +import { display } from '@datadog/browser-core' +import { initializeReactPlugin } from '../../../test/initializeReactPlugin' +import { computeViewName, startNextjsView } from './viewTracking' + +describe('computeViewName', () => { + ;[ + { pathname: '/', params: {}, expected: '/' }, + { pathname: '/about', params: {}, expected: '/about' }, + { pathname: '/user/42', params: { id: '42' }, expected: '/user/:id' }, + { + pathname: '/orders/456/items/789', + params: { orderId: '456', itemId: '789' }, + expected: '/orders/:orderId/items/:itemId', + }, + { + pathname: '/user/abc12345-1234-1234-1234-123456789012', + params: { id: 'abc12345-1234-1234-1234-123456789012' }, + expected: '/user/:id', + }, + { + pathname: '/user/abc12345-1234-1234-1234-123456789012/profile', + params: { id: 'abc12345-1234-1234-1234-123456789012' }, + expected: '/user/:id/profile', + }, + { + pathname: '/docs/a/b/c', + params: { slug: ['a', 'b', 'c'] }, + expected: '/docs/:slug', + }, + { + pathname: '/blog/my-awesome-post', + params: { slug: 'my-awesome-post' }, + expected: '/blog/:slug', + }, + ].forEach(({ pathname, params, expected }) => { + it(`computes ${pathname} with params ${JSON.stringify(params)} to ${expected}`, () => { + expect(computeViewName(pathname, params)).toBe(expected) + }) + }) +}) + +describe('startNextjsView', () => { + let startViewSpy: jasmine.Spy<(name?: string | object) => void> + + beforeEach(() => { + startViewSpy = jasmine.createSpy() + initializeReactPlugin({ + configuration: { + nextjs: true, + }, + publicApi: { + startView: startViewSpy, + }, + }) + }) + + it('creates a new view with the given view name', () => { + startNextjsView('/user/:id') + + expect(startViewSpy).toHaveBeenCalledOnceWith('/user/:id') + }) + + it('warns when nextjs configuration is missing', () => { + const localStartViewSpy = jasmine.createSpy() + const warnSpy = spyOn(display, 'warn') + initializeReactPlugin({ + configuration: {}, + publicApi: { + startView: localStartViewSpy, + }, + }) + + startNextjsView('/product/:id') + + expect(warnSpy).toHaveBeenCalledOnceWith( + '`nextjs: true` is missing from the react plugin configuration, the view will not be tracked.' + ) + expect(localStartViewSpy).not.toHaveBeenCalled() + }) + + it('does not create a view when nextjs flag is false', () => { + const localStartViewSpy = jasmine.createSpy() + const warnSpy = spyOn(display, 'warn') + initializeReactPlugin({ + configuration: { + nextjs: false, + }, + publicApi: { + startView: localStartViewSpy, + }, + }) + + startNextjsView('/product/:id') + + expect(warnSpy).toHaveBeenCalled() + expect(localStartViewSpy).not.toHaveBeenCalled() + }) +}) diff --git a/packages/rum-react/src/domain/nextjs/viewTracking.tsx b/packages/rum-react/src/domain/nextjs/viewTracking.tsx new file mode 100644 index 0000000000..f2a94276da --- /dev/null +++ b/packages/rum-react/src/domain/nextjs/viewTracking.tsx @@ -0,0 +1,41 @@ +import { display } from '@datadog/browser-core' +import { onRumInit } from '../reactPlugin' + +/** + * Computes a view name by replacing dynamic parameter values in the pathname + * with their corresponding parameter names from Next.js's useParams(). + * + * @example + * computeViewName('/user/42', { id: '42' }) // => '/user/:id' + * computeViewName('/docs/a/b/c', { slug: ['a', 'b', 'c'] }) // => '/docs/:slug' + */ +export function computeViewName(pathname: string, params: Record): string { + let viewName = pathname + for (const [key, value] of Object.entries(params)) { + if (value === undefined) { + continue + } + if (Array.isArray(value)) { + viewName = viewName.replace(value.join('/'), `:${key}`) + } else { + viewName = viewName.replace(value, `:${key}`) + } + } + return viewName +} + +/** + * Starts a new RUM view with the given view name. + * + * @internal + */ +export function startNextjsView(viewName: string) { + onRumInit((configuration, rumPublicApi) => { + if (!configuration.nextjs) { + display.warn('`nextjs: true` is missing from the react plugin configuration, the view will not be tracked.') + return + } + + rumPublicApi.startView(viewName) + }) +} diff --git a/packages/rum-react/src/domain/reactPlugin.spec.ts b/packages/rum-react/src/domain/reactPlugin.spec.ts index 2540c79186..6f0fdd573e 100644 --- a/packages/rum-react/src/domain/reactPlugin.spec.ts +++ b/packages/rum-react/src/domain/reactPlugin.spec.ts @@ -69,6 +69,13 @@ describe('reactPlugin', () => { const pluginConfiguration = { router: true } const plugin = reactPlugin(pluginConfiguration) - expect(plugin.getConfigurationTelemetry()).toEqual({ router: true }) + expect(plugin.getConfigurationTelemetry()).toEqual({ router: true, nextjs: false }) + }) + + it('returns the configuration telemetry when nextjs is true', () => { + const pluginConfiguration = { nextjs: true } + const plugin = reactPlugin(pluginConfiguration) + + expect(plugin.getConfigurationTelemetry()).toEqual({ router: false, nextjs: true }) }) }) diff --git a/packages/rum-react/src/domain/reactPlugin.ts b/packages/rum-react/src/domain/reactPlugin.ts index 946e733ff7..479a896f91 100644 --- a/packages/rum-react/src/domain/reactPlugin.ts +++ b/packages/rum-react/src/domain/reactPlugin.ts @@ -23,6 +23,13 @@ export interface ReactPluginConfiguration { * ``` */ router?: boolean + + /** + * Enable Next.js App Router integration. Make sure to use the DatadogRumProvider from + * {@link @datadog/browser-rum-react/nextjs! | @datadog/browser-rum-react/nextjs} + * to enable automatic view tracking. + */ + nextjs?: boolean } /** @@ -61,7 +68,7 @@ export function reactPlugin(configuration: ReactPluginConfiguration = {}): React for (const subscriber of onRumInitSubscribers) { subscriber(globalConfiguration, globalPublicApi) } - if (configuration.router) { + if (configuration.router || configuration.nextjs) { initConfiguration.trackViewsManually = true } }, @@ -74,7 +81,7 @@ export function reactPlugin(configuration: ReactPluginConfiguration = {}): React } }, getConfigurationTelemetry() { - return { router: !!configuration.router } + return { router: !!configuration.router, nextjs: !!configuration.nextjs } }, } satisfies RumPlugin } diff --git a/packages/rum-react/src/entries/nextjs.ts b/packages/rum-react/src/entries/nextjs.ts new file mode 100644 index 0000000000..d4b2d9a3a2 --- /dev/null +++ b/packages/rum-react/src/entries/nextjs.ts @@ -0,0 +1,49 @@ +/** + * Next.js App Router integration. + * + * @packageDocumentation + * @example + * ```tsx + * // app/providers.tsx (client component) + * 'use client' + * import type { ReactNode } from 'react' + * import { datadogRum } from '@datadog/browser-rum' + * import { reactPlugin, DatadogRumProvider } from '@datadog/browser-rum-react/nextjs' + * + * datadogRum.init({ + * applicationId: '', + * clientToken: '', + * site: '', + * plugins: [reactPlugin({ nextjs: true })], + * }) + * + * export function RumProvider({ children }: { children: ReactNode }) { + * return {children} + * } + * ``` + * @example + * ```tsx + * // app/layout.tsx (server component) + * import { RumProvider } from './providers' + * + * export default function RootLayout({ children }) { + * return ( + * + * + * {children} + * + * + * ) + * } + * ``` + */ + +export { DatadogRumProvider } from '../domain/nextjs' +export type { DatadogRumProviderProps } from '../domain/nextjs' +// Re-export shared functionality from main package +export { ErrorBoundary, addReactError } from '../domain/error' +export type { ErrorBoundaryProps, ErrorBoundaryFallback } from '../domain/error' +export type { ReactPluginConfiguration, ReactPlugin } from '../domain/reactPlugin' +export { reactPlugin } from '../domain/reactPlugin' +// eslint-disable-next-line camelcase +export { UNSTABLE_ReactComponentTracker } from '../domain/performance' diff --git a/scripts/build/build-test-apps.ts b/scripts/build/build-test-apps.ts index 1af38f9102..80abd3e0ef 100644 --- a/scripts/build/build-test-apps.ts +++ b/scripts/build/build-test-apps.ts @@ -18,6 +18,7 @@ runMain(async () => { buildApp('test/apps/react-router-v6-app') buildApp('test/apps/react-heavy-spa') buildApp('test/apps/react-shopist-like') + buildApp('test/apps/nextjs-app-router') await buildReactRouterv7App() await buildExtensions() diff --git a/test/apps/nextjs-app-router/.gitignore b/test/apps/nextjs-app-router/.gitignore new file mode 100644 index 0000000000..20e9477154 --- /dev/null +++ b/test/apps/nextjs-app-router/.gitignore @@ -0,0 +1,3 @@ +.next +node_modules +.yarn/* \ No newline at end of file diff --git a/test/apps/nextjs-app-router/app/error-test/page.tsx b/test/apps/nextjs-app-router/app/error-test/page.tsx new file mode 100644 index 0000000000..d4e877bf09 --- /dev/null +++ b/test/apps/nextjs-app-router/app/error-test/page.tsx @@ -0,0 +1,61 @@ +'use client' + +import { useState } from 'react' +import Link from 'next/link' +import { ErrorBoundary } from '@datadog/browser-rum-react' + +function ComponentWithErrorButton() { + const [shouldError, setShouldError] = useState(false) + + if (shouldError) { + throw new Error('Error triggered by button click') + } + + return ( +
+

Click button to trigger error

+ +
+ ) +} + +export default function ErrorTestPage() { + const throwAsyncError = async () => { + await new Promise((resolve) => setTimeout(resolve, 100)) + throw new Error('Test asynchronous error') + } + + const throwUnhandledRejection = () => { + Promise.reject(new Error('Test unhandled promise rejection')) + } + + return ( +
+ ← Back to Home +

Error Testing

+ +
+

Error Boundary (Datadog RUM)

+ ( +
+

Something went wrong

+

{error.message}

+ +
+ )} + > + +
+
+ +
+

Other Error Types

+ + +
+
+ ) +} diff --git a/test/apps/nextjs-app-router/app/layout.tsx b/test/apps/nextjs-app-router/app/layout.tsx new file mode 100644 index 0000000000..e4a1d2e274 --- /dev/null +++ b/test/apps/nextjs-app-router/app/layout.tsx @@ -0,0 +1,24 @@ +import type { Metadata } from 'next' +import { RumProvider } from './providers' + +export const metadata: Metadata = { + title: 'Next.js App Router Test', + description: 'Test app for Datadog RUM Next.js integration', +} + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + + +
{children}
+
+ + + ) +} diff --git a/test/apps/nextjs-app-router/app/page.tsx b/test/apps/nextjs-app-router/app/page.tsx new file mode 100644 index 0000000000..22699faf36 --- /dev/null +++ b/test/apps/nextjs-app-router/app/page.tsx @@ -0,0 +1,23 @@ +import Link from 'next/link' + +export default function HomePage() { + return ( +
+

Home

+
    +
  • + Go to User 42 +
  • +
  • + Go to User 123 +
  • +
  • + Go to Tracked Component +
  • +
  • + Go to Error Test +
  • +
+
+ ) +} diff --git a/test/apps/nextjs-app-router/app/providers.tsx b/test/apps/nextjs-app-router/app/providers.tsx new file mode 100644 index 0000000000..7c9b9db9ff --- /dev/null +++ b/test/apps/nextjs-app-router/app/providers.tsx @@ -0,0 +1,20 @@ +'use client' + +import type { ReactNode } from 'react' +import { datadogRum } from '@datadog/browser-rum' +import { reactPlugin, DatadogRumProvider } from '@datadog/browser-rum-react/nextjs' + +// In E2E tests, RUM_CONFIGURATION is injected via Playwright's addInitScript +if (typeof window !== 'undefined') { + const config = (window as any).RUM_CONFIGURATION + if (config) { + datadogRum.init({ + ...config, + plugins: [reactPlugin({ nextjs: true }), ...(config.plugins || [])], + }) + } +} + +export function RumProvider({ children }: { children: ReactNode }) { + return {children} +} diff --git a/test/apps/nextjs-app-router/app/tracked/page.tsx b/test/apps/nextjs-app-router/app/tracked/page.tsx new file mode 100644 index 0000000000..12401a8b48 --- /dev/null +++ b/test/apps/nextjs-app-router/app/tracked/page.tsx @@ -0,0 +1,16 @@ +'use client' + +import Link from 'next/link' +import { UNSTABLE_ReactComponentTracker as ReactComponentTracker } from '@datadog/browser-rum-react' + +export default function TrackedPage() { + return ( +
+ ← Back to Home + +

Component Tracker

+

This component is tracked for performance metrics.

+
+
+ ) +} diff --git a/test/apps/nextjs-app-router/app/user/[id]/page.tsx b/test/apps/nextjs-app-router/app/user/[id]/page.tsx new file mode 100644 index 0000000000..5a496c9ba4 --- /dev/null +++ b/test/apps/nextjs-app-router/app/user/[id]/page.tsx @@ -0,0 +1,13 @@ +import Link from 'next/link' + +export default async function UserPage({ params }: { params: Promise<{ id: string }> }) { + const { id } = await params + + return ( +
+ ← Back to Home +

User {id}

+

This is a dynamic route testing view name normalization.

+
+ ) +} diff --git a/test/apps/nextjs-app-router/next-env.d.ts b/test/apps/nextjs-app-router/next-env.d.ts new file mode 100644 index 0000000000..9edff1c7ca --- /dev/null +++ b/test/apps/nextjs-app-router/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/test/apps/nextjs-app-router/next.config.js b/test/apps/nextjs-app-router/next.config.js new file mode 100644 index 0000000000..c77754a91e --- /dev/null +++ b/test/apps/nextjs-app-router/next.config.js @@ -0,0 +1,8 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + turbopack: { + root: __dirname, + }, +} + +module.exports = nextConfig diff --git a/test/apps/nextjs-app-router/package.json b/test/apps/nextjs-app-router/package.json new file mode 100644 index 0000000000..4d34606854 --- /dev/null +++ b/test/apps/nextjs-app-router/package.json @@ -0,0 +1,33 @@ +{ + "name": "nextjs-app-router", + "private": true, + "scripts": { + "dev": "next dev --port 3000", + "build": "next build", + "start": "next start --port 3000" + }, + "dependencies": { + "@datadog/browser-rum": "file:../../../packages/rum/package.tgz", + "@datadog/browser-rum-react": "file:../../../packages/rum-react/package.tgz", + "next": "16.1.6", + "react": "19.2.3", + "react-dom": "19.2.3" + }, + "resolutions": { + "@datadog/browser-rum-core": "file:../../../packages/rum-core/package.tgz", + "@datadog/browser-core": "file:../../../packages/core/package.tgz", + "@datadog/browser-rum": "file:../../../packages/rum/package.tgz", + "@datadog/browser-rum-react": "file:../../../packages/rum-react/package.tgz", + "@datadog/browser-rum-slim": "file:../../../packages/rum-slim/package.tgz", + "@datadog/browser-worker": "file:../../../packages/worker/package.tgz" + }, + "devDependencies": { + "@types/node": "22.16.0", + "@types/react": "19.2.8", + "@types/react-dom": "19.2.3", + "typescript": "5.9.3" + }, + "volta": { + "extends": "../../../package.json" + } +} diff --git a/test/apps/nextjs-app-router/tsconfig.json b/test/apps/nextjs-app-router/tsconfig.json new file mode 100644 index 0000000000..f22e40d712 --- /dev/null +++ b/test/apps/nextjs-app-router/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + }, + "target": "ES2017" + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/test/apps/nextjs-app-router/yarn.lock b/test/apps/nextjs-app-router/yarn.lock new file mode 100644 index 0000000000..3aad4a10e2 --- /dev/null +++ b/test/apps/nextjs-app-router/yarn.lock @@ -0,0 +1,715 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@datadog/browser-core@file:../../../packages/core/package.tgz::locator=nextjs-app-router%40workspace%3A.": + version: 6.27.1 + resolution: "@datadog/browser-core@file:../../../packages/core/package.tgz#../../../packages/core/package.tgz::hash=236de9&locator=nextjs-app-router%40workspace%3A." + checksum: 10c0/7ebd0e1dff67c77cd0ee1667f3f80e35ef2eb33811d3f0ed83eb04a3b7f0621e29d101a9f3f38476b3304b61d8a0b4c4e70f4d0474cff8ef3c3c81f9c836a6f0 + languageName: node + linkType: hard + +"@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz::locator=nextjs-app-router%40workspace%3A.": + version: 6.27.1 + resolution: "@datadog/browser-rum-core@file:../../../packages/rum-core/package.tgz#../../../packages/rum-core/package.tgz::hash=0ea591&locator=nextjs-app-router%40workspace%3A." + dependencies: + "@datadog/browser-core": "npm:6.27.1" + checksum: 10c0/5d75b104db8b27e238134f397f6f167050e10f0a1f4580a00d1b25f8f0cebb6111c7df3df32e1e8761aa39c66c47eb43aa222f4ff0f8a29a7ec4b11859bd3557 + languageName: node + linkType: hard + +"@datadog/browser-rum-react@file:../../../packages/rum-react/package.tgz::locator=nextjs-app-router%40workspace%3A.": + version: 6.27.1 + resolution: "@datadog/browser-rum-react@file:../../../packages/rum-react/package.tgz#../../../packages/rum-react/package.tgz::hash=9fefa5&locator=nextjs-app-router%40workspace%3A." + dependencies: + "@datadog/browser-core": "npm:6.27.1" + "@datadog/browser-rum-core": "npm:6.27.1" + peerDependencies: + next: ">=13" + react: 18 || 19 + react-router: 6 || 7 + react-router-dom: 6 || 7 + peerDependenciesMeta: + "@datadog/browser-rum": + optional: true + "@datadog/browser-rum-slim": + optional: true + next: + optional: true + react: + optional: true + react-router: + optional: true + react-router-dom: + optional: true + checksum: 10c0/dc30c02d78ce3c02e90f175378e756df2d3fb6f8bc653ca5786718e73b66acd1a9c798acc045a664235d313198d43ee987115363e9dacd1e02762d685810698a + languageName: node + linkType: hard + +"@datadog/browser-rum@file:../../../packages/rum/package.tgz::locator=nextjs-app-router%40workspace%3A.": + version: 6.27.1 + resolution: "@datadog/browser-rum@file:../../../packages/rum/package.tgz#../../../packages/rum/package.tgz::hash=5b1f33&locator=nextjs-app-router%40workspace%3A." + dependencies: + "@datadog/browser-core": "npm:6.27.1" + "@datadog/browser-rum-core": "npm:6.27.1" + peerDependencies: + "@datadog/browser-logs": 6.27.1 + peerDependenciesMeta: + "@datadog/browser-logs": + optional: true + checksum: 10c0/083cdd3519ce1df8716a18067a564eb191adbb00ac2b3dec72d5ae9b34c0330922d2dcad2f696404a1c68ae3cf983a2da126e0ed4d7b63d008049b1b211693e8 + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.7.0": + version: 1.8.1 + resolution: "@emnapi/runtime@npm:1.8.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/f4929d75e37aafb24da77d2f58816761fe3f826aad2e37fa6d4421dac9060cbd5098eea1ac3c9ecc4526b89deb58153852fa432f87021dc57863f2ff726d713f + languageName: node + linkType: hard + +"@img/colour@npm:^1.0.0": + version: 1.0.0 + resolution: "@img/colour@npm:1.0.0" + checksum: 10c0/02261719c1e0d7aa5a2d585981954f2ac126f0c432400aa1a01b925aa2c41417b7695da8544ee04fd29eba7ecea8eaf9b8bef06f19dc8faba78f94eeac40667d + languageName: node + linkType: hard + +"@img/sharp-darwin-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-arm64": + optional: true + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-darwin-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-x64": + optional: true + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm@npm:1.2.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-ppc64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-riscv64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-riscv64@npm:1.2.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-s390x@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.4" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linux-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-arm@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm": + optional: true + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-ppc64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-ppc64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-ppc64": + optional: true + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-riscv64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-riscv64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-riscv64": + optional: true + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-s390x@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-s390x@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-s390x": + optional: true + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-wasm32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-wasm32@npm:0.34.5" + dependencies: + "@emnapi/runtime": "npm:^1.7.0" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@img/sharp-win32-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-arm64@npm:0.34.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-win32-ia32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-ia32@npm:0.34.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@img/sharp-win32-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-x64@npm:0.34.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@next/env@npm:16.1.6": + version: 16.1.6 + resolution: "@next/env@npm:16.1.6" + checksum: 10c0/ed7023edb94b9b2e5da3f9c99d08b614da9757c1edd0ecec792fce4d336b4f0c64db1a84955e07cfbd848b9e61c4118fff28f4098cd7b0a7f97814a90565ebe6 + languageName: node + linkType: hard + +"@next/swc-darwin-arm64@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-darwin-arm64@npm:16.1.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-darwin-x64@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-darwin-x64@npm:16.1.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-linux-arm64-gnu@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-arm64-gnu@npm:16.1.6" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-arm64-musl@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-arm64-musl@npm:16.1.6" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-x64-gnu@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-x64-gnu@npm:16.1.6" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-x64-musl@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-x64-musl@npm:16.1.6" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-win32-arm64-msvc@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-win32-arm64-msvc@npm:16.1.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-win32-x64-msvc@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-win32-x64-msvc@npm:16.1.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@swc/helpers@npm:0.5.15": + version: 0.5.15 + resolution: "@swc/helpers@npm:0.5.15" + dependencies: + tslib: "npm:^2.8.0" + checksum: 10c0/33002f74f6f885f04c132960835fdfc474186983ea567606db62e86acd0680ca82f34647e8e610f4e1e422d1c16fce729dde22cd3b797ab1fd9061a825dabca4 + languageName: node + linkType: hard + +"@types/node@npm:22.16.0": + version: 22.16.0 + resolution: "@types/node@npm:22.16.0" + dependencies: + undici-types: "npm:~6.21.0" + checksum: 10c0/6219b521062f6c38d4d85ebd25807bd7f2bc703a5acba24e2c6716938d9d6cefd6fafd7b5156f61580eb58a0d82e8921751b778655675389631d813e5f261c03 + languageName: node + linkType: hard + +"@types/react-dom@npm:19.2.3": + version: 19.2.3 + resolution: "@types/react-dom@npm:19.2.3" + peerDependencies: + "@types/react": ^19.2.0 + checksum: 10c0/b486ebe0f4e2fb35e2e108df1d8fc0927ca5d6002d5771e8a739de11239fe62d0e207c50886185253c99eb9dedfeeb956ea7429e5ba17f6693c7acb4c02f8cd1 + languageName: node + linkType: hard + +"@types/react@npm:19.2.8": + version: 19.2.8 + resolution: "@types/react@npm:19.2.8" + dependencies: + csstype: "npm:^3.2.2" + checksum: 10c0/832834998c4ee971fca72ecf1eb95dc924ad3931a2112c687a4dae498aabd115c5fa4db09186853e34a646226b0223808c8f867df03d17601168f9cf119448de + languageName: node + linkType: hard + +"baseline-browser-mapping@npm:^2.8.3": + version: 2.9.19 + resolution: "baseline-browser-mapping@npm:2.9.19" + bin: + baseline-browser-mapping: dist/cli.js + checksum: 10c0/569928db78bcd081953d7db79e4243a59a579a34b4ae1806b9b42d3b7f84e5bc40e6e82ae4fa06e7bef8291bf747b33b3f9ef5d3c6e1e420cb129d9295536129 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001579": + version: 1.0.30001768 + resolution: "caniuse-lite@npm:1.0.30001768" + checksum: 10c0/16808cb39f9563098deab6d45bcd0642a79fc5ace8dbcea8106b008b179820353e3ec089ed7e54f1f3c8bb84f2c2835b451f308212d8f36c2b7942f879e91955 + languageName: node + linkType: hard + +"client-only@npm:0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 10c0/9d6cfd0c19e1c96a434605added99dff48482152af791ec4172fb912a71cff9027ff174efd8cdb2160cc7f377543e0537ffc462d4f279bc4701de3f2a3c4b358 + languageName: node + linkType: hard + +"csstype@npm:^3.2.2": + version: 3.2.3 + resolution: "csstype@npm:3.2.3" + checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce + languageName: node + linkType: hard + +"detect-libc@npm:^2.1.2": + version: 2.1.2 + resolution: "detect-libc@npm:2.1.2" + checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.6": + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/40e7f70b3d15f725ca072dfc4f74e81fcf1fbb02e491cf58ac0c79093adc9b0a73b152bcde57df4b79cd097e13023d7504acb38404a4da7bc1cd8e887b82fe0b + languageName: node + linkType: hard + +"next@npm:16.1.6": + version: 16.1.6 + resolution: "next@npm:16.1.6" + dependencies: + "@next/env": "npm:16.1.6" + "@next/swc-darwin-arm64": "npm:16.1.6" + "@next/swc-darwin-x64": "npm:16.1.6" + "@next/swc-linux-arm64-gnu": "npm:16.1.6" + "@next/swc-linux-arm64-musl": "npm:16.1.6" + "@next/swc-linux-x64-gnu": "npm:16.1.6" + "@next/swc-linux-x64-musl": "npm:16.1.6" + "@next/swc-win32-arm64-msvc": "npm:16.1.6" + "@next/swc-win32-x64-msvc": "npm:16.1.6" + "@swc/helpers": "npm:0.5.15" + baseline-browser-mapping: "npm:^2.8.3" + caniuse-lite: "npm:^1.0.30001579" + postcss: "npm:8.4.31" + sharp: "npm:^0.34.4" + styled-jsx: "npm:5.1.6" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.51.1 + babel-plugin-react-compiler: "*" + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + sharp: + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/543766bf879bb5a5d454dc18cb302953270a92efba1d01dd028ea83c64b69573ce7d6e6c3759ecbaabec0a84131b0237263c24d1ccd7c8a97205e776dcd34e0b + languageName: node + linkType: hard + +"nextjs-app-router@workspace:.": + version: 0.0.0-use.local + resolution: "nextjs-app-router@workspace:." + dependencies: + "@datadog/browser-rum": "file:../../../packages/rum/package.tgz" + "@datadog/browser-rum-react": "file:../../../packages/rum-react/package.tgz" + "@types/node": "npm:22.16.0" + "@types/react": "npm:19.2.8" + "@types/react-dom": "npm:19.2.3" + next: "npm:16.1.6" + react: "npm:19.2.3" + react-dom: "npm:19.2.3" + typescript: "npm:5.9.3" + languageName: unknown + linkType: soft + +"picocolors@npm:^1.0.0": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"postcss@npm:8.4.31": + version: 8.4.31 + resolution: "postcss@npm:8.4.31" + dependencies: + nanoid: "npm:^3.3.6" + picocolors: "npm:^1.0.0" + source-map-js: "npm:^1.0.2" + checksum: 10c0/748b82e6e5fc34034dcf2ae88ea3d11fd09f69b6c50ecdd3b4a875cfc7cdca435c958b211e2cb52355422ab6fccb7d8f2f2923161d7a1b281029e4a913d59acf + languageName: node + linkType: hard + +"react-dom@npm:19.2.3": + version: 19.2.3 + resolution: "react-dom@npm:19.2.3" + dependencies: + scheduler: "npm:^0.27.0" + peerDependencies: + react: ^19.2.3 + checksum: 10c0/dc43f7ede06f46f3acc16ee83107c925530de9b91d1d0b3824583814746ff4c498ea64fd65cd83aba363205268adff52e2827c582634ae7b15069deaeabc4892 + languageName: node + linkType: hard + +"react@npm:19.2.3": + version: 19.2.3 + resolution: "react@npm:19.2.3" + checksum: 10c0/094220b3ba3a76c1b668f972ace1dd15509b157aead1b40391d1c8e657e720c201d9719537375eff08f5e0514748c0319063392a6f000e31303aafc4471f1436 + languageName: node + linkType: hard + +"scheduler@npm:^0.27.0": + version: 0.27.0 + resolution: "scheduler@npm:0.27.0" + checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452 + languageName: node + linkType: hard + +"semver@npm:^7.7.3": + version: 7.7.3 + resolution: "semver@npm:7.7.3" + bin: + semver: bin/semver.js + checksum: 10c0/4afe5c986567db82f44c8c6faef8fe9df2a9b1d98098fc1721f57c696c4c21cebd572f297fc21002f81889492345b8470473bc6f4aff5fb032a6ea59ea2bc45e + languageName: node + linkType: hard + +"sharp@npm:^0.34.4": + version: 0.34.5 + resolution: "sharp@npm:0.34.5" + dependencies: + "@img/colour": "npm:^1.0.0" + "@img/sharp-darwin-arm64": "npm:0.34.5" + "@img/sharp-darwin-x64": "npm:0.34.5" + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + "@img/sharp-linux-arm": "npm:0.34.5" + "@img/sharp-linux-arm64": "npm:0.34.5" + "@img/sharp-linux-ppc64": "npm:0.34.5" + "@img/sharp-linux-riscv64": "npm:0.34.5" + "@img/sharp-linux-s390x": "npm:0.34.5" + "@img/sharp-linux-x64": "npm:0.34.5" + "@img/sharp-linuxmusl-arm64": "npm:0.34.5" + "@img/sharp-linuxmusl-x64": "npm:0.34.5" + "@img/sharp-wasm32": "npm:0.34.5" + "@img/sharp-win32-arm64": "npm:0.34.5" + "@img/sharp-win32-ia32": "npm:0.34.5" + "@img/sharp-win32-x64": "npm:0.34.5" + detect-libc: "npm:^2.1.2" + semver: "npm:^7.7.3" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-libvips-darwin-arm64": + optional: true + "@img/sharp-libvips-darwin-x64": + optional: true + "@img/sharp-libvips-linux-arm": + optional: true + "@img/sharp-libvips-linux-arm64": + optional: true + "@img/sharp-libvips-linux-ppc64": + optional: true + "@img/sharp-libvips-linux-riscv64": + optional: true + "@img/sharp-libvips-linux-s390x": + optional: true + "@img/sharp-libvips-linux-x64": + optional: true + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + "@img/sharp-libvips-linuxmusl-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-ppc64": + optional: true + "@img/sharp-linux-riscv64": + optional: true + "@img/sharp-linux-s390x": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-linuxmusl-arm64": + optional: true + "@img/sharp-linuxmusl-x64": + optional: true + "@img/sharp-wasm32": + optional: true + "@img/sharp-win32-arm64": + optional: true + "@img/sharp-win32-ia32": + optional: true + "@img/sharp-win32-x64": + optional: true + checksum: 10c0/fd79e29df0597a7d5704b8461c51f944ead91a5243691697be6e8243b966402beda53ddc6f0a53b96ea3cb8221f0b244aa588114d3ebf8734fb4aefd41ab802f + languageName: node + linkType: hard + +"source-map-js@npm:^1.0.2": + version: 1.2.1 + resolution: "source-map-js@npm:1.2.1" + checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf + languageName: node + linkType: hard + +"styled-jsx@npm:5.1.6": + version: 5.1.6 + resolution: "styled-jsx@npm:5.1.6" + dependencies: + client-only: "npm:0.0.1" + peerDependencies: + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + peerDependenciesMeta: + "@babel/core": + optional: true + babel-plugin-macros: + optional: true + checksum: 10c0/ace50e7ea5ae5ae6a3b65a50994c51fca6ae7df9c7ecfd0104c36be0b4b3a9c5c1a2374d16e2a11e256d0b20be6d47256d768ecb4f91ab390f60752a075780f5 + languageName: node + linkType: hard + +"tslib@npm:^2.4.0, tslib@npm:^2.8.0": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + +"typescript@npm:5.9.3": + version: 5.9.3 + resolution: "typescript@npm:5.9.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A5.9.3#optional!builtin": + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 + languageName: node + linkType: hard + +"undici-types@npm:~6.21.0": + version: 6.21.0 + resolution: "undici-types@npm:6.21.0" + checksum: 10c0/c01ed51829b10aa72fc3ce64b747f8e74ae9b60eafa19a7b46ef624403508a54c526ffab06a14a26b3120d055e1104d7abe7c9017e83ced038ea5cf52f8d5e04 + languageName: node + linkType: hard diff --git a/test/e2e/lib/framework/createTest.ts b/test/e2e/lib/framework/createTest.ts index 56ead6f1f0..318091a5dd 100644 --- a/test/e2e/lib/framework/createTest.ts +++ b/test/e2e/lib/framework/createTest.ts @@ -9,6 +9,7 @@ import { BrowserLogsManager, deleteAllCookies, getBrowserName, sendXhr } from '. import { DEFAULT_LOGS_CONFIGURATION, DEFAULT_RUM_CONFIGURATION } from '../helpers/configuration' import { validateRumFormat } from '../helpers/validation' import type { BrowserConfiguration } from '../../../browsers.conf' +import { NEXTJS_APP_URL } from '../helpers/playwright' import { IntakeRegistry } from './intakeRegistry' import { flushEvents } from './flushEvents' import type { Servers } from './httpServers' @@ -39,6 +40,7 @@ interface TestContext { deleteAllCookies: () => Promise sendXhr: (url: string, headers?: string[][]) => Promise evaluateInWorker: (fn: () => void) => Promise + isNextjsApp: boolean } type TestRunner = (testContext: TestContext) => Promise | void @@ -60,6 +62,7 @@ class TestBuilder { } = {} private worker: Worker | undefined private hostName?: string + private nextjsApp = false constructor( private title: string, @@ -111,6 +114,12 @@ class TestBuilder { return this } + withNextjsApp() { + this.nextjsApp = true + this.setups = [{ factory: () => '' }] + return this + } + withBasePath(newBasePath: string) { this.basePath = newBasePath return this @@ -179,6 +188,7 @@ class TestBuilder { testFixture: this.testFixture, extension: this.extension, hostName: this.hostName, + nextjsApp: this.nextjsApp, worker: this.worker, callerLocation: this.callerLocation, } @@ -302,17 +312,22 @@ function declareTest(title: string, setupOptions: SetupOptions, factory: SetupFa const testContext = createTestContext(servers, page, context, browserLogs, browserName, setupOptions) servers.intake.bindServerApp(createIntakeServerApp(testContext.intakeRegistry)) - const setup = factory(setupOptions, servers) - servers.base.bindServerApp( - createMockServerApp(servers, setup, setupOptions.remoteConfiguration, setupOptions.worker) - ) - servers.crossOrigin.bindServerApp(createMockServerApp(servers, setup)) + // Next.js runs on its own server, only set up mock server for other test apps + if (!setupOptions.nextjsApp) { + const setup = factory(setupOptions, servers) + servers.base.bindServerApp( + createMockServerApp(servers, setup, setupOptions.remoteConfiguration, setupOptions.worker) + ) + servers.crossOrigin.bindServerApp(createMockServerApp(servers, setup)) + } - await setUpTest(browserLogs, testContext) + await setUpTest(browserLogs, testContext, setupOptions, servers) try { await runner(testContext) - tearDownPassedTest(testContext) + if (!setupOptions.nextjsApp) { + tearDownPassedTest(testContext) + } } finally { await tearDownTest(testContext) } @@ -325,12 +340,17 @@ function createTestContext( browserContext: BrowserContext, browserLogsManager: BrowserLogsManager, browserName: TestContext['browserName'], - { basePath, hostName }: SetupOptions + { basePath, hostName, nextjsApp }: SetupOptions ): TestContext { - const baseUrl = new URL(basePath, servers.base.origin) + let baseUrl: URL - if (hostName) { - baseUrl.hostname = hostName + if (nextjsApp) { + baseUrl = new URL(basePath, NEXTJS_APP_URL) + } else { + baseUrl = new URL(basePath, servers.base.origin) + if (hostName) { + baseUrl.hostname = hostName + } } return { @@ -340,6 +360,7 @@ function createTestContext( page, browserContext, browserName, + isNextjsApp: !!nextjsApp, withBrowserLogs: (cb: (logs: BrowserLog[]) => void) => { try { cb(browserLogsManager.get()) @@ -365,7 +386,7 @@ function createTestContext( }, `(${fn.toString()})()`) }, flushBrowserLogs: () => browserLogsManager.clear(), - flushEvents: () => flushEvents(page), + flushEvents: () => flushEvents(page, nextjsApp ? NEXTJS_APP_URL : undefined), deleteAllCookies: () => deleteAllCookies(browserContext), sendXhr: (url: string, headers?: string[][]) => sendXhr(page, url, headers), getExtensionId: async () => { @@ -380,7 +401,12 @@ function createTestContext( } } -async function setUpTest(browserLogsManager: BrowserLogsManager, { baseUrl, page, browserContext }: TestContext) { +async function setUpTest( + browserLogsManager: BrowserLogsManager, + { baseUrl, page, browserContext }: TestContext, + setupOptions: SetupOptions, + servers: Servers +) { browserContext.on('console', (msg) => { browserLogsManager.add({ level: msg.type() as BrowserLog['level'], @@ -399,6 +425,20 @@ async function setUpTest(browserLogsManager: BrowserLogsManager, { baseUrl, page }) }) + // For Next.js apps, inject RUM configuration before navigation + if (setupOptions.nextjsApp && setupOptions.rum) { + const rumConfig = { + ...setupOptions.rum, + proxy: servers.intake.origin, + } + await page.addInitScript( + ({ config }) => { + ;(window as any).RUM_CONFIGURATION = config + }, + { config: rumConfig } + ) + } + await page.goto(baseUrl) await waitForServersIdle() } diff --git a/test/e2e/lib/framework/flushEvents.ts b/test/e2e/lib/framework/flushEvents.ts index 7567d42116..1a30c26e4c 100644 --- a/test/e2e/lib/framework/flushEvents.ts +++ b/test/e2e/lib/framework/flushEvents.ts @@ -2,7 +2,7 @@ import type { Page } from '@playwright/test' import { getTestServers, waitForServersIdle } from './httpServers' import { waitForRequests } from './waitForRequests' -export async function flushEvents(page: Page) { +export async function flushEvents(page: Page, gotoUrl?: string) { await waitForRequests(page) const servers = await getTestServers() @@ -21,6 +21,6 @@ export async function flushEvents(page: Page) { // The issue mainly occurs with local e2e tests (not browserstack), because the network latency is // very low (same machine), so the request resolves very quickly. In real life conditions, this // issue is mitigated, because requests will likely take a few milliseconds to reach the server. - await page.goto(`${servers.base.origin}/ok?duration=200`) + await page.goto(gotoUrl ?? `${servers.base.origin}/ok?duration=200`) await waitForServersIdle() } diff --git a/test/e2e/lib/framework/pageSetups.ts b/test/e2e/lib/framework/pageSetups.ts index 22ec67f9b8..9e9db18b0f 100644 --- a/test/e2e/lib/framework/pageSetups.ts +++ b/test/e2e/lib/framework/pageSetups.ts @@ -26,6 +26,7 @@ export interface SetupOptions { logsConfiguration?: LogsInitConfiguration } hostName?: string + nextjsApp?: boolean worker?: WorkerOptions callerLocation?: CallerLocation } diff --git a/test/e2e/lib/helpers/playwright.ts b/test/e2e/lib/helpers/playwright.ts index a6affa5973..657eabdabf 100644 --- a/test/e2e/lib/helpers/playwright.ts +++ b/test/e2e/lib/helpers/playwright.ts @@ -4,6 +4,7 @@ import { getBuildInfos } from '../../../envUtils.ts' import packageJson from '../../../../package.json' with { type: 'json' } export const DEV_SERVER_BASE_URL = 'http://localhost:8080' +export const NEXTJS_APP_URL = 'http://localhost:3000' export function getPlaywrightConfigBrowserName(name: string): PlaywrightWorkerOptions['browserName'] { if (name.includes('firefox')) { diff --git a/test/e2e/playwright.base.config.ts b/test/e2e/playwright.base.config.ts index 9bc8fb1a0d..b7393fb63b 100644 --- a/test/e2e/playwright.base.config.ts +++ b/test/e2e/playwright.base.config.ts @@ -1,7 +1,7 @@ import path from 'path' import type { ReporterDescription, Config } from '@playwright/test' import { getTestReportDirectory } from '../envUtils' -import { DEV_SERVER_BASE_URL } from './lib/helpers/playwright' +import { DEV_SERVER_BASE_URL, NEXTJS_APP_URL } from './lib/helpers/playwright' const isCi = !!process.env.CI const isLocal = !isCi @@ -31,13 +31,24 @@ export const config: Config = { trace: isCi ? 'off' : 'retain-on-failure', }, - webServer: isLocal - ? { - stdout: 'pipe', - cwd: path.join(__dirname, '../..'), - command: 'yarn dev', - url: DEV_SERVER_BASE_URL, - reuseExistingServer: true, - } - : undefined, + webServer: [ + ...(isLocal + ? [ + { + stdout: 'pipe' as const, + cwd: path.join(__dirname, '../..'), + command: 'yarn dev', + url: DEV_SERVER_BASE_URL, + reuseExistingServer: true, + }, + ] + : []), + { + stdout: 'pipe' as const, + cwd: path.join(__dirname, '../apps/nextjs-app-router'), + command: isLocal ? 'yarn dev' : 'yarn start', + url: NEXTJS_APP_URL, + reuseExistingServer: true, + }, + ], } diff --git a/test/e2e/scenario/nextjsPlugin.scenario.ts b/test/e2e/scenario/nextjsPlugin.scenario.ts new file mode 100644 index 0000000000..4d0734fc9f --- /dev/null +++ b/test/e2e/scenario/nextjsPlugin.scenario.ts @@ -0,0 +1,121 @@ +import { test, expect } from '@playwright/test' +import { createTest } from '../lib/framework' + +test.describe('nextjs app router', () => { + createTest('should track initial view') + .withRum() + .withNextjsApp() + .run(async ({ page, flushEvents, intakeRegistry }) => { + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + const homeView = viewEvents.find((e) => e.view.name === '/' && e.view.loading_type === 'initial_load') + expect(homeView).toBeDefined() + }) + + createTest('should normalize dynamic route to /user/:id') + .withRum() + .withNextjsApp() + .run(async ({ page, flushEvents, intakeRegistry }) => { + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42') + + await page.click('text=Back to Home') + await page.waitForURL('**/localhost:3000/') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + expect(viewEvents.length).toBeGreaterThanOrEqual(2) + + const homeView = viewEvents.find((e) => e.view.name === '/') + expect(homeView).toBeDefined() + + const userView = viewEvents.find((e) => e.view.name === '/user/:id') + expect(userView).toBeDefined() + expect(userView?.view.loading_type).toBe('route_change') + }) + + createTest('should track SPA navigation with loading_time') + .withRum() + .withNextjsApp() + .run(async ({ page, flushEvents, intakeRegistry }) => { + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + const userView = viewEvents.find((e) => e.view.name === '/user/:id' && e.view.loading_type === 'route_change') + expect(userView).toBeDefined() + expect(userView?.view.loading_time).toBeDefined() + expect(userView?.view.loading_time).toBeGreaterThan(0) + }) + + createTest('should track back navigation via popstate') + .withRum() + .withNextjsApp() + .run(async ({ page, flushEvents, intakeRegistry }) => { + await page.click('text=Go to User 42') + await page.waitForURL('**/user/42') + + await page.goBack() + await page.waitForURL('**/localhost:3000/') + + await flushEvents() + + const viewEvents = intakeRegistry.rumViewEvents + // Should have at least 3 views: initial /, /user/:id, and back to / + expect(viewEvents.filter((e) => e.view.name === '/').length).toBeGreaterThanOrEqual(2) + }) + + createTest('should send a react component render vital event') + .withRum() + .withNextjsApp() + .run(async ({ page, flushEvents, intakeRegistry }) => { + await page.click('text=Go to Tracked Component') + await page.waitForURL('**/tracked') + + await page.click('text=Back to Home') + await page.waitForURL('**/localhost:3000/') + + await flushEvents() + + const vitalEvents = intakeRegistry.rumVitalEvents + expect(vitalEvents.length).toBeGreaterThan(0) + + const trackedVital = vitalEvents.find((e) => e.vital.description === 'TrackedPage') + expect(trackedVital).toBeDefined() + expect(trackedVital?.vital.duration).toEqual(expect.any(Number)) + }) + + createTest('should capture react error from error boundary') + .withRum() + .withNextjsApp() + .run(async ({ page, flushEvents, intakeRegistry, withBrowserLogs }) => { + await page.click('text=Go to Error Test') + await page.waitForURL('**/error-test') + await page.click('#error-button') + + await page.click('text=Back to Home') + await page.waitForURL('**/localhost:3000/') + + await flushEvents() + + const errorEvents = intakeRegistry.rumErrorEvents + expect(errorEvents.length).toBeGreaterThan(0) + + const boundaryError = errorEvents.find((e) => e.error.message?.includes('Error triggered by button')) + expect(boundaryError).toBeDefined() + expect(boundaryError?.error.source).toBe('custom') + expect(boundaryError?.context?.framework).toBe('react') + expect(boundaryError?.error.component_stack).toBeDefined() + + withBrowserLogs((browserLogs) => { + expect(browserLogs.length).toBeGreaterThan(0) + }) + }) +}) diff --git a/test/unit/karma.base.conf.js b/test/unit/karma.base.conf.js index 480210acd5..5279a2886c 100644 --- a/test/unit/karma.base.conf.js +++ b/test/unit/karma.base.conf.js @@ -126,7 +126,7 @@ function overrideTsLoaderRule(module) { // We use swc-loader to transpile some dependencies that are using syntax not compatible with browsers we use for testing module.rules.push({ test: /\.m?js$/, - include: /node_modules\/(react|react-router-dom|react-dom|react-router|turbo-stream)/, + include: /node_modules\/(react|react-router-dom|react-dom|react-router|turbo-stream|next)/, use: { loader: 'swc-loader', options: { diff --git a/yarn.lock b/yarn.lock index 6c5aa88a58..469d61d899 100644 --- a/yarn.lock +++ b/yarn.lock @@ -330,12 +330,14 @@ __metadata: "@datadog/browser-rum-core": "npm:6.27.1" "@types/react": "npm:19.2.11" "@types/react-dom": "npm:19.2.3" + next: "npm:16.1.6" react: "npm:19.2.4" react-dom: "npm:19.2.4" react-router: "npm:7.13.0" react-router-dom: "npm:7.13.0" react-router-dom-6: "npm:react-router-dom@6.30.3" peerDependencies: + next: ">=13" react: 18 || 19 react-router: 6 || 7 react-router-dom: 6 || 7 @@ -344,6 +346,8 @@ __metadata: optional: true "@datadog/browser-rum-slim": optional: true + next: + optional: true react: optional: true react-router: @@ -462,7 +466,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/runtime@npm:^1.1.0, @emnapi/runtime@npm:^1.4.3": +"@emnapi/runtime@npm:^1.1.0, @emnapi/runtime@npm:^1.4.3, @emnapi/runtime@npm:^1.7.0": version: 1.8.1 resolution: "@emnapi/runtime@npm:1.8.1" dependencies: @@ -873,6 +877,233 @@ __metadata: languageName: node linkType: hard +"@img/colour@npm:^1.0.0": + version: 1.0.0 + resolution: "@img/colour@npm:1.0.0" + checksum: 10c0/02261719c1e0d7aa5a2d585981954f2ac126f0c432400aa1a01b925aa2c41417b7695da8544ee04fd29eba7ecea8eaf9b8bef06f19dc8faba78f94eeac40667d + languageName: node + linkType: hard + +"@img/sharp-darwin-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-arm64": + optional: true + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-darwin-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-darwin-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-darwin-x64": + optional: true + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.4" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.4" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-arm@npm:1.2.4" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-ppc64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.4" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-riscv64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-riscv64@npm:1.2.4" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-s390x@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.4" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linux-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-x64@npm:1.2.4": + version: 1.2.4 + resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.4" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linux-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-arm@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-arm@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-arm": + optional: true + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-ppc64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-ppc64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-ppc64": + optional: true + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-riscv64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-riscv64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-riscv64": + optional: true + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-s390x@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-s390x@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-s390x": + optional: true + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linux-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linux-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-linuxmusl-x64@npm:0.34.5" + dependencies: + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-wasm32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-wasm32@npm:0.34.5" + dependencies: + "@emnapi/runtime": "npm:^1.7.0" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@img/sharp-win32-arm64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-arm64@npm:0.34.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-win32-ia32@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-ia32@npm:0.34.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@img/sharp-win32-x64@npm:0.34.5": + version: 0.34.5 + resolution: "@img/sharp-win32-x64@npm:0.34.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@inquirer/ansi@npm:^1.0.0, @inquirer/ansi@npm:^1.0.2": version: 1.0.2 resolution: "@inquirer/ansi@npm:1.0.2" @@ -1634,6 +1865,69 @@ __metadata: languageName: node linkType: hard +"@next/env@npm:16.1.6": + version: 16.1.6 + resolution: "@next/env@npm:16.1.6" + checksum: 10c0/ed7023edb94b9b2e5da3f9c99d08b614da9757c1edd0ecec792fce4d336b4f0c64db1a84955e07cfbd848b9e61c4118fff28f4098cd7b0a7f97814a90565ebe6 + languageName: node + linkType: hard + +"@next/swc-darwin-arm64@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-darwin-arm64@npm:16.1.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-darwin-x64@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-darwin-x64@npm:16.1.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-linux-arm64-gnu@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-arm64-gnu@npm:16.1.6" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-arm64-musl@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-arm64-musl@npm:16.1.6" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-x64-gnu@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-x64-gnu@npm:16.1.6" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-x64-musl@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-linux-x64-musl@npm:16.1.6" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-win32-arm64-msvc@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-win32-arm64-msvc@npm:16.1.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-win32-x64-msvc@npm:16.1.6": + version: 16.1.6 + resolution: "@next/swc-win32-x64-msvc@npm:16.1.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -2680,6 +2974,15 @@ __metadata: languageName: node linkType: hard +"@swc/helpers@npm:0.5.15": + version: 0.5.15 + resolution: "@swc/helpers@npm:0.5.15" + dependencies: + tslib: "npm:^2.8.0" + checksum: 10c0/33002f74f6f885f04c132960835fdfc474186983ea567606db62e86acd0680ca82f34647e8e610f4e1e422d1c16fce729dde22cd3b797ab1fd9061a825dabca4 + languageName: node + linkType: hard + "@swc/types@npm:^0.1.25": version: 0.1.25 resolution: "@swc/types@npm:0.1.25" @@ -4278,7 +4581,7 @@ __metadata: languageName: node linkType: hard -"baseline-browser-mapping@npm:^2.9.0": +"baseline-browser-mapping@npm:^2.8.3, baseline-browser-mapping@npm:^2.9.0": version: 2.9.19 resolution: "baseline-browser-mapping@npm:2.9.19" bin: @@ -4757,6 +5060,13 @@ __metadata: languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001579": + version: 1.0.30001770 + resolution: "caniuse-lite@npm:1.0.30001770" + checksum: 10c0/02d15a8b723af65318cb4d888a52bb090076898da7b0de99e8676d537f8d1d2ae4797e81518e1e30cbfe84c33b048c322e8bfafc5b23cfee8defb0d2bf271149 + languageName: node + linkType: hard + "caniuse-lite@npm:^1.0.30001759": version: 1.0.30001768 resolution: "caniuse-lite@npm:1.0.30001768" @@ -5010,6 +5320,13 @@ __metadata: languageName: node linkType: hard +"client-only@npm:0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 10c0/9d6cfd0c19e1c96a434605added99dff48482152af791ec4172fb912a71cff9027ff174efd8cdb2160cc7f377543e0537ffc462d4f279bc4701de3f2a3c4b358 + languageName: node + linkType: hard + "cliui@npm:^7.0.2": version: 7.0.4 resolution: "cliui@npm:7.0.4" @@ -5840,6 +6157,13 @@ __metadata: languageName: node linkType: hard +"detect-libc@npm:^2.1.2": + version: 2.1.2 + resolution: "detect-libc@npm:2.1.2" + checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4 + languageName: node + linkType: hard + "detect-node-es@npm:^1.1.0": version: 1.1.0 resolution: "detect-node-es@npm:1.1.0" @@ -10628,7 +10952,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.11": +"nanoid@npm:^3.3.11, nanoid@npm:^3.3.6": version: 3.3.11 resolution: "nanoid@npm:3.3.11" bin: @@ -10681,6 +11005,66 @@ __metadata: languageName: node linkType: hard +"next@npm:16.1.6": + version: 16.1.6 + resolution: "next@npm:16.1.6" + dependencies: + "@next/env": "npm:16.1.6" + "@next/swc-darwin-arm64": "npm:16.1.6" + "@next/swc-darwin-x64": "npm:16.1.6" + "@next/swc-linux-arm64-gnu": "npm:16.1.6" + "@next/swc-linux-arm64-musl": "npm:16.1.6" + "@next/swc-linux-x64-gnu": "npm:16.1.6" + "@next/swc-linux-x64-musl": "npm:16.1.6" + "@next/swc-win32-arm64-msvc": "npm:16.1.6" + "@next/swc-win32-x64-msvc": "npm:16.1.6" + "@swc/helpers": "npm:0.5.15" + baseline-browser-mapping: "npm:^2.8.3" + caniuse-lite: "npm:^1.0.30001579" + postcss: "npm:8.4.31" + sharp: "npm:^0.34.4" + styled-jsx: "npm:5.1.6" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.51.1 + babel-plugin-react-compiler: "*" + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + sharp: + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/543766bf879bb5a5d454dc18cb302953270a92efba1d01dd028ea83c64b69573ce7d6e6c3759ecbaabec0a84131b0237263c24d1ccd7c8a97205e776dcd34e0b + languageName: node + linkType: hard + "no-case@npm:^3.0.4": version: 3.0.4 resolution: "no-case@npm:3.0.4" @@ -11815,7 +12199,7 @@ __metadata: languageName: node linkType: hard -"picocolors@npm:^1.1.1": +"picocolors@npm:^1.0.0, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 @@ -11990,6 +12374,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:8.4.31": + version: 8.4.31 + resolution: "postcss@npm:8.4.31" + dependencies: + nanoid: "npm:^3.3.6" + picocolors: "npm:^1.0.0" + source-map-js: "npm:^1.0.2" + checksum: 10c0/748b82e6e5fc34034dcf2ae88ea3d11fd09f69b6c50ecdd3b4a875cfc7cdca435c958b211e2cb52355422ab6fccb7d8f2f2923161d7a1b281029e4a913d59acf + languageName: node + linkType: hard + "postcss@npm:^8.5.6": version: 8.5.6 resolution: "postcss@npm:8.5.6" @@ -13366,6 +13761,90 @@ __metadata: languageName: node linkType: hard +"sharp@npm:^0.34.4": + version: 0.34.5 + resolution: "sharp@npm:0.34.5" + dependencies: + "@img/colour": "npm:^1.0.0" + "@img/sharp-darwin-arm64": "npm:0.34.5" + "@img/sharp-darwin-x64": "npm:0.34.5" + "@img/sharp-libvips-darwin-arm64": "npm:1.2.4" + "@img/sharp-libvips-darwin-x64": "npm:1.2.4" + "@img/sharp-libvips-linux-arm": "npm:1.2.4" + "@img/sharp-libvips-linux-arm64": "npm:1.2.4" + "@img/sharp-libvips-linux-ppc64": "npm:1.2.4" + "@img/sharp-libvips-linux-riscv64": "npm:1.2.4" + "@img/sharp-libvips-linux-s390x": "npm:1.2.4" + "@img/sharp-libvips-linux-x64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4" + "@img/sharp-linux-arm": "npm:0.34.5" + "@img/sharp-linux-arm64": "npm:0.34.5" + "@img/sharp-linux-ppc64": "npm:0.34.5" + "@img/sharp-linux-riscv64": "npm:0.34.5" + "@img/sharp-linux-s390x": "npm:0.34.5" + "@img/sharp-linux-x64": "npm:0.34.5" + "@img/sharp-linuxmusl-arm64": "npm:0.34.5" + "@img/sharp-linuxmusl-x64": "npm:0.34.5" + "@img/sharp-wasm32": "npm:0.34.5" + "@img/sharp-win32-arm64": "npm:0.34.5" + "@img/sharp-win32-ia32": "npm:0.34.5" + "@img/sharp-win32-x64": "npm:0.34.5" + detect-libc: "npm:^2.1.2" + semver: "npm:^7.7.3" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-libvips-darwin-arm64": + optional: true + "@img/sharp-libvips-darwin-x64": + optional: true + "@img/sharp-libvips-linux-arm": + optional: true + "@img/sharp-libvips-linux-arm64": + optional: true + "@img/sharp-libvips-linux-ppc64": + optional: true + "@img/sharp-libvips-linux-riscv64": + optional: true + "@img/sharp-libvips-linux-s390x": + optional: true + "@img/sharp-libvips-linux-x64": + optional: true + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + "@img/sharp-libvips-linuxmusl-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-ppc64": + optional: true + "@img/sharp-linux-riscv64": + optional: true + "@img/sharp-linux-s390x": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-linuxmusl-arm64": + optional: true + "@img/sharp-linuxmusl-x64": + optional: true + "@img/sharp-wasm32": + optional: true + "@img/sharp-win32-arm64": + optional: true + "@img/sharp-win32-ia32": + optional: true + "@img/sharp-win32-x64": + optional: true + checksum: 10c0/fd79e29df0597a7d5704b8461c51f944ead91a5243691697be6e8243b966402beda53ddc6f0a53b96ea3cb8221f0b244aa588114d3ebf8734fb4aefd41ab802f + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -13587,7 +14066,7 @@ __metadata: languageName: node linkType: hard -"source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": +"source-map-js@npm:^1.0.2, source-map-js@npm:^1.2.0, source-map-js@npm:^1.2.1": version: 1.2.1 resolution: "source-map-js@npm:1.2.1" checksum: 10c0/7bda1fc4c197e3c6ff17de1b8b2c20e60af81b63a52cb32ec5a5d67a20a7d42651e2cb34ebe93833c5a2a084377e17455854fee3e21e7925c64a51b6a52b0faf @@ -14004,6 +14483,22 @@ __metadata: languageName: node linkType: hard +"styled-jsx@npm:5.1.6": + version: 5.1.6 + resolution: "styled-jsx@npm:5.1.6" + dependencies: + client-only: "npm:0.0.1" + peerDependencies: + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + peerDependenciesMeta: + "@babel/core": + optional: true + babel-plugin-macros: + optional: true + checksum: 10c0/ace50e7ea5ae5ae6a3b65a50994c51fca6ae7df9c7ecfd0104c36be0b4b3a9c5c1a2374d16e2a11e256d0b20be6d47256d768ecb4f91ab390f60752a075780f5 + languageName: node + linkType: hard + "supports-color@npm:^7.1.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" @@ -14384,7 +14879,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.8.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62