diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/README.md b/dev-packages/e2e-tests/test-applications/solid-solidrouter/README.md deleted file mode 100644 index 81e5eb6c2d40..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/README.md +++ /dev/null @@ -1,40 +0,0 @@ -## Usage - -Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`. - -This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely -be removed once you clone a template. - -```bash -$ npm install # or pnpm install or yarn install -``` - -## Exploring the template - -This template's goal is to showcase the routing features of Solid. It also showcase how the router and Suspense work -together to parallelize data fetching tied to a route via the `.data.ts` pattern. - -You can learn more about it on the [`@solidjs/router` repository](https://github.com/solidjs/solid-router) - -### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) - -## Available Scripts - -In the project directory, you can run: - -### `npm run dev` or `npm start` - -Runs the app in the development mode.
Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.
- -### `npm run build` - -Builds the app for production to the `dist` folder.
It correctly bundles Solid in production mode and optimizes the -build for the best performance. - -The build is minified and the filenames include the hashes.
Your app is ready to be deployed! - -## Deployment - -You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.) diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/index.html b/dev-packages/e2e-tests/test-applications/solid-solidrouter/index.html deleted file mode 100644 index 1905a0429019..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - Solid App - - - -
- - - - diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/postcss.config.js b/dev-packages/e2e-tests/test-applications/solid-solidrouter/postcss.config.js deleted file mode 100644 index 12a703d900da..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/errors/404.tsx b/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/errors/404.tsx deleted file mode 100644 index 56e5ad5e3be0..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/errors/404.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export default function NotFound() { - return ( -
-

404: Not Found

-

It's gone 😞

-
- ); -} diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/index.css b/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/index.css deleted file mode 100644 index b5c61c956711..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/index.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/index.tsx b/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/index.tsx deleted file mode 100644 index 66773f009d1e..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -/* @refresh reload */ -import * as Sentry from '@sentry/solid'; -import { solidRouterBrowserTracingIntegration, withSentryRouterRouting } from '@sentry/solid/solidrouter'; -import { Router } from '@solidjs/router'; -import { render } from 'solid-js/web'; -import './index.css'; -import PageRoot from './pageroot'; -import { routes } from './routes'; - -Sentry.init({ - dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, - debug: true, - environment: 'qa', // dynamic sampling bias to keep transactions - integrations: [solidRouterBrowserTracingIntegration()], - release: 'e2e-test', - tunnel: 'http://localhost:3031/', // proxy server - tracesSampleRate: 1.0, -}); - -const SentryRouter = withSentryRouterRouting(Router); - -render(() => {routes}, document.getElementById('root')); diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pageroot.tsx b/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pageroot.tsx deleted file mode 100644 index 0919c0e362db..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pageroot.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { A } from '@solidjs/router'; - -export default function PageRoot(props) { - return ( - <> - -
{props.children}
- - ); -} diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pages/errorboundaryexample.tsx b/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pages/errorboundaryexample.tsx deleted file mode 100644 index b4cb4e93a02f..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pages/errorboundaryexample.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as Sentry from '@sentry/solid'; -import { ErrorBoundary } from 'solid-js'; - -const SentryErrorBoundary = Sentry.withSentryErrorBoundary(ErrorBoundary); - -export default function ErrorBoundaryExample() { - return ( - ( -
-

Error Boundary Fallback

-
- {error.message} -
- -
- )} - > - -
- ); -} diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pages/home.tsx b/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pages/home.tsx deleted file mode 100644 index 08e92728762c..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pages/home.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { A } from '@solidjs/router'; -import { createSignal } from 'solid-js'; - -export default function Home() { - const [count, setCount] = createSignal(0); - - return ( -
-

Home

-

This is the home page.

- -
- - - Count: {count()} - - -
-
- - - User 5 - -
-
- ); -} diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pages/user.tsx b/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pages/user.tsx deleted file mode 100644 index 639ab0be8118..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/pages/user.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { useParams } from '@solidjs/router'; - -export default function User() { - const params = useParams(); - return
User ID: {params.id}
; -} diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/routes.ts b/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/routes.ts deleted file mode 100644 index 96b78e113ef5..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/src/routes.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { lazy } from 'solid-js'; - -import ErrorBoundaryExample from './pages/errorboundaryexample'; -import Home from './pages/home'; - -export const routes = [ - { - path: '/', - component: Home, - }, - { - path: '/user/:id', - component: lazy(() => import('./pages/user')), - }, - { - path: '/error-boundary-example', - component: ErrorBoundaryExample, - }, - { - path: '**', - component: lazy(() => import('./errors/404')), - }, -]; diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tailwind.config.ts b/dev-packages/e2e-tests/test-applications/solid-solidrouter/tailwind.config.ts deleted file mode 100644 index f69a95185570..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tailwind.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { Config } from 'tailwindcss'; - -const config: Config = { - content: ['./src/**/*.{js,jsx,ts,tsx}'], - theme: { - extend: {}, - }, - plugins: [], -}; - -export default config; diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errorboundary.test.ts b/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errorboundary.test.ts deleted file mode 100644 index 14396feb2334..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errorboundary.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForError } from '@sentry-internal/test-utils'; - -test('captures an exception', async ({ page }) => { - const errorEventPromise = waitForError('solid', errorEvent => { - return !errorEvent.type && errorEvent.transaction === '/error-boundary-example'; - }); - - const [, errorEvent] = await Promise.all([page.goto('/error-boundary-example'), errorEventPromise]); - - expect(errorEvent).toMatchObject({ - exception: { - values: [ - { - type: 'ReferenceError', - value: 'NonExistentComponent is not defined', - mechanism: { - type: 'auto.function.solid.error_boundary', - handled: true, - }, - }, - ], - }, - transaction: '/error-boundary-example', - }); -}); - -test('captures a second exception after resetting the boundary', async ({ page }) => { - const firstErrorEventPromise = waitForError('solid', errorEvent => { - return !errorEvent.type && errorEvent.transaction === '/error-boundary-example'; - }); - - const [, firstErrorEvent] = await Promise.all([page.goto('/error-boundary-example'), firstErrorEventPromise]); - - expect(firstErrorEvent).toMatchObject({ - exception: { - values: [ - { - type: 'ReferenceError', - value: 'NonExistentComponent is not defined', - mechanism: { - type: 'auto.function.solid.error_boundary', - handled: true, - }, - }, - ], - }, - transaction: '/error-boundary-example', - }); - - const secondErrorEventPromise = waitForError('solid', errorEvent => { - return !errorEvent.type && errorEvent.transaction === '/error-boundary-example'; - }); - - const [, secondErrorEvent] = await Promise.all([ - page.locator('#errorBoundaryResetBtn').click(), - await secondErrorEventPromise, - ]); - - expect(secondErrorEvent).toMatchObject({ - exception: { - values: [ - { - type: 'ReferenceError', - value: 'NonExistentComponent is not defined', - mechanism: { - type: 'auto.function.solid.error_boundary', - handled: true, - }, - }, - ], - }, - transaction: '/error-boundary-example', - }); -}); diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errors.test.ts deleted file mode 100644 index a77f107af624..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/errors.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForError } from '@sentry-internal/test-utils'; - -test('sends an error', async ({ page }) => { - const errorPromise = waitForError('solid', async errorEvent => { - return !errorEvent.type && errorEvent.exception?.values?.[0]?.value === 'Error thrown from Solid E2E test app'; - }); - - await Promise.all([page.goto(`/`), page.locator('#errorBtn').click()]); - - const error = await errorPromise; - - expect(error).toMatchObject({ - exception: { - values: [ - { - type: 'Error', - value: 'Error thrown from Solid E2E test app', - mechanism: { - type: 'auto.browser.global_handlers.onerror', - handled: false, - }, - }, - ], - }, - transaction: '/', - }); -}); diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/performance.test.ts deleted file mode 100644 index f73ff4940527..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tests/performance.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('sends a pageload transaction', async ({ page }) => { - const transactionPromise = waitForTransaction('solid', async transactionEvent => { - return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; - }); - - const [, pageloadTransaction] = await Promise.all([page.goto('/'), transactionPromise]); - - expect(pageloadTransaction).toMatchObject({ - contexts: { - trace: { - op: 'pageload', - origin: 'auto.pageload.browser', - }, - }, - transaction: '/', - transaction_info: { - source: 'url', - }, - }); -}); - -test('sends a navigation transaction', async ({ page }) => { - const transactionPromise = waitForTransaction('solid', async transactionEvent => { - return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation'; - }); - - await page.goto(`/`); - - const [, navigationTransaction] = await Promise.all([page.locator('#navLink').click(), transactionPromise]); - - expect(navigationTransaction).toMatchObject({ - contexts: { - trace: { - op: 'navigation', - origin: 'auto.navigation.solid.solidrouter', - }, - }, - transaction: '/user/5', - transaction_info: { - source: 'url', - }, - }); -}); - -test('updates the transaction when using the back button', async ({ page }) => { - // Solid Router sends a `-1` navigation when using the back button. - // The sentry solidRouterBrowserTracingIntegration tries to update such - // transactions with the proper name once the `useLocation` hook triggers. - const navigationTxnPromise = waitForTransaction('solid', async transactionEvent => { - return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation'; - }); - - await page.goto(`/`); - - const [, navigationTxn] = await Promise.all([page.locator('#navLink').click(), navigationTxnPromise]); - - expect(navigationTxn).toMatchObject({ - contexts: { - trace: { - op: 'navigation', - origin: 'auto.navigation.solid.solidrouter', - }, - }, - transaction: '/user/5', - transaction_info: { - source: 'url', - }, - }); - - const backNavigationTxnPromise = waitForTransaction('solid', async transactionEvent => { - return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation'; - }); - - const [, backNavigationTxn] = await Promise.all([page.goBack(), backNavigationTxnPromise]); - - expect(backNavigationTxn).toMatchObject({ - contexts: { - trace: { - op: 'navigation', - origin: 'auto.navigation.solid.solidrouter', - }, - }, - transaction: '/', - transaction_info: { - source: 'url', - }, - }); -}); diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tsconfig.json b/dev-packages/e2e-tests/test-applications/solid-solidrouter/tsconfig.json deleted file mode 100644 index 5d2faf0af117..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "node", - "allowSyntheticDefaultImports": true, - "esModuleInterop": true, - "jsx": "preserve", - "jsxImportSource": "solid-js", - "types": ["vite/client"], - "noEmit": true, - "isolatedModules": true - } -} diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/vite.config.ts b/dev-packages/e2e-tests/test-applications/solid-solidrouter/vite.config.ts deleted file mode 100644 index d1835ee1b8ff..000000000000 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/vite.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineConfig } from 'vite'; -import solidPlugin from 'vite-plugin-solid'; - -export default defineConfig({ - plugins: [solidPlugin()], - build: { - target: 'esnext', - }, - envPrefix: 'PUBLIC_', -}); diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/.cta.json b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/.cta.json new file mode 100644 index 000000000000..3b9146f46e52 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/.cta.json @@ -0,0 +1,11 @@ +{ + "projectName": "solid-tanstack-router", + "mode": "code-router", + "typescript": true, + "tailwind": true, + "packageManager": "pnpm", + "git": true, + "version": 1, + "framework": "solid", + "chosenAddOns": [] +} diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/.gitignore b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/.gitignore similarity index 100% rename from dev-packages/e2e-tests/test-applications/solid-solidrouter/.gitignore rename to dev-packages/e2e-tests/test-applications/solid-tanstack-router/.gitignore diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/.npmrc b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/.npmrc similarity index 100% rename from dev-packages/e2e-tests/test-applications/solid-solidrouter/.npmrc rename to dev-packages/e2e-tests/test-applications/solid-tanstack-router/.npmrc diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/README.md b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/README.md new file mode 100644 index 000000000000..cde052fb5212 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/README.md @@ -0,0 +1,165 @@ +Welcome to your new TanStack app! + +# Getting Started + +To run this application: + +```bash +pnpm install +pnpm start +``` + +# Building For Production + +To build this application for production: + +```bash +pnpm build +``` + +## Styling + +This project uses [Tailwind CSS](https://tailwindcss.com/) for styling. + +## Routing + +This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a code based router. Which means that the routes are defined in code (in the `./src/main.tsx` file). If you like you can also use a file based routing setup by following the [File Based Routing](https://tanstack.com/router/latest/docs/framework/solid/guide/file-based-routing) guide. + +### Adding A Route + +To add a new route to your application just add another `createRoute` call to the `./src/main.tsx` file. The example below adds a new `/about`route to the root route. + +```tsx +const aboutRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/about', + component: () =>

About

, +}); +``` + +You will also need to add the route to the `routeTree` in the `./src/main.tsx` file. + +```tsx +const routeTree = rootRoute.addChildren([indexRoute, aboutRoute]); +``` + +With this set up you should be able to navigate to `/about` and see the about page. + +Of course you don't need to implement the About page in the `main.tsx` file. You can create that component in another file and import it into the `main.tsx` file, then use it in the `component` property of the `createRoute` call, like so: + +```tsx +import About from './components/About.tsx'; + +const aboutRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/about', + component: About, +}); +``` + +That is how we have the `App` component set up with the home page. + +For more information on the options you have when you are creating code based routes check out the [Code Based Routing](https://tanstack.com/router/latest/docs/framework/solid/guide/code-based-routing) documentation. + +Now that you have two routes you can use a `Link` component to navigate between them. + +### Adding Links + +To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/solid-router`. + +```tsx +import { Link } from '@tanstack/solid-router'; +``` + +Then anywhere in your JSX you can use it like so: + +```tsx +About +``` + +This will create a link that will navigate to the `/about` route. + +More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/solid/api/router/linkComponent). + +### Using A Layout + +Layouts can be used to wrap the contents of the routes in menus, headers, footers, etc. + +There is already a layout in the `src/main.tsx` file: + +```tsx +const rootRoute = createRootRoute({ + component: () => ( + <> + + + + ), +}); +``` + +You can use the Soliid component specified in the `component` property of the `rootRoute` to wrap the contents of the routes. The `` component is used to render the current route within the body of the layout. For example you could add a header to the layout like so: + +```tsx +import { Link } from '@tanstack/solid-router'; + +const rootRoute = createRootRoute({ + component: () => ( + <> +
+ +
+ + + + ), +}); +``` + +The `` component is not required so you can remove it if you don't want it in your layout. + +More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/solid/guide/routing-concepts#layouts). + +## Data Fetching + +There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered. + +For example: + +```tsx +const peopleRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/people', + loader: async () => { + const response = await fetch('https://swapi.dev/api/people'); + return response.json() as Promise<{ + results: { + name: string; + }[]; + }>; + }, + component: () => { + const data = peopleRoute.useLoaderData(); + return ( +
    + {data.results.map(person => ( +
  • {person.name}
  • + ))} +
+ ); + }, +}); +``` + +Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/solid/guide/data-loading#loader-parameters). + +# Demo files + +Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed. + +# Learn More + +You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com). diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/index.html b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/index.html new file mode 100644 index 000000000000..e1b9457f30b9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + + Create TanStack App - app-ts + + +
+ + + diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/package.json b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/package.json similarity index 52% rename from dev-packages/e2e-tests/test-applications/solid-solidrouter/package.json rename to dev-packages/e2e-tests/test-applications/solid-tanstack-router/package.json index ada4d06624ad..5dc35acaf095 100644 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/package.json +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/package.json @@ -1,32 +1,32 @@ { - "name": "solid-solidrouter", - "version": "0.0.0", - "description": "", + "name": "solid-tanstack-router", + "private": true, + "type": "module", "scripts": { "build": "vite build", "clean": "npx rimraf node_modules pnpm-lock.yaml dist", "dev": "vite", + "start": "vite preview", "preview": "vite preview", - "start": "vite", "test:prod": "TEST_ENV=production playwright test", "test:build": "pnpm install && pnpm build", "test:assert": "pnpm test:prod" }, - "license": "MIT", + "dependencies": { + "@sentry/solid": "latest || *", + "@tailwindcss/vite": "^4.0.6", + "@tanstack/solid-router": "^1.132.25", + "@tanstack/solid-router-devtools": "^1.132.25", + "@tanstack/solid-start": "^1.132.25", + "solid-js": "^1.9.5", + "tailwindcss": "^4.0.6" + }, "devDependencies": { "@playwright/test": "~1.53.2", "@sentry-internal/test-utils": "link:../../../test-utils", - "autoprefixer": "^10.4.17", - "postcss": "^8.4.33", - "solid-devtools": "^0.29.2", - "tailwindcss": "^3.4.1", - "vite": "^5.4.11", - "vite-plugin-solid": "^2.11.6" - }, - "dependencies": { - "@solidjs/router": "^0.13.5", - "solid-js": "^1.8.18", - "@sentry/solid": "latest || *" + "typescript": "^5.7.2", + "vite": "^7.1.7", + "vite-plugin-solid": "^2.11.2" }, "volta": { "extends": "../../package.json" diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/playwright.config.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/solid-solidrouter/playwright.config.mjs rename to dev-packages/e2e-tests/test-applications/solid-tanstack-router/playwright.config.mjs diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/favicon.ico b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/favicon.ico new file mode 100644 index 000000000000..a11777cc471a Binary files /dev/null and b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/favicon.ico differ diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/logo192.png b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/logo192.png new file mode 100644 index 000000000000..fc44b0a3796c Binary files /dev/null and b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/logo192.png differ diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/logo512.png b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/logo512.png new file mode 100644 index 000000000000..a4e47a6545bc Binary files /dev/null and b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/logo512.png differ diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/manifest.json b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/manifest.json new file mode 100644 index 000000000000..078ef5011624 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "TanStack App", + "name": "Create TanStack App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/robots.txt b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/robots.txt new file mode 100644 index 000000000000..e9e57dc4d41b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/App.tsx b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/App.tsx new file mode 100644 index 000000000000..a584b2d83600 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/App.tsx @@ -0,0 +1,20 @@ +import logo from './logo.svg'; + +function App() { + return ( +
+ logo +

+ Edit src/App.tsx and save to reload. +

+ + Learn Solid + + + Learn TanStack + +
+ ); +} + +export default App; diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/logo.svg b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/logo.svg new file mode 100644 index 000000000000..21159f9fc0eb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/logo.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/main.tsx b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/main.tsx new file mode 100644 index 000000000000..4580fa6e8a90 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/main.tsx @@ -0,0 +1,99 @@ +import { Link, Outlet, RouterProvider, createRootRoute, createRoute, createRouter } from '@tanstack/solid-router'; +import * as Sentry from '@sentry/solid'; +import { tanstackRouterBrowserTracingIntegration } from '@sentry/solid/tanstackrouter'; +import { render } from 'solid-js/web'; + +import './styles.css'; + +import App from './App.tsx'; + +const rootRoute = createRootRoute({ + component: () => ( + <> +
    +
  • + Home +
  • +
  • + + Post 1 + +
  • +
  • + + Post 2 + +
  • +
+
+ + + ), +}); + +const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: App, +}); + +const postsRoute = createRoute({ + getParentRoute: () => rootRoute, + path: 'posts/', +}); + +const postIdRoute = createRoute({ + getParentRoute: () => postsRoute, + path: '$postId', + shouldReload() { + return true; + }, + loader: ({ params }) => { + return Sentry.startSpan({ name: `loading-post-${params.postId}` }, async () => { + await new Promise(resolve => setTimeout(resolve, 1000)); + }); + }, + component: function Post() { + const params = postIdRoute.useParams(); + return
Post ID: {params().postId}
; + }, +}); + +const routeTree = rootRoute.addChildren([indexRoute, postsRoute.addChildren([postIdRoute])]); + +const router = createRouter({ + routeTree, + defaultPreload: 'intent', + scrollRestoration: true, +}); + +declare module '@tanstack/solid-router' { + interface Register { + router: typeof router; + } +} + +declare const __APP_DSN__: string; + +Sentry.init({ + dsn: __APP_DSN__, + debug: true, + environment: 'qa', // dynamic sampling bias to keep transactions + integrations: [tanstackRouterBrowserTracingIntegration(router)], + release: 'e2e-test', + tunnel: 'http://localhost:3031/', // proxy server + tracesSampleRate: 1.0, +}); + +function MainApp() { + return ( + <> + + + ); +} + +const rootElement = document.getElementById('app'); +if (rootElement) { + render(() => , rootElement); +} diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/styles.css b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/styles.css new file mode 100644 index 000000000000..9dbc2a933202 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/src/styles.css @@ -0,0 +1,14 @@ +@import 'tailwindcss'; + +body { + @apply m-0; + font-family: + -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; +} diff --git a/dev-packages/e2e-tests/test-applications/solid-solidrouter/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/start-event-proxy.mjs similarity index 71% rename from dev-packages/e2e-tests/test-applications/solid-solidrouter/start-event-proxy.mjs rename to dev-packages/e2e-tests/test-applications/solid-tanstack-router/start-event-proxy.mjs index 075d4dcb5cf5..496ea15d6c2a 100644 --- a/dev-packages/e2e-tests/test-applications/solid-solidrouter/start-event-proxy.mjs +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/start-event-proxy.mjs @@ -2,5 +2,5 @@ import { startEventProxyServer } from '@sentry-internal/test-utils'; startEventProxyServer({ port: 3031, - proxyServerName: 'solid', + proxyServerName: 'solid-tanstack-router', }); diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/tests/routing-instrumentation.test.ts b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/tests/routing-instrumentation.test.ts new file mode 100644 index 000000000000..7119c7e76b99 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/tests/routing-instrumentation.test.ts @@ -0,0 +1,72 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test('sends a pageload transaction with a parameterized URL', async ({ page }) => { + const transactionPromise = waitForTransaction('solid-tanstack-router', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + await page.goto(`/posts/456`); + + const rootSpan = await transactionPromise; + + expect(rootSpan).toMatchObject({ + contexts: { + trace: { + data: { + 'sentry.source': 'route', + 'sentry.origin': 'auto.pageload.solid.tanstack_router', + 'sentry.op': 'pageload', + 'url.path.parameter.postId': '456', + }, + op: 'pageload', + origin: 'auto.pageload.solid.tanstack_router', + }, + }, + transaction: '/posts/$postId', + transaction_info: { + source: 'route', + }, + }); +}); + +test('sends a navigation transaction with a parameterized URL', async ({ page }) => { + const pageloadTxnPromise = waitForTransaction('solid-tanstack-router', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + const navigationTxnPromise = waitForTransaction('solid-tanstack-router', async transactionEvent => { + return ( + !!transactionEvent?.transaction && + transactionEvent.transaction === '/posts/$postId' && + transactionEvent.contexts?.trace?.op === 'navigation' + ); + }); + + await page.goto(`/`); + await pageloadTxnPromise; + + await page.waitForTimeout(5000); + await page.locator('#nav-link').click(); + + const navigationTxn = await navigationTxnPromise; + + expect(navigationTxn).toMatchObject({ + contexts: { + trace: { + data: { + 'sentry.source': 'route', + 'sentry.origin': 'auto.navigation.solid.tanstack_router', + 'sentry.op': 'navigation', + 'url.path.parameter.postId': '2', + }, + op: 'navigation', + origin: 'auto.navigation.solid.tanstack_router', + }, + }, + transaction: '/posts/$postId', + transaction_info: { + source: 'route', + }, + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/tsconfig.json b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/tsconfig.json new file mode 100644 index 000000000000..0ce9a7b137af --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/tsconfig.json @@ -0,0 +1,25 @@ +{ + "include": ["**/*.ts", "**/*.tsx"], + "compilerOptions": { + "target": "ES2022", + "jsx": "preserve", + "jsxImportSource": "solid-js", + "module": "ESNext", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + /* Linting */ + "skipLibCheck": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + } +} diff --git a/dev-packages/e2e-tests/test-applications/solid-tanstack-router/vite.config.ts b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/vite.config.ts new file mode 100644 index 000000000000..bd612e95fbb8 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/solid-tanstack-router/vite.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vite'; + +import solidPlugin from 'vite-plugin-solid'; +import tailwindcss from '@tailwindcss/vite'; + +// https://vitejs.dev/config/ +export default defineConfig({ + define: { + __APP_DSN__: JSON.stringify(process.env.E2E_TEST_DSN), + }, + plugins: [solidPlugin(), tailwindcss()], +}); diff --git a/packages/solid/README.md b/packages/solid/README.md index e5ddd2186c02..58fa5c75c345 100644 --- a/packages/solid/README.md +++ b/packages/solid/README.md @@ -52,6 +52,43 @@ render( ); ``` +### Tanstack Router + +The Tanstack Router instrumentation uses the Tanstack Router library to create navigation spans to ensure you collect +meaningful performance data about the health of your page loads and associated requests. + +Add `tanstackRouterBrowserTracingIntegration` instead of the regular `Sentry.browserTracingIntegration`. + +Make sure `tanstackRouterBrowserTracingIntegration` is initialized by your `Sentry.init` call. Otherwise, the routing +instrumentation will not work properly. + +Pass your router instance from `createRouter` to the integration. + +```js +import * as Sentry from '@sentry/solid'; +import { tanstackRouterBrowserTracingIntegration } from '@sentry/solid/tanstackrouter'; +import { Route, Router } from '@solidjs/router'; + +const router = createRouter({ + // your router config + // ... +}); + +declare module '@tanstack/solid-router' { + interface Register { + router: typeof router; + } +} + +Sentry.init({ + dsn: '__PUBLIC_DSN__', + integrations: [tanstackRouterBrowserTracingIntegration(router)], + tracesSampleRate: 1.0, // Capture 100% of the transactions +}); + +render(() => , document.getElementById('root')); +``` + # Solid ErrorBoundary To automatically capture exceptions from inside a component tree and render a fallback component, wrap the native Solid diff --git a/packages/solid/package.json b/packages/solid/package.json index 8a614ca120a2..5fdde9a0c97a 100644 --- a/packages/solid/package.json +++ b/packages/solid/package.json @@ -38,6 +38,16 @@ "types": "./solidrouter.d.ts", "default": "./build/cjs/solidrouter.js" } + }, + "./tanstackrouter": { + "import": { + "types": "./tanstackrouter.d.ts", + "default": "./build/esm/tanstackrouter.js" + }, + "require": { + "types": "./tanstackrouter.d.ts", + "default": "./build/cjs/tanstackrouter.js" + } } }, "publishConfig": { @@ -49,15 +59,20 @@ }, "peerDependencies": { "@solidjs/router": "^0.13.4", + "@tanstack/solid-router": "^1.132.27", "solid-js": "^1.8.4" }, "peerDependenciesMeta": { "@solidjs/router": { "optional": true + }, + "@tanstack/solid-router": { + "optional": true } }, "devDependencies": { "@solidjs/router": "^0.13.4", + "@tanstack/solid-router": "^1.132.27", "@solidjs/testing-library": "0.8.5", "@testing-library/dom": "^7.21.4", "@testing-library/jest-dom": "^6.4.5", @@ -70,15 +85,15 @@ "build": "run-p build:transpile build:types", "build:dev": "yarn build", "build:transpile": "rollup -c rollup.npm.config.mjs", - "build:types": "run-s build:types:core build:types:solidrouter", + "build:types": "run-s build:types:core build:types:routers", "build:types:core": "tsc -p tsconfig.types.json", - "build:types:solidrouter": "tsc -p tsconfig.solidrouter-types.json", + "build:types:routers": "tsc -p tsconfig.routers-types.json", "build:watch": "run-p build:transpile:watch build:types:watch", "build:dev:watch": "yarn build:watch", "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:tarball": "npm pack", - "circularDepCheck": "madge --circular src/index.ts && madge --circular src/solidrouter.ts", + "circularDepCheck": "madge --circular src/index.ts && madge --circular src/solidrouter.ts && madge --circular src/tanstackrouter.ts", "clean": "rimraf build coverage sentry-solid-*.tgz ./*.d.ts ./*.d.ts.map", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", diff --git a/packages/solid/rollup.npm.config.mjs b/packages/solid/rollup.npm.config.mjs index b044fda38c75..4da78623cb50 100644 --- a/packages/solid/rollup.npm.config.mjs +++ b/packages/solid/rollup.npm.config.mjs @@ -2,6 +2,6 @@ import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollu export default makeNPMConfigVariants( makeBaseNPMConfig({ - entrypoints: ['src/index.ts', 'src/solidrouter.ts'], + entrypoints: ['src/index.ts', 'src/solidrouter.ts', 'src/tanstackrouter.ts'], }), ); diff --git a/packages/solid/src/tanstackrouter.ts b/packages/solid/src/tanstackrouter.ts new file mode 100644 index 000000000000..09790530e822 --- /dev/null +++ b/packages/solid/src/tanstackrouter.ts @@ -0,0 +1,126 @@ +import { + browserTracingIntegration as originalBrowserTracingIntegration, + startBrowserTracingNavigationSpan, + startBrowserTracingPageLoadSpan, + WINDOW, +} from '@sentry/browser'; +import type { Integration } from '@sentry/core'; +import { + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, +} from '@sentry/core'; +import type { AnyRouter } from '@tanstack/solid-router'; + +type RouteMatch = ReturnType[number]; + +/** + * A custom browser tracing integration for TanStack Router. + * + * The minimum compatible version of `@tanstack/solid-router` is `1.64.0 + * + * @param router A TanStack Router `Router` instance that should be used for routing instrumentation. + * @param options Sentry browser tracing configuration. + */ +export function tanstackRouterBrowserTracingIntegration( + router: R, + options: Parameters[0] = {}, +): Integration { + const browserTracingIntegrationInstance = originalBrowserTracingIntegration({ + ...options, + instrumentNavigation: false, + instrumentPageLoad: false, + }); + + const { instrumentPageLoad = true, instrumentNavigation = true } = options; + + return { + ...browserTracingIntegrationInstance, + afterAllSetup(client) { + browserTracingIntegrationInstance.afterAllSetup(client); + + const initialWindowLocation = WINDOW.location; + if (instrumentPageLoad && initialWindowLocation) { + const matchedRoutes = router.matchRoutes( + initialWindowLocation.pathname, + router.options.parseSearch(initialWindowLocation.search), + { preload: false, throwOnError: false }, + ); + + const lastMatch = matchedRoutes[matchedRoutes.length - 1]; + + startBrowserTracingPageLoadSpan(client, { + name: lastMatch ? lastMatch.routeId : initialWindowLocation.pathname, + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'pageload', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.pageload.solid.tanstack_router', + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: lastMatch ? 'route' : 'url', + ...routeMatchToParamSpanAttributes(lastMatch), + }, + }); + } + + if (instrumentNavigation) { + // The onBeforeNavigate hook is called at the very beginning of a navigation and is only called once per navigation, even when the user is redirected + router.subscribe('onBeforeNavigate', onBeforeNavigateArgs => { + // onBeforeNavigate is called during pageloads. We can avoid creating navigation spans by comparing the states of the to and from arguments. + if (onBeforeNavigateArgs.toLocation.state === onBeforeNavigateArgs.fromLocation?.state) { + return; + } + + const onResolvedMatchedRoutes = router.matchRoutes( + onBeforeNavigateArgs.toLocation.pathname, + onBeforeNavigateArgs.toLocation.search, + { preload: false, throwOnError: false }, + ); + + const onBeforeNavigateLastMatch = onResolvedMatchedRoutes[onResolvedMatchedRoutes.length - 1]; + + const navigationLocation = WINDOW.location; + const navigationSpan = startBrowserTracingNavigationSpan(client, { + name: onBeforeNavigateLastMatch ? onBeforeNavigateLastMatch.routeId : navigationLocation.pathname, + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.solid.tanstack_router', + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: onBeforeNavigateLastMatch ? 'route' : 'url', + }, + }); + + // In case the user is redirected during navigation we want to update the span with the right value. + const unsubscribeOnResolved = router.subscribe('onResolved', onResolvedArgs => { + unsubscribeOnResolved(); + if (navigationSpan) { + const onResolvedMatchedRoutes = router.matchRoutes( + onResolvedArgs.toLocation.pathname, + onResolvedArgs.toLocation.search, + { preload: false, throwOnError: false }, + ); + + const onResolvedLastMatch = onResolvedMatchedRoutes[onResolvedMatchedRoutes.length - 1]; + + if (onResolvedLastMatch) { + navigationSpan.updateName(onResolvedLastMatch.routeId); + navigationSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); + navigationSpan.setAttributes(routeMatchToParamSpanAttributes(onResolvedLastMatch)); + } + } + }); + }); + } + }, + }; +} + +function routeMatchToParamSpanAttributes(match: RouteMatch | undefined): Record { + if (!match) { + return {}; + } + + const paramAttributes: Record = {}; + Object.entries(match.params as Record).forEach(([key, value]) => { + paramAttributes[`url.path.parameter.${key}`] = value; + paramAttributes[`params.${key}`] = value; // params.[key] is an alias + }); + + return paramAttributes; +} diff --git a/packages/solid/tsconfig.solidrouter-types.json b/packages/solid/tsconfig.routers-types.json similarity index 85% rename from packages/solid/tsconfig.solidrouter-types.json rename to packages/solid/tsconfig.routers-types.json index 055ad82a187a..e173ebc0eb87 100644 --- a/packages/solid/tsconfig.solidrouter-types.json +++ b/packages/solid/tsconfig.routers-types.json @@ -9,7 +9,7 @@ }, "//": "This type is built separately because it is for a subpath export, which has problems if it is not in the root", - "include": ["src/solidrouter.ts"], + "include": ["src/solidrouter.ts", "src/tanstackrouter.ts"], "//": "Without this, we cannot output into the root dir", "exclude": [] } diff --git a/packages/solid/tsconfig.types.json b/packages/solid/tsconfig.types.json index fa96a3ccc08b..510f8c4fae3f 100644 --- a/packages/solid/tsconfig.types.json +++ b/packages/solid/tsconfig.types.json @@ -8,6 +8,6 @@ "outDir": "build/types" }, - "//": "This is built separately in tsconfig.solidrouter-types.json", - "exclude": ["src/solidrouter.ts"] + "//": "This is built separately in tsconfig.routers-types.json", + "exclude": ["src/solidrouter.ts", "src/tanstackrouter.ts"] } diff --git a/yarn.lock b/yarn.lock index 5e9edc00f1df..70c9e3d80b73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5217,6 +5217,11 @@ "@nodelib/fs.scandir" "2.1.4" fastq "^1.6.0" +"@nothing-but/utils@~0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@nothing-but/utils/-/utils-0.17.0.tgz#eab601990c71ef29053ffc484909f2d1f26d88d8" + integrity sha512-TuCHcHLOqDL0SnaAxACfuRHBNRgNJcNn9X0GiH5H3YSDBVquCr3qEIG3FOQAuMyZCbu9w8nk2CHhOsn7IvhIwQ== + "@npmcli/fs@^2.1.0": version "2.1.2" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" @@ -7630,6 +7635,136 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== +"@solid-devtools/debugger@^0.28.1": + version "0.28.1" + resolved "https://registry.yarnpkg.com/@solid-devtools/debugger/-/debugger-0.28.1.tgz#5c2e9d533ef65ac9debb4b1c3a625c6494f811c6" + integrity sha512-6qIUI6VYkXoRnL8oF5bvh2KgH71qlJ18hNw/mwSyY6v48eb80ZR48/5PDXufUa3q+MBSuYa1uqTMwLewpay9eg== + dependencies: + "@nothing-but/utils" "~0.17.0" + "@solid-devtools/shared" "^0.20.0" + "@solid-primitives/bounds" "^0.1.1" + "@solid-primitives/event-listener" "^2.4.1" + "@solid-primitives/keyboard" "^1.3.1" + "@solid-primitives/rootless" "^1.5.1" + "@solid-primitives/scheduled" "^1.5.1" + "@solid-primitives/static-store" "^0.1.1" + "@solid-primitives/utils" "^6.3.1" + +"@solid-devtools/logger@^0.9.4": + version "0.9.11" + resolved "https://registry.yarnpkg.com/@solid-devtools/logger/-/logger-0.9.11.tgz#a94d8ec640df8887eca7a0aaf8b2788adb244228" + integrity sha512-THbiY1iQlieL6vdgJc4FIsLe7V8a57hod/Thm8zdKrTkWL88UPZjkBBfM+mVNGusd4OCnAN20tIFBhNnuT1Dew== + dependencies: + "@nothing-but/utils" "~0.17.0" + "@solid-devtools/debugger" "^0.28.1" + "@solid-devtools/shared" "^0.20.0" + "@solid-primitives/utils" "^6.3.1" + +"@solid-devtools/shared@^0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@solid-devtools/shared/-/shared-0.20.0.tgz#1dff1573ee3bc43acd6d2dc3a2ed3765b20dcd3c" + integrity sha512-o5TACmUOQsxpzpOKCjbQqGk8wL8PMi+frXG9WNu4Lh3PQVUB6hs95Kl/S8xc++zwcMguUKZJn8h5URUiMOca6Q== + dependencies: + "@nothing-but/utils" "~0.17.0" + "@solid-primitives/event-listener" "^2.4.1" + "@solid-primitives/media" "^2.3.1" + "@solid-primitives/refs" "^1.1.1" + "@solid-primitives/rootless" "^1.5.1" + "@solid-primitives/scheduled" "^1.5.1" + "@solid-primitives/static-store" "^0.1.1" + "@solid-primitives/styles" "^0.1.1" + "@solid-primitives/utils" "^6.3.1" + +"@solid-primitives/bounds@^0.1.1": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@solid-primitives/bounds/-/bounds-0.1.3.tgz#6c7cca6fb969281a1ee103efc982cb190358f81c" + integrity sha512-UbiyKMdSPmtijcEDnYLQL3zzaejpwWDAJJ4Gt5P0hgVs6A72piov0GyNw7V2SroH7NZFwxlYS22YmOr8A5xc1Q== + dependencies: + "@solid-primitives/event-listener" "^2.4.3" + "@solid-primitives/resize-observer" "^2.1.3" + "@solid-primitives/static-store" "^0.1.2" + "@solid-primitives/utils" "^6.3.2" + +"@solid-primitives/event-listener@^2.4.1", "@solid-primitives/event-listener@^2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@solid-primitives/event-listener/-/event-listener-2.4.3.tgz#e09380222e38ed1b27f3d93bc72e85ba8507b3c0" + integrity sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg== + dependencies: + "@solid-primitives/utils" "^6.3.2" + +"@solid-primitives/keyboard@^1.3.1": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@solid-primitives/keyboard/-/keyboard-1.3.3.tgz#d51ab3c66308c2551d47452ff3dbdbbc0f25c546" + integrity sha512-9dQHTTgLBqyAI7aavtO+HnpTVJgWQA1ghBSrmLtMu1SMxLPDuLfuNr+Tk5udb4AL4Ojg7h9JrKOGEEDqsJXWJA== + dependencies: + "@solid-primitives/event-listener" "^2.4.3" + "@solid-primitives/rootless" "^1.5.2" + "@solid-primitives/utils" "^6.3.2" + +"@solid-primitives/media@^2.3.1": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@solid-primitives/media/-/media-2.3.3.tgz#74d669b6814c30a8308a468cfd7412133ea7d16e" + integrity sha512-hQ4hLOGvfbugQi5Eu1BFWAIJGIAzztq9x0h02xgBGl2l0Jaa3h7tg6bz5tV1NSuNYVGio4rPoa7zVQQLkkx9dA== + dependencies: + "@solid-primitives/event-listener" "^2.4.3" + "@solid-primitives/rootless" "^1.5.2" + "@solid-primitives/static-store" "^0.1.2" + "@solid-primitives/utils" "^6.3.2" + +"@solid-primitives/refs@^1.0.8", "@solid-primitives/refs@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@solid-primitives/refs/-/refs-1.1.2.tgz#1a37a825754bc8fe7f8845fc0c7664683646288e" + integrity sha512-K7tf2thy7L+YJjdqXspXOg5xvNEOH8tgEWsp0+1mQk3obHBRD6hEjYZk7p7FlJphSZImS35je3UfmWuD7MhDfg== + dependencies: + "@solid-primitives/utils" "^6.3.2" + +"@solid-primitives/resize-observer@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@solid-primitives/resize-observer/-/resize-observer-2.1.3.tgz#459db96f9c4a3d98a194d940c6e69f3ad4b2dad8" + integrity sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ== + dependencies: + "@solid-primitives/event-listener" "^2.4.3" + "@solid-primitives/rootless" "^1.5.2" + "@solid-primitives/static-store" "^0.1.2" + "@solid-primitives/utils" "^6.3.2" + +"@solid-primitives/rootless@^1.5.1", "@solid-primitives/rootless@^1.5.2": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@solid-primitives/rootless/-/rootless-1.5.2.tgz#0a9243a977672169cb8ed43bf4eba0c4d8eb5ac5" + integrity sha512-9HULb0QAzL2r47CCad0M+NKFtQ+LrGGNHZfteX/ThdGvKIg2o2GYhBooZubTCd/RTu2l2+Nw4s+dEfiDGvdrrQ== + dependencies: + "@solid-primitives/utils" "^6.3.2" + +"@solid-primitives/scheduled@^1.5.1": + version "1.5.2" + resolved "https://registry.yarnpkg.com/@solid-primitives/scheduled/-/scheduled-1.5.2.tgz#616def57b9250bc0e4415c604b31ab5a71465632" + integrity sha512-/j2igE0xyNaHhj6kMfcUQn5rAVSTLbAX+CDEBm25hSNBmNiHLu2lM7Usj2kJJ5j36D67bE8wR1hBNA8hjtvsQA== + +"@solid-primitives/static-store@^0.1.1", "@solid-primitives/static-store@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@solid-primitives/static-store/-/static-store-0.1.2.tgz#acdbecee75f17a5b64416859082fca67eefbaaaa" + integrity sha512-ReK+5O38lJ7fT+L6mUFvUr6igFwHBESZF+2Ug842s7fvlVeBdIVEdTCErygff6w7uR6+jrr7J8jQo+cYrEq4Iw== + dependencies: + "@solid-primitives/utils" "^6.3.2" + +"@solid-primitives/styles@^0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@solid-primitives/styles/-/styles-0.1.2.tgz#282fbf8d37add03873fd67b692b2e03ad412581e" + integrity sha512-7iX5K+J5b1PRrbgw3Ki92uvU2LgQ0Kd/QMsrAZxDg5dpUBwMyTijZkA3bbs1ikZsT1oQhS41bTyKbjrXeU0Awg== + dependencies: + "@solid-primitives/rootless" "^1.5.2" + "@solid-primitives/utils" "^6.3.2" + +"@solid-primitives/utils@^6.3.1", "@solid-primitives/utils@^6.3.2": + version "6.3.2" + resolved "https://registry.yarnpkg.com/@solid-primitives/utils/-/utils-6.3.2.tgz#13d6126fc5a498965d7c45dd41c052e42dcfd7e1" + integrity sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ== + +"@solidjs/meta@^0.29.4": + version "0.29.4" + resolved "https://registry.yarnpkg.com/@solidjs/meta/-/meta-0.29.4.tgz#28a444db5200d1c9e4e62d8762ea808d3e8beffd" + integrity sha512-zdIWBGpR9zGx1p1bzIPqF5Gs+Ks/BH8R6fWhmUa/dcK1L2rUC8BAcZJzNRYBQv74kScf1TSOs0EY//Vd/I0V8g== + "@solidjs/router@^0.13.4": version "0.13.6" resolved "https://registry.yarnpkg.com/@solidjs/router/-/router-0.13.6.tgz#210ca2761d4bf294f06ac0f9e25c16fafdabefac" @@ -7793,6 +7928,56 @@ dependencies: defer-to-connect "^1.0.1" +"@tanstack/history@1.132.21": + version "1.132.21" + resolved "https://registry.yarnpkg.com/@tanstack/history/-/history-1.132.21.tgz#09ae649b0c0c2d1093f0b1e34b9ab0cd3b2b1d2f" + integrity sha512-5ziPz3YarKU5cBJoEJ4muV8cy+5W4oWdJMqW7qosMrK5fb9Qfm+QWX+kO3emKJMu4YOUofVu3toEuuD3x1zXKw== + +"@tanstack/router-core@1.132.27": + version "1.132.27" + resolved "https://registry.yarnpkg.com/@tanstack/router-core/-/router-core-1.132.27.tgz#8869e98d10ea42338cb115af45bdcbc10eaf2b7f" + integrity sha512-mNx+nba7mXc7sJdX+kYH4rSW8f7Jx/+0hPOkX4XAnqiq7I1ng3gGqmGuf4+2BYTG2aD+aTSPExUPczy9VNgRfQ== + dependencies: + "@tanstack/history" "1.132.21" + "@tanstack/store" "^0.7.0" + cookie-es "^2.0.0" + seroval "^1.3.2" + seroval-plugins "^1.3.2" + tiny-invariant "^1.3.3" + tiny-warning "^1.0.3" + +"@tanstack/solid-router@^1.132.27": + version "1.132.27" + resolved "https://registry.yarnpkg.com/@tanstack/solid-router/-/solid-router-1.132.27.tgz#cafa331a8190fb6775f3cd3b88f31adce82e8cc8" + integrity sha512-d1JfRvl53wJpoOsqStSX5ATCWegSWo7ygrwT+uRvXIebG3fsriGHWkL0u39U515fIYX9Br3PU2iKNk5eShCgtA== + dependencies: + "@solid-devtools/logger" "^0.9.4" + "@solid-primitives/refs" "^1.0.8" + "@solidjs/meta" "^0.29.4" + "@tanstack/history" "1.132.21" + "@tanstack/router-core" "1.132.27" + "@tanstack/solid-store" "0.7.0" + isbot "^5.1.22" + tiny-invariant "^1.3.3" + tiny-warning "^1.0.3" + +"@tanstack/solid-store@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@tanstack/solid-store/-/solid-store-0.7.0.tgz#4fd8172bb8ba6f8438ddaa5e1ed1fc8afa14a5a1" + integrity sha512-uDQYkUuH3MppitiduZLTEcItkTr8vEJ33jzp2rH2VvlNRMGbuU54GQcqf3dLIlTbZ1/Z2TtIBtBjjl+N/OhwRg== + dependencies: + "@tanstack/store" "0.7.0" + +"@tanstack/store@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@tanstack/store/-/store-0.7.0.tgz#afef29b06c6b592e93181cee9baa62fe77454459" + integrity sha512-CNIhdoUsmD2NolYuaIs8VfWM467RK6oIBAW4nPEKZhg1smZ+/CwtCdpURgp7nxSqOaV9oKkzdWD80+bC66F/Jg== + +"@tanstack/store@^0.7.0": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@tanstack/store/-/store-0.7.7.tgz#2c8b1d8c094f3614ae4e0483253239abd0e14488" + integrity sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ== + "@testing-library/dom@^7.21.4": version "7.31.2" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a" @@ -19847,6 +20032,11 @@ isbinaryfile@^5.0.0: resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-5.0.0.tgz#034b7e54989dab8986598cbcea41f66663c65234" integrity sha512-UDdnyGvMajJUWCkib7Cei/dvyJrrvo4FIrsvSFWdPpXSUorzXrDJ0S+X5Q4ZlasfPjca4yqCNNsjbCeiy8FFeg== +isbot@^5.1.22: + version "5.1.31" + resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.31.tgz#ecbab171da577002c66f9123fe180c1e795e4e4e" + integrity sha512-DPgQshehErHAqSCKDb3rNW03pa2wS/v5evvUqtxt6TTnHRqAG8FdzcSSJs9656pK6Y+NT7K9R4acEYXLHYfpUQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -27534,12 +27724,12 @@ serialize-javascript@^6.0.0, serialize-javascript@^6.0.1, serialize-javascript@^ dependencies: randombytes "^2.1.0" -seroval-plugins@^1.0.2, seroval-plugins@~1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/seroval-plugins/-/seroval-plugins-1.3.2.tgz#4200b538d699853c9bf5c3b7155c498c7c263a6a" - integrity sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ== +seroval-plugins@^1.0.2, seroval-plugins@^1.3.2, seroval-plugins@~1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/seroval-plugins/-/seroval-plugins-1.3.3.tgz#51bcacf09e5384080d7ea4002b08fd9f6166daf5" + integrity sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w== -seroval@^1.0.2, seroval@~1.3.0: +seroval@^1.0.2, seroval@^1.3.2, seroval@~1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/seroval/-/seroval-1.3.2.tgz#7e5be0dc1a3de020800ef013ceae3a313f20eca7" integrity sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ== @@ -29359,7 +29549,7 @@ tiny-lr@^2.0.0: object-assign "^4.1.0" qs "^6.4.0" -tiny-warning@^1.0.0: +tiny-warning@^1.0.0, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==