From a59f1cae5ed59d2f61ff879e991ca20146dfe82b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 11:40:05 +0000 Subject: [PATCH 01/54] Add Nextra documentation site and slim down README - Create Nextra 4 docs site with full documentation at docs/ - Migrate all API reference, guides, and troubleshooting content - Add Next.js section with overview, App Router, and Pages Router pages - Add React Native section with navigation and app state tracking - Slim down README to quick start essentials with links to react-fathom.com - Preserve Fathom referral link and all feature documentation --- README.md | 1242 ++-------------------- docs/app/[[...mdxPath]]/page.tsx | 26 + docs/app/layout.tsx | 43 + docs/content/_meta.ts | 11 + docs/content/api/_meta.ts | 6 + docs/content/api/components.mdx | 171 +++ docs/content/api/hooks.mdx | 239 +++++ docs/content/api/native.mdx | 180 ++++ docs/content/api/providers.mdx | 189 ++++ docs/content/contributing.mdx | 180 ++++ docs/content/getting-started.mdx | 101 ++ docs/content/guides/_meta.ts | 6 + docs/content/guides/custom-client.mdx | 182 ++++ docs/content/guides/custom-domains.mdx | 133 +++ docs/content/guides/default-options.mdx | 137 +++ docs/content/guides/testing.mdx | 227 ++++ docs/content/index.mdx | 77 ++ docs/content/nextjs/_meta.ts | 5 + docs/content/nextjs/app-router.mdx | 217 ++++ docs/content/nextjs/index.mdx | 63 ++ docs/content/nextjs/pages-router.mdx | 203 ++++ docs/content/react-native/_meta.ts | 6 + docs/content/react-native/advanced.mdx | 172 +++ docs/content/react-native/app-state.mdx | 114 ++ docs/content/react-native/index.mdx | 95 ++ docs/content/react-native/navigation.mdx | 161 +++ docs/content/react.mdx | 217 ++++ docs/content/troubleshooting.mdx | 225 ++++ docs/mdx-components.tsx | 10 + docs/next.config.mjs | 10 + docs/package.json | 21 + 31 files changed, 3523 insertions(+), 1146 deletions(-) create mode 100644 docs/app/[[...mdxPath]]/page.tsx create mode 100644 docs/app/layout.tsx create mode 100644 docs/content/_meta.ts create mode 100644 docs/content/api/_meta.ts create mode 100644 docs/content/api/components.mdx create mode 100644 docs/content/api/hooks.mdx create mode 100644 docs/content/api/native.mdx create mode 100644 docs/content/api/providers.mdx create mode 100644 docs/content/contributing.mdx create mode 100644 docs/content/getting-started.mdx create mode 100644 docs/content/guides/_meta.ts create mode 100644 docs/content/guides/custom-client.mdx create mode 100644 docs/content/guides/custom-domains.mdx create mode 100644 docs/content/guides/default-options.mdx create mode 100644 docs/content/guides/testing.mdx create mode 100644 docs/content/index.mdx create mode 100644 docs/content/nextjs/_meta.ts create mode 100644 docs/content/nextjs/app-router.mdx create mode 100644 docs/content/nextjs/index.mdx create mode 100644 docs/content/nextjs/pages-router.mdx create mode 100644 docs/content/react-native/_meta.ts create mode 100644 docs/content/react-native/advanced.mdx create mode 100644 docs/content/react-native/app-state.mdx create mode 100644 docs/content/react-native/index.mdx create mode 100644 docs/content/react-native/navigation.mdx create mode 100644 docs/content/react.mdx create mode 100644 docs/content/troubleshooting.mdx create mode 100644 docs/mdx-components.tsx create mode 100644 docs/next.config.mjs create mode 100644 docs/package.json diff --git a/README.md b/README.md index 848f6b9..9704a17 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,12 @@ # react-fathom -[![npm](https://img.shields.io/npm/v/react-fathom?style=flat-square)](https://www.pkgstats.com/pkg:react-fathom) -[![NPM](https://img.shields.io/npm/l/react-fathom?style=flat-square)](LICENSE) -[![npm](https://img.shields.io/npm/dt/react-fathom?style=flat-square)](https://www.pkgstats.com/pkg:react-fathom) +[![npm](https://img.shields.io/npm/v/react-fathom?style=flat-square)](https://www.npmjs.com/package/react-fathom) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-fathom?style=flat-square)](https://bundlephobia.com/package/react-fathom) -[![GitHub stars](https://img.shields.io/github/stars/ryanhefner/react-fathom?style=flat-square)](https://github.com/ryanhefner/react-fathom/stargazers) [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org/) -[![codecov](https://codecov.io/gh/ryanhefner/react-fathom/branch/main/graph/badge.svg)](https://codecov.io/gh/ryanhefner/react-fathom) -**Privacy-focused analytics for React, Next.js, and React Native.** Easily integrate [Fathom Analytics](https://usefathom.com/ref/EKONBS) into your applications with automatic pageview tracking, custom event tracking, and full TypeScript support. +**Privacy-focused analytics for React, Next.js, and React Native.** -## Table of Contents - -- [Quick Start](#quick-start) -- [Why react-fathom?](#why-react-fathom) -- [Features](#features) -- [Installation](#install) -- [Usage](#usage) - - [Basic React Setup](#basic-react-setup) - - [Next.js App Router](#nextjs-app-router) - - [Next.js Pages Router](#nextjs-pages-router) - - [React Native](#react-native) -- [API Reference](#api) -- [Troubleshooting](#troubleshooting) -- [Contributing](#contributing) -- [License](#license) +πŸ“– **[Full Documentation](https://react-fathom.com)** Β· [API Reference](https://react-fathom.com/api/providers) Β· [Troubleshooting](https://react-fathom.com/troubleshooting) ## Quick Start @@ -51,188 +33,39 @@ import { useFathom } from 'react-fathom' function MyComponent() { const { trackEvent } = useFathom() - - return ( - - ) + return } ``` -That's it! Pageviews are tracked automatically. +Pageviews are tracked automatically. ## Why react-fathom? -### Privacy-First Analytics +[Fathom Analytics](https://usefathom.com/ref/EKONBS) is a privacy-focused alternative to Google Analyticsβ€”no cookies, no consent banners, GDPR compliant by default. -[Fathom Analytics](https://usefathom.com/ref/EKONBS) is a privacy-focused alternative to Google Analytics. Unlike traditional analytics platforms: +The official `fathom-client` works, but: -- **No cookies required** - GDPR, CCPA, and PECR compliant out of the box -- **No personal data collection** - Respects user privacy by design -- **No consent banners needed** - Simplified compliance for your websites -- **Fast and lightweight** - Won't slow down your site +| Problem | react-fathom solution | +|---------|----------------------| +| Web onlyβ€”no React Native | **Full React Native support** with offline event queuing | +| Next.js App Router requires boilerplate | **`NextFathomProviderApp`** works directly in Server Component layouts | +| Imperative API only | **Hooks** (`useFathom`, `useTrackOnMount`, `useTrackOnVisible`) and **declarative components** (``, ``) | +| No tree-shaking | **Fully tree-shakeable**β€”bundle only what you use | -### Why Use This Package? - -- **React-native integration** - Works seamlessly with React's component model and hooks -- **Automatic tracking** - Pageviews tracked automatically on route changes -- **Next.js optimized** - First-class support for both App Router and Pages Router -- **React Native support** - Full mobile support with offline queuing -- **TypeScript-first** - Complete type definitions for a great developer experience -- **Tree-shakeable** - Only bundle what you use - -**New to Fathom?** Get a **$10 credit** when you sign up using [this referral link](https://usefathom.com/ref/EKONBS). +**New to Fathom?** Get a **$10 credit** with [this referral link](https://usefathom.com/ref/EKONBS). ## Features -- πŸš€ **Zero-config** Fathom Analytics integration for React -- πŸ“¦ **Tree-shakeable** - Only bundle what you use -- πŸ”„ **Automatic pageview tracking** for Next.js (Pages Router & App Router) -- πŸ“± **React Native support** with offline queuing and navigation tracking -- πŸ’ͺ **Full TypeScript** support with type definitions -- 🎯 **Flexible** - Works with any React app, Next.js, or React Native -- ⚑ **Lightweight** - Minimal bundle size impact - -## Install - -Via [npm](https://npmjs.com/package/react-fathom) - -```sh -npm install react-fathom fathom-client -``` - -Via [Yarn](https://yarn.pm/react-fathom) - -```sh -yarn add react-fathom fathom-client -``` - -## Peer Dependencies - -- `react` >= 16.8 -- `react-dom` >= 16.8 (only if using web) -- `fathom-client` >= 3.0.0 (only if using web, not needed for React Native) -- `next` >= 10.0.0 (only if using Next.js providers) -- `react-native` >= 0.60.0 (only if using React Native) -- `react-native-webview` >= 11.0.0 (only if using React Native) +- πŸ”’ Privacy-first Fathom Analytics integration +- βš›οΈ Hooks API and declarative tracking components +- πŸ“± React Native with offline queuing and navigation tracking +- ⚑ Next.js App Router and Pages Router support +- 🌳 Tree-shakeable, fully typed (TypeScript) ## Usage -### Basic React Setup - -Wrap your app with `FathomProvider`: - -```tsx -import { FathomProvider } from 'react-fathom' - -function App() { - return {/* Your app */} -} -``` - -### Using the Hook - -Access Fathom methods via the `useFathom` hook: - -```tsx -import { useFathom } from 'react-fathom' - -function MyComponent() { - const { trackPageview, trackEvent, trackGoal, load } = useFathom() - - const handleClick = () => { - trackEvent('button-click', { _value: 100 }) // Optional: value in cents - } - - const handlePurchase = () => { - trackGoal('purchase', 2999) // $29.99 in cents - } - - return ( - <> - - - - ) -} -``` - -### Convenience Hooks - -Track events and pageviews with convenience hooks: - -```tsx -import { - useTrackOnMount, - useTrackOnClick, - useTrackOnVisible, -} from 'react-fathom' - -function MyComponent() { - // Track pageview on mount - useTrackOnMount({ url: '/custom-page' }) - - // Track event on click - const handleClick = useTrackOnClick({ - eventName: 'button-click', - _value: 100, // Optional: value in cents - callback: (e) => { - console.log('Tracked click!', e) - }, - }) - - // Track event when element becomes visible - const ref = useTrackOnVisible({ - eventName: 'section-viewed', - _value: 1, // Optional: value in cents - callback: (entry) => { - console.log('Element is visible!', entry) - }, - }) - - return ( - <> - -
This will be tracked when visible
- - ) -} -``` - -### Declarative Components - -Use declarative components for tracking: - -```tsx -import { TrackPageview, TrackClick, TrackVisible } from 'react-fathom' - -function MyPage() { - return ( - <> - {/* Track pageview on mount */} - -
Page content
-
- - {/* Track click events */} - - - - - {/* Track when element becomes visible */} - -
Hero section
-
- - ) -} -``` - ### Next.js App Router -**Recommended:** Use `NextFathomProviderApp` for easy integration in App Router layouts: - ```tsx // app/layout.tsx import { NextFathomProviderApp } from 'react-fathom/next' @@ -250,33 +83,10 @@ export default function RootLayout({ children }) { } ``` -**Alternative:** You can also use `FathomProvider` with `NextFathomTrackViewApp` separately if you need more control: - -```tsx -// app/layout.tsx -import { FathomProvider } from 'react-fathom' -import { NextFathomTrackViewApp } from 'react-fathom/next' - -export default function RootLayout({ children }) { - return ( - - - - - {children} - - - - ) -} -``` - -> **Note:** Since `FathomProvider` uses React hooks, you'll need to wrap it in a Client Component when using it directly in a Server Component layout. `NextFathomProviderApp` handles this for you automatically. +πŸ“– [Full Next.js guide](https://react-fathom.com/nextjs) ### Next.js Pages Router -Use `FathomProvider` with `NextFathomTrackViewPages` for automatic route tracking: - ```tsx // pages/_app.tsx import { FathomProvider } from 'react-fathom' @@ -290,121 +100,69 @@ function MyApp({ Component, pageProps }) { ) } - -export default MyApp ``` -## Default Options Merging - -The `FathomProvider` supports setting default options that automatically merge with any options passed to tracking calls. This is useful for setting app-wide defaults like custom event IDs or referrer information. - -### How Merging Works - -Default options are spread first, then any options you pass to individual tracking calls are spread second. This means: +πŸ“– [Full Next.js guide](https://react-fathom.com/nextjs) -- **Default options** provide base values for all tracking calls -- **Provided options** override defaults when specified -- You can set defaults once and forget about them - -```tsx - - {/* All trackEvent calls will include _site_id: 'my-app' unless overridden */} - -``` +### Hooks ```tsx -// Inside your component -const { trackEvent } = useFathom() +import { useFathom, useTrackOnMount, useTrackOnClick, useTrackOnVisible } from 'react-fathom' -// Uses default: { _site_id: 'my-app' } -trackEvent('button-click') - -// Merges with default: { _site_id: 'my-app', _value: 100 } -trackEvent('purchase', { _value: 100 }) - -// Overrides default: { _site_id: 'custom-site', _value: 50 } -trackEvent('special-event', { _site_id: 'custom-site', _value: 50 }) -``` +function MyComponent() { + const { trackEvent, trackGoal } = useFathom() -### Nested Providers + // Track on mount + useTrackOnMount({ url: '/custom-page' }) -When nesting `FathomProvider` components, child providers inherit defaults from their parent but can override them: + // Track clicks + const handleClick = useTrackOnClick({ eventName: 'cta-click', _value: 100 }) -```tsx - - {/* Events here use _site_id: 'global' */} + // Track visibility + const ref = useTrackOnVisible({ eventName: 'hero-viewed' }) - - {/* Events here use _site_id: 'dashboard' */} - - + return ( + <> +
Hero section
+ + + + ) +} ``` -## Custom Client Implementation +πŸ“– [Hooks API reference](https://react-fathom.com/api/hooks) -The `FathomProvider` accepts an optional `client` prop that allows you to provide a custom Fathom client implementation. This is useful for: - -- **React Native apps** that need a custom tracking implementation -- **Testing** with mock clients -- **Server-side rendering** scenarios -- **Custom analytics pipelines** that wrap Fathom +### Declarative Components -### FathomClient Interface +```tsx +import { TrackPageview, TrackClick, TrackVisible } from 'react-fathom' -Your custom client must implement the `FathomClient` interface: +function MyPage() { + return ( + <> + -```tsx -import type { FathomClient, EventOptions, LoadOptions, PageViewOptions } from 'react-fathom' + +
Tracks when visible
+
-const myCustomClient: FathomClient = { - load: (siteId: string, options?: LoadOptions) => { - // Initialize your tracking - }, - trackPageview: (opts?: PageViewOptions) => { - // Track pageview - }, - trackEvent: (eventName: string, opts?: EventOptions) => { - // Track custom event - }, - trackGoal: (code: string, cents: number) => { - // Track goal conversion - }, - setSite: (id: string) => { - // Change site ID - }, - blockTrackingForMe: () => { - // Block tracking - }, - enableTrackingForMe: () => { - // Enable tracking - }, - isTrackingEnabled: () => { - // Return tracking status - return true - }, + + + + + ) } ``` -### React Native - -For React Native apps, use the dedicated `/native` export. This uses a hidden WebView to load Fathom's official tracking script, ensuring full compatibility with Fathom Analytics. +πŸ“– [Components API reference](https://react-fathom.com/api/components) -**Install the required peer dependency:** +### React Native ```bash npm install react-native-webview -# or -yarn add react-native-webview ``` -**Basic setup:** - ```tsx import { NativeFathomProvider } from 'react-fathom/native' @@ -414,7 +172,6 @@ function App() { siteId="YOUR_SITE_ID" debug={__DEV__} trackAppState - onReady={() => console.log('Fathom ready!')} > @@ -422,869 +179,62 @@ function App() { } ``` -> **Note:** The provider renders a hidden WebView (0x0 pixels) that loads the Fathom script. Events are queued until the WebView is ready, then automatically sent. - -#### React Navigation Integration - -Track screen navigation as pageviews with React Navigation: - -```tsx -import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native' -import { NativeFathomProvider, useNavigationTracking } from 'react-fathom/native' - -function App() { - const navigationRef = useNavigationContainerRef() - - return ( - - - - - - - ) -} - -function NavigationTracker({ navigationRef }) { - useNavigationTracking({ - navigationRef, - transformRouteName: (name) => `/screens/${name}`, - }) - return null -} -``` - -#### App State Tracking - -Track when users foreground/background your app: - -```tsx -import { useAppStateTracking } from 'react-fathom/native' - -function AppTracker() { - useAppStateTracking({ - foregroundEventName: 'app-resumed', - backgroundEventName: 'app-paused', - onStateChange: (state) => console.log('App state:', state), - }) - return null -} -``` - -#### Using Custom Domains - -If you use [Fathom's custom domains feature](https://usefathom.com/docs/script/custom-domains), specify your domain: - -```tsx - - - -``` - -#### Advanced: Manual WebView Client Setup - -For advanced use cases, you can manually set up the WebView client: - -```tsx -import { useRef, useMemo, useCallback } from 'react' -import { - FathomWebView, - createWebViewClient, - FathomProvider, - type FathomWebViewRef, -} from 'react-fathom/native' - -function App() { - const webViewRef = useRef(null) - - const client = useMemo( - () => createWebViewClient(() => webViewRef.current, { debug: __DEV__ }), - [] - ) - - const handleReady = useCallback(() => { - client.setWebViewReady() - }, [client]) - - return ( - - - - - ) -} -``` - -### Mock Client for Testing - -```tsx -import { FathomProvider, type FathomClient } from 'react-fathom' - -const mockClient: FathomClient = { - load: jest.fn(), - trackPageview: jest.fn(), - trackEvent: jest.fn(), - trackGoal: jest.fn(), - setSite: jest.fn(), - blockTrackingForMe: jest.fn(), - enableTrackingForMe: jest.fn(), - isTrackingEnabled: jest.fn(() => true), -} - -// In your tests -render( - - - -) - -// Assert tracking calls -expect(mockClient.trackEvent).toHaveBeenCalledWith('button-click', { _value: 100 }) -``` - -## API - -### `FathomProvider` - -Main provider component for React apps. Supports composable nesting - nested providers can override `client`, `defaultPageviewOptions`, or `defaultEventOptions`. - -**Props:** - -- `siteId` (string, optional): Your Fathom Analytics site ID -- `client` (FathomClient, optional): Custom Fathom client instance -- `clientRef` (MutableRefObject, optional): Ref that will be populated with the resolved client instance, allowing the parent component to access the client directly -- `clientOptions` (LoadOptions, optional): Options passed to `fathom-client` -- `defaultPageviewOptions` (PageViewOptions, optional): Default options merged into all `trackPageview` calls -- `defaultEventOptions` (EventOptions, optional): Default options merged into all `trackEvent` calls - -**Example:** - -```tsx - - {/* Your app */} - -``` - -**Using clientRef for parent access:** - -```tsx -import { useRef } from 'react' -import { FathomProvider, FathomClient } from 'react-fathom' - -function App() { - const clientRef = useRef(null) - - const handleDeepLink = (url: string) => { - // Parent can track events directly via the ref - clientRef.current?.trackEvent('deep_link', { _url: url }) - } - - return ( - - - - ) -} -``` - -### `NextFathomProviderApp` - -Client component wrapper that combines `FathomProvider` and `NextFathomTrackViewApp` for easy integration in Next.js App Router layouts. This component is marked with `'use client'` and can be used directly in Server Components like the root `layout.tsx` file. - -**Props:** - -- `siteId` (string, optional): Your Fathom Analytics site ID -- `client` (FathomClient, optional): Custom Fathom client instance -- `clientOptions` (LoadOptions, optional): Options passed to `fathom-client` -- `defaultPageviewOptions` (PageViewOptions, optional): Default options merged into all `trackPageview` calls -- `defaultEventOptions` (EventOptions, optional): Default options merged into all `trackEvent` calls -- `disableAutoTrack` (boolean, optional): Disable automatic pageview tracking on route changes (defaults to false) -- `children` (ReactNode, required): Child components to render - -**Example:** - -```tsx -// app/layout.tsx -import { NextFathomProviderApp } from 'react-fathom/next' - -export default function RootLayout({ children }) { - return ( - - - - {children} - - - - ) -} -``` - -### `NextFathomTrackViewApp` - -Component that tracks pageviews for Next.js App Router. Must be used within a `FathomProvider`. - -**Props:** - -- `disableAutoTrack` (boolean, optional): Disable automatic pageview tracking on route changes (defaults to false) - -**Example:** - -```tsx - - - {/* Your app */} - -``` +πŸ“– [Full React Native guide](https://react-fathom.com/react-native) -### `NextFathomTrackViewPages` +## API Overview -Component that tracks pageviews for Next.js Pages Router. Must be used within a `FathomProvider`. +### Providers -**Props:** +| Component | Use case | +|-----------|----------| +| `FathomProvider` | Basic React apps | +| `NextFathomProviderApp` | Next.js App Router | +| `NextFathomTrackViewPages` | Next.js Pages Router (add inside `FathomProvider`) | +| `NativeFathomProvider` | React Native | -- `disableAutoTrack` (boolean, optional): Disable automatic pageview tracking on route changes (defaults to false) +### Hooks -**Example:** - -```tsx - - - {/* Your app */} - -``` - -### `useFathom()` - -Hook to access Fathom methods and context. - -**Returns:** - -- `trackPageview(options?)`: Track a pageview (automatically merges `defaultPageviewOptions`) -- `trackEvent(eventName, options?)`: Track a custom event (automatically merges `defaultEventOptions`) -- `trackGoal(code, cents)`: Track a goal conversion -- `load(siteId, options?)`: Load Fathom with a site ID -- `setSite(siteId)`: Change the site ID -- `blockTrackingForMe()`: Block tracking for current user -- `enableTrackingForMe()`: Enable tracking for current user -- `isTrackingEnabled()`: Check if tracking is enabled -- `client`: The Fathom client instance -- `defaultPageviewOptions`: Current default pageview options -- `defaultEventOptions`: Current default event options - -### `useTrackOnMount(options?)` - -Hook to track a pageview when a component mounts. - -**Options:** - -- `url` (string, optional): URL to track -- `referrer` (string, optional): Referrer URL -- All other `PageViewOptions` from `fathom-client` - -### `useTrackOnClick(options)` - -Hook that returns a click handler function to track events. - -**Options:** - -- `eventName` (string, required): Event name to track -- `preventDefault` (boolean, optional): Whether to prevent default behavior (defaults to false) -- `callback` ((e?: MouseEvent) => void, optional): Callback function to run after tracking -- All other `EventOptions` from `fathom-client` - -### `useTrackOnVisible(options)` - -Hook that returns a ref to attach to an element. Tracks an event when the element becomes visible. - -**Options:** - -- `eventName` (string, required): Event name to track -- `callback` ((entry: IntersectionObserverEntry) => void, optional): Callback function to run after tracking -- `threshold` (number, optional): IntersectionObserver threshold (defaults to 0.1) -- `rootMargin` (string, optional): IntersectionObserver rootMargin -- All other `EventOptions` from `fathom-client` - -### `TrackPageview` - -Component that tracks a pageview when it mounts. - -**Props:** - -- `url` (string, optional): URL to track -- `referrer` (string, optional): Referrer URL -- `children` (ReactNode, optional): Child elements to render -- All other `PageViewOptions` from `fathom-client` - -### `TrackClick` - -Component that tracks an event when clicked. - -**Props:** - -- `eventName` (string, required): Event name to track -- `preventDefault` (boolean, optional): Whether to prevent default behavior (defaults to false) -- `children` (ReactNode, required): Child element(s) to render -- All other `EventOptions` from `fathom-client` - -### `TrackVisible` - -Component that tracks an event when it becomes visible. - -**Props:** - -- `eventName` (string, required): Event name to track -- `threshold` (number, optional): IntersectionObserver threshold (defaults to 0.1) -- `rootMargin` (string, optional): IntersectionObserver rootMargin -- `children` (ReactNode, required): Child element(s) to render -- `as` (string, optional): HTML element type to render (defaults to 'div') -- All other `EventOptions` from `fathom-client` - -## Native API - -The `/native` export provides React Native-specific components and hooks. It uses a hidden WebView to load Fathom's official tracking script, ensuring full compatibility with Fathom Analytics (both Fathom Pro and self-hosted Fathom Lite). - -### `NativeFathomProvider` - -Convenience provider for React Native apps that manages a hidden WebView with Fathom's tracking script. - -**Props:** - -- `siteId` (string, required): Your Fathom Analytics site ID -- `loadOptions` (LoadOptions, optional): Options passed to `fathom.load()` in the WebView -- `scriptDomain` (string, optional): Custom domain for Fathom script (defaults to 'cdn.usefathom.com') -- `defaultPageviewOptions` (PageViewOptions, optional): Default options merged into all `trackPageview` calls -- `defaultEventOptions` (EventOptions, optional): Default options merged into all `trackEvent` calls -- `trackAppState` (boolean, optional): Enable automatic app state tracking (defaults to false) -- `debug` (boolean, optional): Enable debug logging (defaults to false) -- `onReady` (() => void, optional): Called when the Fathom script has loaded -- `onError` ((error: string) => void, optional): Called when an error occurs loading the script -- `clientRef` (MutableRefObject, optional): Ref that will be populated with the WebView-based client instance, allowing the parent component to access the client directly (includes queue management methods) -- `children` (ReactNode, required): Child components to render - -**Example:** - -```tsx - console.log('Analytics ready!')} - onError={(err) => console.error('Analytics error:', err)} -> - - -``` - -**Using clientRef for parent access:** - -```tsx -import { useRef } from 'react' -import { NativeFathomProvider, WebViewFathomClient } from 'react-fathom/native' - -function App() { - const clientRef = useRef(null) - - const handleDeepLink = (url: string) => { - // Parent can track events directly via the ref - clientRef.current?.trackEvent('deep_link', { _url: url }) - - // Can also check queue status (React Native specific) - console.log('Queued events:', clientRef.current?.getQueueLength()) - } - - return ( - - - - ) -} -``` - -### `FathomWebView` - -Hidden WebView component that loads and manages the Fathom Analytics script. Used internally by `NativeFathomProvider`, but can be used directly for advanced setups. - -**Props:** - -- `siteId` (string, required): Your Fathom Analytics site ID -- `loadOptions` (LoadOptions, optional): Options passed to `fathom.load()` -- `scriptDomain` (string, optional): Custom domain for Fathom script (defaults to 'cdn.usefathom.com') -- `onReady` (() => void, optional): Called when the Fathom script has loaded -- `onError` ((error: string) => void, optional): Called when an error occurs -- `debug` (boolean, optional): Enable debug logging (defaults to false) - -**Ref Methods (FathomWebViewRef):** - -- `trackPageview(opts?)`: Track a pageview -- `trackEvent(eventName, opts?)`: Track a custom event -- `trackGoal(code, cents)`: Track a goal conversion -- `blockTrackingForMe()`: Block tracking for current user -- `enableTrackingForMe()`: Enable tracking for current user -- `isReady()`: Check if the WebView is ready - -### `createWebViewClient(getWebViewRef, options?)` - -Factory function to create a client that communicates with a FathomWebView. - -**Parameters:** - -- `getWebViewRef` (() => FathomWebViewRef | null): Function that returns the WebView ref -- `options` (WebViewClientOptions, optional): - - `debug` (boolean): Enable debug logging (defaults to false) - - `enableQueue` (boolean): Enable command queuing before WebView is ready (defaults to true) - - `maxQueueSize` (number): Maximum commands to queue (defaults to 100) - -**Returns:** A `FathomClient` instance with additional methods: - -- `processQueue()`: Manually process queued commands (returns number of processed) -- `getQueueLength()`: Get the current queue length -- `setWebViewReady()`: Call when WebView signals it's ready (flushes queue) - -### `useAppStateTracking(options?)` - -Hook that tracks app state changes (foreground/background) as Fathom events. - -**Options:** - -- `foregroundEventName` (string, optional): Event name for foreground (defaults to 'app-foreground') -- `backgroundEventName` (string, optional): Event name for background (defaults to 'app-background') -- `eventOptions` (EventOptions, optional): Additional options for app state events -- `onStateChange` ((state: 'active' | 'background' | 'inactive') => void, optional): Callback on state change - -### `useNavigationTracking(options)` - -Hook that tracks React Navigation screen changes as pageviews. - -**Options:** - -- `navigationRef` (RefObject, required): React Navigation container ref -- `transformRouteName` ((name: string) => string, optional): Transform route names before tracking -- `shouldTrackRoute` ((name: string, params?: object) => boolean, optional): Filter which routes to track -- `includeParams` (boolean, optional): Include route params in tracked URL (defaults to false) - -**Example:** - -```tsx -const navigationRef = useNavigationContainerRef() - -useNavigationTracking({ - navigationRef, - transformRouteName: (name) => `/app/${name.toLowerCase()}`, - shouldTrackRoute: (name) => !name.startsWith('Modal'), - includeParams: true, -}) -``` - -## Tree-shaking - -This library is optimized for tree-shaking. When you import only what you need: - -```tsx -import { useFathom } from 'react-fathom' -``` - -Bundlers will automatically exclude unused code, keeping your bundle size minimal. - -## TypeScript - -Full TypeScript support is included. Types are automatically generated and exported. - -### Exported Types - -For convenience, `react-fathom` re-exports the core types from `fathom-client` so you don't need to import from multiple packages: - -```tsx -import type { - // From react-fathom - FathomClient, - FathomContextInterface, - FathomProviderProps, - // Re-exported from fathom-client - EventOptions, - LoadOptions, - PageViewOptions, -} from 'react-fathom' - -// No need for this anymore: -// import type { EventOptions } from 'fathom-client' -``` - -This simplifies your imports when building custom clients or working with typed event options. - -## Troubleshooting - -### Common Issues - -#### Events not appearing in Fathom dashboard - -**1. Verify your site ID** - -Your site ID should match exactly what's shown in your [Fathom dashboard](https://app.usefathom.com). It's typically an 8-character alphanumeric string like `ABCD1234`. - -```tsx -// Double-check this value - -``` - -**2. Check for ad blockers** - -Many ad blockers and privacy extensions block analytics scripts. To test: -- Open an incognito/private window with extensions disabled -- Or temporarily whitelist your development domain - -**3. Domain restrictions** - -Fathom only tracks events from domains you've configured. For local development: - -```tsx - -``` - -**4. Inspect network requests** - -Open your browser's Network tab and look for requests to `cdn.usefathom.com`. If you see: -- **No requests**: The script isn't loading (check provider setup) -- **Blocked requests**: Ad blocker is interfering -- **Failed requests**: Check your site ID and domain configuration - -#### Duplicate pageview tracking - -If you're seeing double pageviews, you likely have multiple tracking setups: - -```tsx -// WRONG: Both auto tracking AND manual tracking - - {/* This tracks pageviews */} - {/* AND clientOptions.auto defaults to true, which also tracks */} - - -// CORRECT: Use one or the other - - - -``` - -#### useFathom returns undefined methods - -This was the old behavior. As of the latest version, `useFathom()` returns stub methods that warn in development when called outside a provider. If you're seeing `undefined`: - -1. Update to the latest version: `npm update react-fathom` -2. Ensure your component is inside a `FathomProvider` - -#### Next.js App Router: "use client" errors - -Server Components can't use hooks directly. Use the pre-configured client component: - -```tsx -// app/layout.tsx -import { NextFathomProviderApp } from 'react-fathom/next' - -export default function RootLayout({ children }) { - return ( - - - - {children} - - - - ) -} -``` - -If you need a custom setup, create your own client component wrapper: - -```tsx -// components/AnalyticsProvider.tsx -'use client' -import { FathomProvider } from 'react-fathom' - -export function AnalyticsProvider({ children }) { - return ( - - {children} - - ) -} -``` - -#### Next.js: Environment variables not loading - -Ensure your environment variable is prefixed with `NEXT_PUBLIC_` to be available client-side: - -```bash -# .env.local -NEXT_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID # βœ“ Correct -FATHOM_SITE_ID=YOUR_SITE_ID # βœ— Won't work client-side -``` - -#### React Native: Events not sending - -**1. Verify react-native-webview is installed** - -The native module requires `react-native-webview`: - -```bash -npm install react-native-webview -# For iOS, also run: -cd ios && pod install -``` - -**2. Check WebView is ready** - -Events are queued until the WebView loads. Use the `onReady` callback to verify: - -```tsx - console.log('Fathom WebView ready!')} - onError={(err) => console.error('Fathom error:', err)} -> -``` - -**3. Verify network connectivity** - -The WebView needs network access to load the Fathom script. Events are queued before the WebView is ready but won't send if the script fails to load. - -**4. Check for WebView restrictions** - -Some enterprise MDM solutions or app configurations may block WebViews from loading external scripts. Verify that `cdn.usefathom.com` (or your custom domain) is accessible. - -**5. Debug with logging** - -Enable debug mode to see all tracking activity: - -```tsx - -``` - -### Debugging Tips - -#### Enable verbose logging - -For web, check the browser console. For React Native, enable debug mode: - -```tsx -// React Native - -``` - -#### Verify tracking in real-time - -Fathom's dashboard updates in real-time. Open your dashboard alongside your app to see events as they're tracked. - -#### Test with a mock client - -For debugging, replace the real client with a mock that logs everything: - -```tsx -const debugClient = { - load: (id, opts) => console.log('load:', id, opts), - trackPageview: (opts) => console.log('pageview:', opts), - trackEvent: (name, opts) => console.log('event:', name, opts), - trackGoal: (code, cents) => console.log('goal:', code, cents), - setSite: (id) => console.log('setSite:', id), - blockTrackingForMe: () => console.log('blocked'), - enableTrackingForMe: () => console.log('enabled'), - isTrackingEnabled: () => true, -} - - -``` - -### Getting Help - -- [Open an issue](https://github.com/ryanhefner/react-fathom/issues) on GitHub -- [Search existing issues](https://github.com/ryanhefner/react-fathom/issues?q=is%3Aissue) for solutions -- [Fathom Analytics documentation](https://usefathom.com/docs) for platform-specific questions - -## Contributing - -Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or examples, we appreciate your help. - -### Ways to Contribute - -| Type | Description | +| Hook | Description | |------|-------------| -| **Bug Reports** | Found a bug? [Open an issue](https://github.com/ryanhefner/react-fathom/issues/new) with reproduction steps | -| **Feature Requests** | Have an idea? Discuss it in an issue first | -| **Bug Fixes** | PRs for documented issues are always welcome | -| **Documentation** | Help improve docs, add examples, fix typos | -| **Tests** | Increase test coverage or add edge case tests | +| `useFathom()` | Returns `trackPageview`, `trackEvent`, `trackGoal`, and more | +| `useTrackOnMount(opts?)` | Track pageview when component mounts | +| `useTrackOnClick(opts)` | Returns click handler that tracks event | +| `useTrackOnVisible(opts)` | Returns ref; tracks when element becomes visible | -### Development Setup +### Components -**Prerequisites:** -- Node.js 18+ -- npm 9+ +| Component | Description | +|-----------|-------------| +| `` | Track pageview on mount | +| `` | Track event on click | +| `` | Track when visible (IntersectionObserver) | -**1. Clone and install:** +πŸ“– [Full API Reference](https://react-fathom.com/api/providers) -```bash -git clone https://github.com/ryanhefner/react-fathom.git -cd react-fathom -npm install -``` - -**2. Run the development workflow:** +## Common Issues -```bash -# Run tests in watch mode during development -npm run test:watch - -# Run the full test suite -npm test - -# Build the package -npm run build - -# Type check without emitting -npm run typecheck -``` - -### Project Structure - -``` -react-fathom/ -β”œβ”€β”€ src/ -β”‚ β”œβ”€β”€ index.ts # Main entry point -β”‚ β”œβ”€β”€ FathomProvider.tsx # Core provider component -β”‚ β”œβ”€β”€ FathomContext.tsx # React context -β”‚ β”œβ”€β”€ types.ts # TypeScript definitions -β”‚ β”œβ”€β”€ hooks/ # React hooks -β”‚ β”‚ β”œβ”€β”€ useFathom.ts -β”‚ β”‚ β”œβ”€β”€ useTrackOnClick.ts -β”‚ β”‚ β”œβ”€β”€ useTrackOnMount.ts -β”‚ β”‚ └── useTrackOnVisible.ts -β”‚ β”œβ”€β”€ components/ # Declarative tracking components -β”‚ β”‚ β”œβ”€β”€ TrackClick.tsx -β”‚ β”‚ β”œβ”€β”€ TrackPageview.tsx -β”‚ β”‚ └── TrackVisible.tsx -β”‚ β”œβ”€β”€ next/ # Next.js-specific exports -β”‚ β”‚ └── index.ts -β”‚ └── native/ # React Native exports -β”‚ β”œβ”€β”€ index.ts -β”‚ β”œβ”€β”€ FathomWebView.tsx -β”‚ β”œβ”€β”€ createWebViewClient.ts -β”‚ β”œβ”€β”€ NativeFathomProvider.tsx -β”‚ β”œβ”€β”€ useNavigationTracking.ts -β”‚ └── useAppStateTracking.ts -β”œβ”€β”€ examples/ # Example applications -β”‚ β”œβ”€β”€ next-app/ # Next.js App Router example -β”‚ └── next-pages/ # Next.js Pages Router example -└── dist/ # Built output (generated) -``` - -### Testing Guidelines - -We use [Vitest](https://vitest.dev/) for testing. All new features should include tests. - -```bash -# Run all tests -npm test - -# Run tests in watch mode -npm run test:watch - -# Run tests with coverage -npm run test:coverage -``` - -**Writing tests:** +**Events not appearing?** +1. Verify site ID matches your [Fathom dashboard](https://app.usefathom.com) +2. Check for ad blockers (test in incognito) +3. Add `{ includedDomains: ['localhost'] }` to `clientOptions` +**Duplicate pageviews?** ```tsx -// src/hooks/useMyHook.test.tsx -import { renderHook } from '@testing-library/react' -import { describe, it, expect, vi } from 'vitest' -import { useMyHook } from './useMyHook' -import { FathomProvider } from '../FathomProvider' - -describe('useMyHook', () => { - it('should track events correctly', () => { - const mockClient = { - trackEvent: vi.fn(), - // ... other required methods - } - - const wrapper = ({ children }) => ( - {children} - ) - - const { result } = renderHook(() => useMyHook(), { wrapper }) - - result.current.doSomething() - - expect(mockClient.trackEvent).toHaveBeenCalledWith('expected-event', {}) - }) -}) +// Disable fathom-client's auto tracking when using NextFathomTrackViewApp + ``` -### Code Style +πŸ“– [Full Troubleshooting Guide](https://react-fathom.com/troubleshooting) -- **TypeScript**: All code should be fully typed -- **Formatting**: We use Prettier (run `npm run format` before committing) -- **Linting**: ESLint catches common issues (run `npm run lint`) -- **Naming**: - - Components: PascalCase (`TrackClick.tsx`) - - Hooks: camelCase with `use` prefix (`useFathom.ts`) - - Types: PascalCase (`FathomClient`) - -### Submitting a Pull Request - -1. **Fork** the repository -2. **Create a branch** from `main`: - ```bash - git checkout -b fix/my-bug-fix - # or - git checkout -b feature/my-new-feature - ``` -3. **Make your changes** with clear, focused commits -4. **Add or update tests** for your changes -5. **Ensure CI passes**: - ```bash - npm run lint - npm test - npm run build - ``` -6. **Submit a PR** with a clear description of what and why - -### Commit Message Guidelines - -Use clear, descriptive commit messages: - -``` -feat: add useTrackOnScroll hook for scroll tracking -fix: resolve duplicate pageview tracking in Next.js -docs: add troubleshooting section for ad blockers -test: add tests for native offline queue -refactor: simplify FathomContext default values -``` +## Documentation -Prefixes: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `perf` +- πŸ“– [Getting Started](https://react-fathom.com/getting-started) +- βš›οΈ [React Guide](https://react-fathom.com/react) +- ⚑ [Next.js Guide](https://react-fathom.com/nextjs) +- πŸ“± [React Native Guide](https://react-fathom.com/react-native) +- πŸ“š [API Reference](https://react-fathom.com/api/providers) +- πŸ”§ [Troubleshooting](https://react-fathom.com/troubleshooting) +- 🀝 [Contributing](https://react-fathom.com/contributing) ## License diff --git a/docs/app/[[...mdxPath]]/page.tsx b/docs/app/[[...mdxPath]]/page.tsx new file mode 100644 index 0000000..ec073e9 --- /dev/null +++ b/docs/app/[[...mdxPath]]/page.tsx @@ -0,0 +1,26 @@ +import { generateStaticParamsFor, importPage } from 'nextra/pages' +import { useMDXComponents } from '../../mdx-components' + +export const generateStaticParams = generateStaticParamsFor('mdxPath') + +export async function generateMetadata(props: PageProps) { + const params = await props.params + const { metadata } = await importPage(params.mdxPath) + return metadata +} + +type PageProps = { + params: Promise<{ mdxPath?: string[] }> +} + +export default async function Page(props: PageProps) { + const params = await props.params + const { default: MDXContent, toc, metadata } = await importPage(params.mdxPath) + const Wrapper = useMDXComponents().wrapper + + return ( + + + + ) +} diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx new file mode 100644 index 0000000..8fa0e2d --- /dev/null +++ b/docs/app/layout.tsx @@ -0,0 +1,43 @@ +import { Footer, Layout, Navbar } from 'nextra-theme-docs' +import { Head } from 'nextra/components' +import { getPageMap } from 'nextra/page-map' +import type { ReactNode } from 'react' +import 'nextra-theme-docs/style.css' + +export const metadata = { + title: { + default: 'react-fathom', + template: '%s – react-fathom', + }, + description: 'Privacy-focused analytics for React, Next.js, and React Native', +} + +const navbar = ( + react-fathom} + projectLink="https://github.com/ryanhefner/react-fathom" + /> +) + +const footer =
MIT {new Date().getFullYear()} Β© Ryan Hefner
+ +export default async function RootLayout({ children }: { children: ReactNode }) { + return ( + + + + + {children} + + + + ) +} diff --git a/docs/content/_meta.ts b/docs/content/_meta.ts new file mode 100644 index 0000000..c002b9e --- /dev/null +++ b/docs/content/_meta.ts @@ -0,0 +1,11 @@ +export default { + index: 'Introduction', + 'getting-started': 'Getting Started', + react: 'React', + nextjs: 'Next.js', + 'react-native': 'React Native', + api: 'API Reference', + guides: 'Guides', + troubleshooting: 'Troubleshooting', + contributing: 'Contributing', +} diff --git a/docs/content/api/_meta.ts b/docs/content/api/_meta.ts new file mode 100644 index 0000000..82b47df --- /dev/null +++ b/docs/content/api/_meta.ts @@ -0,0 +1,6 @@ +export default { + providers: 'Providers', + hooks: 'Hooks', + components: 'Components', + native: 'Native API', +} diff --git a/docs/content/api/components.mdx b/docs/content/api/components.mdx new file mode 100644 index 0000000..5517ac1 --- /dev/null +++ b/docs/content/api/components.mdx @@ -0,0 +1,171 @@ +# Components + +## TrackPageview + +Track a pageview when the component mounts. + +```tsx +import { TrackPageview } from 'react-fathom' + +function Dashboard() { + return ( + +
Dashboard content
+
+ ) +} +``` + +### Props + +| Prop | Type | Required | Description | +|------|------|----------|-------------| +| `url` | `string` | No | URL to track | +| `referrer` | `string` | No | Referrer URL | +| `children` | `ReactNode` | No | Child elements to render | + +### Without Children + +```tsx +<> + + + +``` + +### With Referrer + +```tsx + + + +``` + +--- + +## TrackClick + +Track an event when a child element is clicked. + +```tsx +import { TrackClick } from 'react-fathom' + +function SignUpSection() { + return ( + + + + ) +} +``` + +### Props + +| Prop | Type | Required | Description | +|------|------|----------|-------------| +| `eventName` | `string` | Yes | Event name to track | +| `_value` | `number` | No | Value in cents | +| `preventDefault` | `boolean` | No | Prevent default click behavior | +| `children` | `ReactNode` | Yes | Clickable child element | + +### With Value + +```tsx + + + +``` + +### With Links + +```tsx + + View Documentation + +``` + +### Multiple Children + +The click handler is attached to a wrapper `div`: + +```tsx + +
+

Feature

+

Description

+
+
+``` + +--- + +## TrackVisible + +Track an event when an element becomes visible in the viewport. + +```tsx +import { TrackVisible } from 'react-fathom' + +function PricingSection() { + return ( + +
+

Pricing

+ {/* pricing content */} +
+
+ ) +} +``` + +### Props + +| Prop | Type | Required | Default | Description | +|------|------|----------|---------|-------------| +| `eventName` | `string` | Yes | - | Event name to track | +| `_value` | `number` | No | - | Value in cents | +| `threshold` | `number` | No | `0.1` | Visibility threshold (0-1) | +| `rootMargin` | `string` | No | - | IntersectionObserver root margin | +| `as` | `string` | No | `'div'` | HTML element to render | +| `children` | `ReactNode` | Yes | - | Child elements | + +### Custom Element Type + +```tsx + +

Footer content

+
+``` + +```tsx + +

Section Title

+
+``` + +### With Threshold + +```tsx +// Track when 50% of the element is visible + +
+

Welcome

+
+
+``` + +### With Root Margin + +```tsx +// Track 100px before the element enters the viewport + +
Content
+
+``` + +### Implementation Notes + +- Uses `IntersectionObserver` internally +- Only tracks once per mount +- Passes through all additional props to the wrapper element +- Falls back gracefully if IntersectionObserver is unavailable diff --git a/docs/content/api/hooks.mdx b/docs/content/api/hooks.mdx new file mode 100644 index 0000000..4e1b721 --- /dev/null +++ b/docs/content/api/hooks.mdx @@ -0,0 +1,239 @@ +# Hooks + +## useFathom + +Main hook for accessing Fathom tracking methods. + +```tsx +import { useFathom } from 'react-fathom' + +function MyComponent() { + const { + trackPageview, + trackEvent, + trackGoal, + load, + setSite, + blockTrackingForMe, + enableTrackingForMe, + isTrackingEnabled, + client, + defaultPageviewOptions, + defaultEventOptions, + } = useFathom() +} +``` + +### Return Value + +| Property | Type | Description | +|----------|------|-------------| +| `trackPageview` | `(options?) => void` | Track a pageview | +| `trackEvent` | `(eventName, options?) => void` | Track a custom event | +| `trackGoal` | `(code, cents) => void` | Track a goal conversion | +| `load` | `(siteId, options?) => void` | Load Fathom with a site ID | +| `setSite` | `(siteId) => void` | Change the site ID | +| `blockTrackingForMe` | `() => void` | Block tracking for current user | +| `enableTrackingForMe` | `() => void` | Enable tracking for current user | +| `isTrackingEnabled` | `() => boolean` | Check if tracking is enabled | +| `client` | `FathomClient` | The underlying client instance | +| `defaultPageviewOptions` | `PageViewOptions` | Current default pageview options | +| `defaultEventOptions` | `EventOptions` | Current default event options | + +### trackPageview + +```tsx +const { trackPageview } = useFathom() + +// Track current page +trackPageview() + +// Track with custom URL +trackPageview({ url: '/custom-page' }) + +// Track with referrer +trackPageview({ url: '/landing', referrer: 'https://google.com' }) +``` + +**Options:** + +| Option | Type | Description | +|--------|------|-------------| +| `url` | `string` | URL to track | +| `referrer` | `string` | Referrer URL | + +### trackEvent + +```tsx +const { trackEvent } = useFathom() + +// Basic event +trackEvent('button-click') + +// Event with value (in cents) +trackEvent('purchase', { _value: 2999 }) // $29.99 +``` + +**Options:** + +| Option | Type | Description | +|--------|------|-------------| +| `_value` | `number` | Value in cents | +| `_site_id` | `string` | Override site ID | + +### trackGoal + +```tsx +const { trackGoal } = useFathom() + +// Track a goal conversion +trackGoal('SIGNUP', 0) // No value +trackGoal('PURCHASE', 4999) // $49.99 +``` + +**Parameters:** + +| Parameter | Type | Description | +|-----------|------|-------------| +| `code` | `string` | Goal code from Fathom dashboard | +| `cents` | `number` | Value in cents | + +--- + +## useTrackOnMount + +Track a pageview when a component mounts. + +```tsx +import { useTrackOnMount } from 'react-fathom' + +function LandingPage() { + useTrackOnMount({ url: '/landing' }) + + return
Welcome!
+} +``` + +### Options + +| Option | Type | Description | +|--------|------|-------------| +| `url` | `string` | URL to track | +| `referrer` | `string` | Referrer URL | + +All options are passed to `trackPageview`. + +--- + +## useTrackOnClick + +Returns a click handler that tracks an event. + +```tsx +import { useTrackOnClick } from 'react-fathom' + +function CTAButton() { + const handleClick = useTrackOnClick({ + eventName: 'cta-click', + _value: 100, + }) + + return +} +``` + +### Options + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `eventName` | `string` | Yes | Event name to track | +| `preventDefault` | `boolean` | No | Prevent default click behavior | +| `callback` | `(e?: MouseEvent) => void` | No | Called after tracking | +| `_value` | `number` | No | Value in cents | + +### With Callback + +```tsx +const handleClick = useTrackOnClick({ + eventName: 'signup-click', + callback: (e) => { + // Do something after tracking + router.push('/signup') + }, +}) +``` + +### With preventDefault + +```tsx +const handleClick = useTrackOnClick({ + eventName: 'link-click', + preventDefault: true, + callback: () => { + // Handle navigation manually + window.location.href = '/external-link' + }, +}) + +return External Link +``` + +--- + +## useTrackOnVisible + +Returns a ref that tracks an event when the element becomes visible. + +```tsx +import { useTrackOnVisible } from 'react-fathom' + +function HeroSection() { + const ref = useTrackOnVisible({ + eventName: 'hero-viewed', + }) + + return ( +
+

Welcome

+
+ ) +} +``` + +### Options + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `eventName` | `string` | Yes | Event name to track | +| `threshold` | `number` | No | Visibility threshold (0-1, default: `0.1`) | +| `rootMargin` | `string` | No | IntersectionObserver root margin | +| `callback` | `(entry: IntersectionObserverEntry) => void` | No | Called when visible | +| `_value` | `number` | No | Value in cents | + +### With Threshold + +```tsx +// Track when 50% visible +const ref = useTrackOnVisible({ + eventName: 'section-viewed', + threshold: 0.5, +}) +``` + +### With Callback + +```tsx +const ref = useTrackOnVisible({ + eventName: 'pricing-viewed', + callback: (entry) => { + console.log('Pricing section visible!', entry.intersectionRatio) + }, +}) +``` + +### Implementation Notes + +- Uses `IntersectionObserver` internally +- Only tracks once per mount +- Cleans up observer on unmount +- Falls back gracefully if IntersectionObserver is unavailable diff --git a/docs/content/api/native.mdx b/docs/content/api/native.mdx new file mode 100644 index 0000000..529bd0a --- /dev/null +++ b/docs/content/api/native.mdx @@ -0,0 +1,180 @@ +# Native API + +React Native-specific exports from `react-fathom/native`. + +## FathomWebView + +Hidden WebView component that loads the Fathom Analytics script. + +```tsx +import { FathomWebView, type FathomWebViewRef } from 'react-fathom/native' + +const webViewRef = useRef(null) + + console.log('Ready!')} + onError={(err) => console.error(err)} + debug={__DEV__} +/> +``` + +### Props + +| Prop | Type | Required | Description | +|------|------|----------|-------------| +| `siteId` | `string` | Yes | Your Fathom site ID | +| `loadOptions` | `LoadOptions` | No | Options passed to `fathom.load()` | +| `scriptDomain` | `string` | No | Custom domain (default: `cdn.usefathom.com`) | +| `onReady` | `() => void` | No | Called when script loads | +| `onError` | `(error: string) => void` | No | Called on error | +| `debug` | `boolean` | No | Enable debug logging | + +### Ref Methods (FathomWebViewRef) + +| Method | Description | +|--------|-------------| +| `trackPageview(opts?)` | Track a pageview | +| `trackEvent(eventName, opts?)` | Track a custom event | +| `trackGoal(code, cents)` | Track a goal conversion | +| `blockTrackingForMe()` | Block tracking for user | +| `enableTrackingForMe()` | Enable tracking for user | +| `isReady()` | Check if WebView is ready | + +--- + +## createWebViewClient + +Factory function to create a client that communicates with a FathomWebView. + +```tsx +import { createWebViewClient } from 'react-fathom/native' + +const client = createWebViewClient( + () => webViewRef.current, + { debug: true } +) +``` + +### Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `getWebViewRef` | `() => FathomWebViewRef \| null` | Function returning the WebView ref | +| `options` | `WebViewClientOptions` | Configuration options | + +### Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `debug` | `boolean` | `false` | Enable debug logging | +| `enableQueue` | `boolean` | `true` | Queue commands before WebView ready | +| `maxQueueSize` | `number` | `100` | Maximum queued commands | + +### Return Value (WebViewFathomClient) + +Standard `FathomClient` methods plus: + +| Method | Description | +|--------|-------------| +| `processQueue()` | Manually process queued commands (returns count) | +| `getQueueLength()` | Get current queue length | +| `setWebViewReady()` | Signal WebView is ready (flushes queue) | + +--- + +## useNavigationTracking + +Hook to track React Navigation screen changes as pageviews. + +```tsx +import { useNavigationTracking } from 'react-fathom/native' + +useNavigationTracking({ + navigationRef, + transformRouteName: (name) => `/screens/${name}`, +}) +``` + +### Options + +| Option | Type | Required | Description | +|--------|------|----------|-------------| +| `navigationRef` | `RefObject` | Yes | React Navigation container ref | +| `transformRouteName` | `(name: string) => string` | No | Transform route names | +| `shouldTrackRoute` | `(name: string, params?: object) => boolean` | No | Filter routes to track | +| `includeParams` | `boolean` | No | Include params in URL (default: `false`) | + +See [Navigation Tracking](/react-native/navigation) for detailed usage. + +--- + +## useAppStateTracking + +Hook to track app foreground/background state changes. + +```tsx +import { useAppStateTracking } from 'react-fathom/native' + +useAppStateTracking({ + foregroundEventName: 'app-resumed', + backgroundEventName: 'app-paused', +}) +``` + +### Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `foregroundEventName` | `string` | `'app-foreground'` | Event for foreground | +| `backgroundEventName` | `string` | `'app-background'` | Event for background | +| `eventOptions` | `EventOptions` | - | Additional event options | +| `onStateChange` | `(state) => void` | - | Callback on state change | + +See [App State Tracking](/react-native/app-state) for detailed usage. + +--- + +## Types + +### FathomClient + +Interface for Fathom client implementations: + +```tsx +interface FathomClient { + load: (siteId: string, options?: LoadOptions) => void + trackPageview: (opts?: PageViewOptions) => void + trackEvent: (eventName: string, opts?: EventOptions) => void + trackGoal: (code: string, cents: number) => void + setSite: (id: string) => void + blockTrackingForMe: () => void + enableTrackingForMe: () => void + isTrackingEnabled: () => boolean +} +``` + +### WebViewFathomClient + +Extends `FathomClient` with queue management: + +```tsx +interface WebViewFathomClient extends FathomClient { + processQueue: () => number + getQueueLength: () => number + setWebViewReady: () => void +} +``` + +### Re-exported Types + +These types are re-exported from `fathom-client` for convenience: + +```tsx +import type { + EventOptions, + LoadOptions, + PageViewOptions, +} from 'react-fathom/native' +``` diff --git a/docs/content/api/providers.mdx b/docs/content/api/providers.mdx new file mode 100644 index 0000000..8023321 --- /dev/null +++ b/docs/content/api/providers.mdx @@ -0,0 +1,189 @@ +# Providers + +## FathomProvider + +Main provider component for React apps. Wraps your application and provides the Fathom context. + +```tsx +import { FathomProvider } from 'react-fathom' + + + + +``` + +### Props + +| Prop | Type | Required | Description | +|------|------|----------|-------------| +| `siteId` | `string` | No* | Your Fathom Analytics site ID | +| `client` | `FathomClient` | No* | Custom Fathom client instance | +| `clientRef` | `MutableRefObject` | No | Ref populated with the client instance | +| `clientOptions` | `LoadOptions` | No | Options passed to `fathom-client` | +| `defaultPageviewOptions` | `PageViewOptions` | No | Default options merged into all `trackPageview` calls | +| `defaultEventOptions` | `EventOptions` | No | Default options merged into all `trackEvent` calls | +| `children` | `ReactNode` | Yes | Child components | + +*Either `siteId` or `client` must be provided. + +### clientOptions + +Options passed to the underlying `fathom-client`: + +| Option | Type | Description | +|--------|------|-------------| +| `auto` | `boolean` | Enable automatic pageview tracking (default: `true`) | +| `canonical` | `boolean` | Use canonical URL for tracking | +| `honorDNT` | `boolean` | Honor Do Not Track browser setting | +| `includedDomains` | `string[]` | Only track on these domains | +| `excludedDomains` | `string[]` | Don't track on these domains | +| `spa` | `'auto' \| 'history' \| 'hash'` | SPA mode for route detection | + +### Example with All Options + +```tsx + + + +``` + +### Nested Providers + +Providers can be nested to override defaults for specific sections: + +```tsx + + {/* Events here use _site_id: 'global' */} + + + {/* Events here use _site_id: 'dashboard' */} + + +``` + +--- + +## NextFathomProviderApp + +Client Component wrapper for Next.js App Router. Combines `FathomProvider` and `NextFathomTrackViewApp`. + +```tsx +import { NextFathomProviderApp } from 'react-fathom/next' + +// app/layout.tsx +export default function RootLayout({ children }) { + return ( + + + + {children} + + + + ) +} +``` + +### Props + +All `FathomProvider` props, plus: + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `disableAutoTrack` | `boolean` | `false` | Disable automatic pageview tracking on route changes | + +--- + +## NextFathomTrackViewApp + +Tracks pageviews for Next.js App Router. Must be used within a `FathomProvider`. + +```tsx +import { FathomProvider } from 'react-fathom' +import { NextFathomTrackViewApp } from 'react-fathom/next' + + + + {children} + +``` + +### Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `disableAutoTrack` | `boolean` | `false` | Disable automatic route tracking | + +--- + +## NextFathomTrackViewPages + +Tracks pageviews for Next.js Pages Router. Must be used within a `FathomProvider`. + +```tsx +import { FathomProvider } from 'react-fathom' +import { NextFathomTrackViewPages } from 'react-fathom/next' + +// pages/_app.tsx +function MyApp({ Component, pageProps }) { + return ( + + + + + ) +} +``` + +### Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `disableAutoTrack` | `boolean` | `false` | Disable automatic route tracking | + +--- + +## NativeFathomProvider + +Provider for React Native apps. Manages a hidden WebView with Fathom's tracking script. + +```tsx +import { NativeFathomProvider } from 'react-fathom/native' + + + + +``` + +### Props + +| Prop | Type | Required | Description | +|------|------|----------|-------------| +| `siteId` | `string` | Yes | Your Fathom site ID | +| `loadOptions` | `LoadOptions` | No | Options passed to `fathom.load()` | +| `scriptDomain` | `string` | No | Custom domain (default: `cdn.usefathom.com`) | +| `defaultPageviewOptions` | `PageViewOptions` | No | Default pageview options | +| `defaultEventOptions` | `EventOptions` | No | Default event options | +| `trackAppState` | `boolean` | No | Enable automatic app state tracking | +| `debug` | `boolean` | No | Enable debug logging | +| `onReady` | `() => void` | No | Called when Fathom script loads | +| `onError` | `(error: string) => void` | No | Called on script load error | +| `clientRef` | `MutableRefObject` | No | Ref for direct client access | +| `children` | `ReactNode` | Yes | Child components | + +See the [React Native guide](/react-native) for detailed usage. diff --git a/docs/content/contributing.mdx b/docs/content/contributing.mdx new file mode 100644 index 0000000..6d9fe79 --- /dev/null +++ b/docs/content/contributing.mdx @@ -0,0 +1,180 @@ +# Contributing + +Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or examples. + +## Ways to Contribute + +| Type | Description | +|------|-------------| +| **Bug Reports** | Found a bug? [Open an issue](https://github.com/ryanhefner/react-fathom/issues/new) with reproduction steps | +| **Feature Requests** | Have an idea? Discuss it in an issue first | +| **Bug Fixes** | PRs for documented issues are always welcome | +| **Documentation** | Help improve docs, add examples, fix typos | +| **Tests** | Increase test coverage or add edge case tests | + +## Development Setup + +### Prerequisites + +- Node.js 18+ +- npm 9+ + +### 1. Clone and Install + +```bash +git clone https://github.com/ryanhefner/react-fathom.git +cd react-fathom +npm install +``` + +### 2. Development Workflow + +```bash +# Run tests in watch mode during development +npm run test:watch + +# Run the full test suite +npm test + +# Build the package +npm run build + +# Type check without emitting +npm run typecheck +``` + +## Project Structure + +``` +react-fathom/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ index.ts # Main entry point +β”‚ β”œβ”€β”€ FathomProvider.tsx # Core provider component +β”‚ β”œβ”€β”€ FathomContext.tsx # React context +β”‚ β”œβ”€β”€ types.ts # TypeScript definitions +β”‚ β”œβ”€β”€ hooks/ # React hooks +β”‚ β”‚ β”œβ”€β”€ useFathom.ts +β”‚ β”‚ β”œβ”€β”€ useTrackOnClick.ts +β”‚ β”‚ β”œβ”€β”€ useTrackOnMount.ts +β”‚ β”‚ └── useTrackOnVisible.ts +β”‚ β”œβ”€β”€ components/ # Declarative tracking components +β”‚ β”‚ β”œβ”€β”€ TrackClick.tsx +β”‚ β”‚ β”œβ”€β”€ TrackPageview.tsx +β”‚ β”‚ └── TrackVisible.tsx +β”‚ β”œβ”€β”€ next/ # Next.js-specific exports +β”‚ β”‚ └── index.ts +β”‚ └── native/ # React Native exports +β”‚ β”œβ”€β”€ index.ts +β”‚ β”œβ”€β”€ FathomWebView.tsx +β”‚ β”œβ”€β”€ createWebViewClient.ts +β”‚ β”œβ”€β”€ NativeFathomProvider.tsx +β”‚ β”œβ”€β”€ useNavigationTracking.ts +β”‚ └── useAppStateTracking.ts +β”œβ”€β”€ docs/ # Documentation site (Nextra) +└── dist/ # Built output (generated) +``` + +## Testing Guidelines + +We use [Vitest](https://vitest.dev/) for testing. All new features should include tests. + +```bash +# Run all tests +npm test + +# Run tests in watch mode +npm run test:watch + +# Run tests with coverage +npm run test:coverage +``` + +### Writing Tests + +```tsx +// src/hooks/useMyHook.test.tsx +import { renderHook } from '@testing-library/react' +import { describe, it, expect, vi } from 'vitest' +import { useMyHook } from './useMyHook' +import { FathomProvider } from '../FathomProvider' + +describe('useMyHook', () => { + it('should track events correctly', () => { + const mockClient = { + load: vi.fn(), + trackPageview: vi.fn(), + trackEvent: vi.fn(), + trackGoal: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const wrapper = ({ children }) => ( + {children} + ) + + const { result } = renderHook(() => useMyHook(), { wrapper }) + + result.current.doSomething() + + expect(mockClient.trackEvent).toHaveBeenCalledWith('expected-event', {}) + }) +}) +``` + +## Code Style + +- **TypeScript**: All code should be fully typed +- **Formatting**: We use Prettier (run `npm run format` before committing) +- **Linting**: ESLint catches common issues (run `npm run lint`) +- **Naming**: + - Components: PascalCase (`TrackClick.tsx`) + - Hooks: camelCase with `use` prefix (`useFathom.ts`) + - Types: PascalCase (`FathomClient`) + +## Submitting a Pull Request + +1. **Fork** the repository +2. **Create a branch** from `main`: + ```bash + git checkout -b fix/my-bug-fix + # or + git checkout -b feature/my-new-feature + ``` +3. **Make your changes** with clear, focused commits +4. **Add or update tests** for your changes +5. **Ensure CI passes**: + ```bash + npm run lint + npm test + npm run build + ``` +6. **Submit a PR** with a clear description of what and why + +## Commit Messages + +Use clear, descriptive commit messages: + +``` +feat: add useTrackOnScroll hook for scroll tracking +fix: resolve duplicate pageview tracking in Next.js +docs: add troubleshooting section for ad blockers +test: add tests for native offline queue +refactor: simplify FathomContext default values +``` + +Prefixes: `feat`, `fix`, `docs`, `test`, `refactor`, `chore`, `perf` + +## Documentation + +The docs site uses [Nextra](https://nextra.site) and lives in the `/docs` directory. + +```bash +cd docs +npm install +npm run dev +``` + +When adding new features, please also update the relevant documentation pages. diff --git a/docs/content/getting-started.mdx b/docs/content/getting-started.mdx new file mode 100644 index 0000000..ebea586 --- /dev/null +++ b/docs/content/getting-started.mdx @@ -0,0 +1,101 @@ +# Getting Started + +## Installation + +Install `react-fathom` and its peer dependency `fathom-client`: + +```bash npm2yarn +npm install react-fathom fathom-client +``` + +## Peer Dependencies + +| Package | Version | Required for | +|---------|---------|--------------| +| `react` | >= 16.8 | All | +| `react-dom` | >= 16.8 | Web only | +| `fathom-client` | >= 3.0.0 | Web only (not needed for React Native) | +| `next` | >= 10.0.0 | Next.js providers only | +| `react-native` | >= 0.60.0 | React Native only | +| `react-native-webview` | >= 11.0.0 | React Native only | + +## Basic Setup + +### 1. Get Your Site ID + +Your Fathom site ID is an 8-character alphanumeric string (e.g., `ABCD1234`). Find it in your [Fathom dashboard](https://app.usefathom.com) under **Settings β†’ Site ID**. + +### 2. Add the Provider + +Wrap your application with `FathomProvider`: + +```tsx +import { FathomProvider } from 'react-fathom' + +function App() { + return ( + + + + ) +} +``` + +### 3. Track Events + +Use the `useFathom` hook to track custom events: + +```tsx +import { useFathom } from 'react-fathom' + +function SignUpButton() { + const { trackEvent } = useFathom() + + return ( + + ) +} +``` + +Pageviews are tracked automatically by default. + +## Next Steps + +- [React usage guide](/react) β€” Hooks and declarative components +- [Next.js integration](/nextjs) β€” App Router and Pages Router +- [React Native setup](/react-native) β€” Mobile apps with offline queuing +- [API Reference](/api/providers) β€” Full provider props and options + +## Environment Variables + +For production apps, store your site ID in an environment variable: + +```tsx + +``` + +For Next.js, use the `NEXT_PUBLIC_` prefix: + +```bash +# .env.local +NEXT_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID +``` + +```tsx + +``` + +## Local Development + +By default, Fathom only tracks events from configured domains. To track during local development: + +```tsx + +``` diff --git a/docs/content/guides/_meta.ts b/docs/content/guides/_meta.ts new file mode 100644 index 0000000..8ba2fd5 --- /dev/null +++ b/docs/content/guides/_meta.ts @@ -0,0 +1,6 @@ +export default { + 'default-options': 'Default Options', + 'custom-client': 'Custom Client', + testing: 'Testing', + 'custom-domains': 'Custom Domains', +} diff --git a/docs/content/guides/custom-client.mdx b/docs/content/guides/custom-client.mdx new file mode 100644 index 0000000..6148600 --- /dev/null +++ b/docs/content/guides/custom-client.mdx @@ -0,0 +1,182 @@ +# Custom Client + +Provide a custom Fathom client implementation for testing, SSR, or custom analytics pipelines. + +## FathomClient Interface + +Your custom client must implement the `FathomClient` interface: + +```tsx +import type { FathomClient, EventOptions, LoadOptions, PageViewOptions } from 'react-fathom' + +const myCustomClient: FathomClient = { + load: (siteId: string, options?: LoadOptions) => { + // Initialize your tracking + }, + trackPageview: (opts?: PageViewOptions) => { + // Track pageview + }, + trackEvent: (eventName: string, opts?: EventOptions) => { + // Track custom event + }, + trackGoal: (code: string, cents: number) => { + // Track goal conversion + }, + setSite: (id: string) => { + // Change site ID + }, + blockTrackingForMe: () => { + // Block tracking + }, + enableTrackingForMe: () => { + // Enable tracking + }, + isTrackingEnabled: () => { + // Return tracking status + return true + }, +} +``` + +## Using a Custom Client + +Pass your client to `FathomProvider`: + +```tsx + + + +``` + +When using a custom client, you don't need to provide `siteId` (unless your client needs it). + +## Use Cases + +### Console Logger (Development) + +Log all tracking calls to the console: + +```tsx +const consoleClient: FathomClient = { + load: (id, opts) => console.log('[Fathom] load:', id, opts), + trackPageview: (opts) => console.log('[Fathom] pageview:', opts), + trackEvent: (name, opts) => console.log('[Fathom] event:', name, opts), + trackGoal: (code, cents) => console.log('[Fathom] goal:', code, cents), + setSite: (id) => console.log('[Fathom] setSite:', id), + blockTrackingForMe: () => console.log('[Fathom] blocked'), + enableTrackingForMe: () => console.log('[Fathom] enabled'), + isTrackingEnabled: () => true, +} + +// Use in development + +``` + +### No-op Client (SSR) + +Prevent errors during server-side rendering: + +```tsx +const noopClient: FathomClient = { + load: () => {}, + trackPageview: () => {}, + trackEvent: () => {}, + trackGoal: () => {}, + setSite: () => {}, + blockTrackingForMe: () => {}, + enableTrackingForMe: () => {}, + isTrackingEnabled: () => false, +} + +// Use on server +const client = typeof window === 'undefined' ? noopClient : undefined + + +``` + +### Analytics Aggregator + +Forward events to multiple analytics services: + +```tsx +import * as fathom from 'fathom-client' +import mixpanel from 'mixpanel-browser' + +const aggregatorClient: FathomClient = { + load: (siteId, options) => { + fathom.load(siteId, options) + mixpanel.init('YOUR_MIXPANEL_TOKEN') + }, + trackPageview: (opts) => { + fathom.trackPageview(opts) + mixpanel.track('Page View', { url: opts?.url }) + }, + trackEvent: (name, opts) => { + fathom.trackEvent(name, opts) + mixpanel.track(name, opts) + }, + trackGoal: (code, cents) => { + fathom.trackGoal(code, cents) + mixpanel.track('Goal', { code, value: cents / 100 }) + }, + setSite: fathom.setSite, + blockTrackingForMe: fathom.blockTrackingForMe, + enableTrackingForMe: fathom.enableTrackingForMe, + isTrackingEnabled: fathom.isTrackingEnabled, +} +``` + +### Event Transformer + +Transform events before sending: + +```tsx +import * as fathom from 'fathom-client' + +const transformerClient: FathomClient = { + load: fathom.load, + trackPageview: fathom.trackPageview, + trackEvent: (name, opts) => { + // Prefix all event names + const prefixedName = `app_${name}` + + // Add timestamp to all events + const enrichedOpts = { + ...opts, + _timestamp: Date.now(), + } + + fathom.trackEvent(prefixedName, enrichedOpts) + }, + trackGoal: fathom.trackGoal, + setSite: fathom.setSite, + blockTrackingForMe: fathom.blockTrackingForMe, + enableTrackingForMe: fathom.enableTrackingForMe, + isTrackingEnabled: fathom.isTrackingEnabled, +} +``` + +## Accessing the Client + +Use `clientRef` to access the resolved client from a parent component: + +```tsx +import { useRef } from 'react' +import { FathomProvider, FathomClient } from 'react-fathom' + +function App() { + const clientRef = useRef(null) + + const handleExternalEvent = () => { + // Access client directly without hooks + clientRef.current?.trackEvent('external-event') + } + + return ( + + + + + ) +} +``` diff --git a/docs/content/guides/custom-domains.mdx b/docs/content/guides/custom-domains.mdx new file mode 100644 index 0000000..f7abaf6 --- /dev/null +++ b/docs/content/guides/custom-domains.mdx @@ -0,0 +1,133 @@ +# Custom Domains + +Use Fathom's custom domains feature to bypass ad blockers and improve tracking accuracy. + +## What Are Custom Domains? + +[Fathom's custom domains](https://usefathom.com/docs/script/custom-domains) let you serve the tracking script from your own domain (e.g., `stats.yourdomain.com`). This helps avoid ad blockers that target `cdn.usefathom.com`. + +## Setup in Fathom + +1. Go to your [Fathom dashboard](https://app.usefathom.com) +2. Navigate to **Settings β†’ Custom Domains** +3. Add your custom domain (e.g., `stats.yourdomain.com`) +4. Configure DNS as instructed +5. Wait for SSL certificate provisioning + +## Using Custom Domains + +### React / Next.js + +Pass the custom domain in `clientOptions`: + +```tsx + +``` + +### React Native + +Use the `scriptDomain` prop: + +```tsx + + + +``` + +Or with manual WebView setup: + +```tsx + +``` + +## Verifying Setup + +### Check Script Loading + +Open your browser's Network tab and verify the Fathom script loads from your custom domain: + +``` +https://stats.yourdomain.com/script.js +``` + +### Check Tracking Requests + +Tracking requests should also go to your custom domain: + +``` +https://stats.yourdomain.com/... +``` + +## Troubleshooting + +### Script Not Loading + +1. Verify DNS is configured correctly +2. Check SSL certificate is active in Fathom dashboard +3. Ensure the domain matches exactly (no trailing slash) + +### CORS Errors + +Custom domains handle CORS automatically. If you see CORS errors: + +1. Verify the custom domain is fully provisioned +2. Check you're using HTTPS +3. Clear browser cache + +### React Native Not Working + +Ensure the WebView can access your custom domain: + +1. Check network connectivity +2. Verify no firewall blocking the domain +3. Use `debug={true}` to see detailed logs + +```tsx + console.error('Load error:', err)} +> +``` + +## Multiple Environments + +Use environment variables to switch domains: + +```tsx +// React / Next.js + +``` + +```bash +# .env.local +NEXT_PUBLIC_FATHOM_SITE_ID=ABCD1234 +NEXT_PUBLIC_FATHOM_SCRIPT_URL=https://stats.yourdomain.com/script.js +``` + +```tsx +// React Native + +``` diff --git a/docs/content/guides/default-options.mdx b/docs/content/guides/default-options.mdx new file mode 100644 index 0000000..d1e9412 --- /dev/null +++ b/docs/content/guides/default-options.mdx @@ -0,0 +1,137 @@ +# Default Options + +Set app-wide defaults that automatically merge into all tracking calls. + +## How It Works + +Default options are spread first, then any options you pass to individual tracking calls override them: + +```tsx + + {/* All trackEvent calls include _site_id: 'my-app' unless overridden */} + +``` + +```tsx +const { trackEvent } = useFathom() + +// Uses default: { _site_id: 'my-app' } +trackEvent('button-click') + +// Merges with default: { _site_id: 'my-app', _value: 100 } +trackEvent('purchase', { _value: 100 }) + +// Overrides default: { _site_id: 'custom-site', _value: 50 } +trackEvent('special-event', { _site_id: 'custom-site', _value: 50 }) +``` + +## Default Pageview Options + +Set defaults for all `trackPageview` calls: + +```tsx + +``` + +## Default Event Options + +Set defaults for all `trackEvent` calls: + +```tsx + +``` + +## Nested Providers + +When nesting providers, child providers inherit defaults from parents but can override them: + +```tsx + + {/* Events here use _site_id: 'global' */} + + + + {/* Events here use _site_id: 'dashboard' */} + + + + + {/* Events here use _site_id: 'settings' */} + + + +``` + +## Use Cases + +### Multi-tenant Apps + +Track which tenant generated each event: + +```tsx +function TenantProvider({ tenantId, children }) { + return ( + + {children} + + ) +} +``` + +### A/B Testing + +Include experiment variant in all events: + +```tsx +function ExperimentProvider({ variant, children }) { + return ( + + {children} + + ) +} +``` + +### Consistent Referrer + +Set a consistent referrer for all pageviews: + +```tsx + +``` + +## Accessing Current Defaults + +The current defaults are available from `useFathom`: + +```tsx +const { defaultPageviewOptions, defaultEventOptions } = useFathom() + +console.log('Current event defaults:', defaultEventOptions) +``` diff --git a/docs/content/guides/testing.mdx b/docs/content/guides/testing.mdx new file mode 100644 index 0000000..a98fd87 --- /dev/null +++ b/docs/content/guides/testing.mdx @@ -0,0 +1,227 @@ +# Testing + +Use mock clients to test tracking behavior without sending real analytics. + +## Mock Client Setup + +Create a mock client with Jest or Vitest: + +```tsx +import { FathomProvider, type FathomClient } from 'react-fathom' + +const mockClient: FathomClient = { + load: vi.fn(), + trackPageview: vi.fn(), + trackEvent: vi.fn(), + trackGoal: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), +} +``` + +## Testing Components + +### Testing Event Tracking + +```tsx +import { render, screen, fireEvent } from '@testing-library/react' +import { FathomProvider, type FathomClient } from 'react-fathom' +import { describe, it, expect, vi, beforeEach } from 'vitest' +import { SignUpButton } from './SignUpButton' + +describe('SignUpButton', () => { + let mockClient: FathomClient + + beforeEach(() => { + mockClient = { + load: vi.fn(), + trackPageview: vi.fn(), + trackEvent: vi.fn(), + trackGoal: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + }) + + it('tracks click event', () => { + render( + + + + ) + + fireEvent.click(screen.getByRole('button')) + + expect(mockClient.trackEvent).toHaveBeenCalledWith('signup-click', {}) + }) + + it('tracks event with value', () => { + render( + + + + ) + + fireEvent.click(screen.getByRole('button')) + + expect(mockClient.trackEvent).toHaveBeenCalledWith('purchase', { _value: 2999 }) + }) +}) +``` + +### Testing Pageview Tracking + +```tsx +import { render } from '@testing-library/react' +import { FathomProvider } from 'react-fathom' +import { describe, it, expect, vi } from 'vitest' +import { Dashboard } from './Dashboard' + +describe('Dashboard', () => { + it('tracks pageview on mount', () => { + const mockClient = { + load: vi.fn(), + trackPageview: vi.fn(), + trackEvent: vi.fn(), + trackGoal: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + + ) + + expect(mockClient.trackPageview).toHaveBeenCalledWith({ url: '/dashboard' }) + }) +}) +``` + +## Test Utilities + +### Create a Reusable Wrapper + +```tsx +// test-utils.tsx +import { ReactNode } from 'react' +import { FathomProvider, type FathomClient } from 'react-fathom' +import { vi } from 'vitest' + +export function createMockClient(): FathomClient { + return { + load: vi.fn(), + trackPageview: vi.fn(), + trackEvent: vi.fn(), + trackGoal: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } +} + +export function createWrapper(client: FathomClient) { + return function Wrapper({ children }: { children: ReactNode }) { + return ( + + {children} + + ) + } +} +``` + +### Usage with renderHook + +```tsx +import { renderHook } from '@testing-library/react' +import { useFathom } from 'react-fathom' +import { createMockClient, createWrapper } from './test-utils' + +describe('useFathom', () => { + it('provides tracking methods', () => { + const mockClient = createMockClient() + + const { result } = renderHook(() => useFathom(), { + wrapper: createWrapper(mockClient), + }) + + result.current.trackEvent('test-event') + + expect(mockClient.trackEvent).toHaveBeenCalledWith('test-event', {}) + }) +}) +``` + +## Testing Default Options + +```tsx +it('merges default options with provided options', () => { + const mockClient = createMockClient() + + render( + + + + ) + + fireEvent.click(screen.getByRole('button')) + + expect(mockClient.trackEvent).toHaveBeenCalledWith('click', { + _site_id: 'test-app', + _value: 100, + }) +}) +``` + +## Testing TrackVisible + +For components that track on visibility, you'll need to mock IntersectionObserver: + +```tsx +import { vi } from 'vitest' + +// Mock IntersectionObserver +const mockIntersectionObserver = vi.fn() +mockIntersectionObserver.mockReturnValue({ + observe: () => null, + unobserve: () => null, + disconnect: () => null, +}) +window.IntersectionObserver = mockIntersectionObserver + +describe('TrackVisible', () => { + it('observes the element', () => { + const mockClient = createMockClient() + + render( + + +
Content
+
+
+ ) + + expect(mockIntersectionObserver).toHaveBeenCalled() + }) +}) +``` + +## Snapshot Testing + +Don't snapshot test analytics callsβ€”they're implementation details. Instead, test that: + +1. The correct events are fired +2. Events include the expected data +3. Events fire at the right times (mount, click, visibility) diff --git a/docs/content/index.mdx b/docs/content/index.mdx new file mode 100644 index 0000000..8361293 --- /dev/null +++ b/docs/content/index.mdx @@ -0,0 +1,77 @@ +# react-fathom + +**Privacy-focused analytics for React, Next.js, and React Native.** + +Easily integrate [Fathom Analytics](https://usefathom.com/ref/EKONBS) into your applications with automatic pageview tracking, custom event tracking, and full TypeScript support. + +## Why Fathom? + +[Fathom Analytics](https://usefathom.com/ref/EKONBS) is a privacy-focused alternative to Google Analytics. Unlike traditional analytics platforms: + +- **No cookies required** β€” GDPR, CCPA, and PECR compliant out of the box +- **No personal data collection** β€” Respects user privacy by design +- **No consent banners needed** β€” Simplified compliance for your websites +- **Fast and lightweight** β€” Won't slow down your site + +**New to Fathom?** Get a **$10 credit** when you sign up using [this referral link](https://usefathom.com/ref/EKONBS). + +## Why react-fathom? + +The official `fathom-client` works, but react-fathom provides a better developer experience: + +| Problem | react-fathom solution | +|---------|----------------------| +| Web onlyβ€”no React Native | **Full React Native support** with offline event queuing | +| Next.js App Router requires boilerplate | **`NextFathomProviderApp`** works directly in Server Component layouts | +| Imperative API only | **Hooks** (`useFathom`, `useTrackOnMount`, `useTrackOnVisible`) and **declarative components** (``, ``) | +| No tree-shaking | **Fully tree-shakeable**β€”bundle only what you use | + +## Features + +- **Zero-config** Fathom Analytics integration for React +- **Tree-shakeable** β€” Only bundle what you use +- **Automatic pageview tracking** for Next.js (Pages Router & App Router) +- **React Native support** with offline queuing and navigation tracking +- **Full TypeScript** support with type definitions +- **Flexible** β€” Works with any React app, Next.js, or React Native +- **Lightweight** β€” Minimal bundle size impact + +## Quick Start + +```bash +npm install react-fathom fathom-client +``` + +```tsx +import { FathomProvider } from 'react-fathom' + +function App() { + return ( + + + + ) +} +``` + +```tsx +import { useFathom } from 'react-fathom' + +function MyComponent() { + const { trackEvent } = useFathom() + + return ( + + ) +} +``` + +That's it! Pageviews are tracked automatically. + +## Platform Guides + +- [React](/react) β€” Basic React setup with hooks and components +- [Next.js](/nextjs) β€” App Router and Pages Router integration +- [React Native](/react-native) β€” Mobile apps with offline queuing diff --git a/docs/content/nextjs/_meta.ts b/docs/content/nextjs/_meta.ts new file mode 100644 index 0000000..c22058c --- /dev/null +++ b/docs/content/nextjs/_meta.ts @@ -0,0 +1,5 @@ +export default { + index: 'Overview', + 'app-router': 'App Router', + 'pages-router': 'Pages Router', +} diff --git a/docs/content/nextjs/app-router.mdx b/docs/content/nextjs/app-router.mdx new file mode 100644 index 0000000..4c8b62f --- /dev/null +++ b/docs/content/nextjs/app-router.mdx @@ -0,0 +1,217 @@ +# App Router + +This guide covers integrating react-fathom with Next.js 13.4+ using the App Router (`app/` directory). + +## Recommended Setup + +Use `NextFathomProviderApp` for the simplest setup. It combines the provider and automatic route tracking: + +```tsx filename="app/layout.tsx" +import { NextFathomProviderApp } from 'react-fathom/next' + +export default function RootLayout({ children }) { + return ( + + + + {children} + + + + ) +} +``` + +That's it! Pageviews are now tracked automatically on every route change. + +## How It Works + +`NextFathomProviderApp` is a Client Component (marked with `'use client'`) that: + +1. Wraps your app with `FathomProvider` +2. Includes `NextFathomTrackViewApp` for automatic route tracking +3. Can be used directly in Server Component layouts + +## Configuration Options + +```tsx filename="app/layout.tsx" + + {children} + +``` + +### Props + +| Prop | Type | Description | +|------|------|-------------| +| `siteId` | `string` | Your Fathom site ID | +| `clientOptions` | `LoadOptions` | Options passed to `fathom-client` | +| `defaultPageviewOptions` | `PageViewOptions` | Default options for all pageviews | +| `defaultEventOptions` | `EventOptions` | Default options for all events | +| `disableAutoTrack` | `boolean` | Disable automatic route tracking | +| `children` | `ReactNode` | Your app content | + +## Alternative: Manual Setup + +If you need more control, use `FathomProvider` with `NextFathomTrackViewApp` separately: + +```tsx filename="app/layout.tsx" +import { FathomProvider } from 'react-fathom' +import { NextFathomTrackViewApp } from 'react-fathom/next' + +export default function RootLayout({ children }) { + return ( + + + + + {children} + + + + ) +} +``` + +import { Callout } from 'nextra/components' + + + Since `FathomProvider` uses React hooks, you may need to wrap it in your own Client Component when using this approach directly in a Server Component layout. + + +## Custom Client Component Wrapper + +For advanced setups, create your own client component: + +```tsx filename="components/AnalyticsProvider.tsx" +'use client' + +import { FathomProvider } from 'react-fathom' +import { NextFathomTrackViewApp } from 'react-fathom/next' + +export function AnalyticsProvider({ children }) { + return ( + + + {children} + + ) +} +``` + +```tsx filename="app/layout.tsx" +import { AnalyticsProvider } from '@/components/AnalyticsProvider' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +## Tracking Events + +Use hooks and components in any Client Component: + +```tsx filename="components/SignUpButton.tsx" +'use client' + +import { useFathom } from 'react-fathom' + +export function SignUpButton() { + const { trackEvent } = useFathom() + + return ( + + ) +} +``` + +Or use declarative components: + +```tsx filename="components/CTASection.tsx" +'use client' + +import { TrackClick, TrackVisible } from 'react-fathom' + +export function CTASection() { + return ( + +
+

Ready to get started?

+ + + +
+
+ ) +} +``` + +## Disabling Auto-Tracking + +To handle pageview tracking manually: + +```tsx + + {children} + +``` + +Then track pageviews where needed: + +```tsx +'use client' + +import { useFathom } from 'react-fathom' +import { usePathname } from 'next/navigation' +import { useEffect } from 'react' + +function CustomTracker() { + const { trackPageview } = useFathom() + const pathname = usePathname() + + useEffect(() => { + // Custom logic before tracking + if (pathname !== '/excluded-page') { + trackPageview({ url: pathname }) + } + }, [pathname, trackPageview]) + + return null +} +``` + +## Avoiding Duplicate Pageviews + +If you see duplicate pageviews, you likely have multiple tracking sources: + +```tsx +// WRONG: Both auto tracking AND fathom-client's auto + + {/* NextFathomTrackViewApp tracks pageviews */} + {/* AND clientOptions.auto defaults to true */} + + +// CORRECT: Disable fathom-client's built-in auto tracking + + {children} + +``` diff --git a/docs/content/nextjs/index.mdx b/docs/content/nextjs/index.mdx new file mode 100644 index 0000000..cb2a97a --- /dev/null +++ b/docs/content/nextjs/index.mdx @@ -0,0 +1,63 @@ +# Next.js + +react-fathom provides first-class support for Next.js with automatic pageview tracking on route changes. + +## Which Router Are You Using? + +Next.js has two routing systems. Choose the guide that matches your project: + +import { Cards, Card } from 'nextra/components' + + + + Next.js 13.4+ with the `app/` directory. Uses Server Components and the new routing system. + + + Traditional Next.js with the `pages/` directory. Works with all Next.js versions. + + + +## Quick Comparison + +| Feature | App Router | Pages Router | +|---------|-----------|--------------| +| Provider | `NextFathomProviderApp` | `FathomProvider` | +| Route tracking | `NextFathomTrackViewApp` (included) | `NextFathomTrackViewPages` | +| Setup location | `app/layout.tsx` | `pages/_app.tsx` | +| Server Components | Yes (provider is a Client Component) | N/A | + +## Import Path + +All Next.js-specific exports come from `react-fathom/next`: + +```tsx +import { + NextFathomProviderApp, + NextFathomTrackViewApp, + NextFathomTrackViewPages, +} from 'react-fathom/next' +``` + +The base `FathomProvider`, hooks, and components come from `react-fathom`: + +```tsx +import { FathomProvider, useFathom, TrackClick } from 'react-fathom' +``` + +## Environment Variables + +Store your site ID in an environment variable for production: + +```bash filename=".env.local" +NEXT_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID +``` + +```tsx + +``` + +import { Callout } from 'nextra/components' + + + Environment variables must be prefixed with `NEXT_PUBLIC_` to be available on the client side. + diff --git a/docs/content/nextjs/pages-router.mdx b/docs/content/nextjs/pages-router.mdx new file mode 100644 index 0000000..ef5d9ec --- /dev/null +++ b/docs/content/nextjs/pages-router.mdx @@ -0,0 +1,203 @@ +# Pages Router + +This guide covers integrating react-fathom with Next.js using the Pages Router (`pages/` directory). + +## Setup + +Add the provider and route tracker to your `_app.tsx`: + +```tsx filename="pages/_app.tsx" +import { FathomProvider } from 'react-fathom' +import { NextFathomTrackViewPages } from 'react-fathom/next' + +function MyApp({ Component, pageProps }) { + return ( + + + + + ) +} + +export default MyApp +``` + +Pageviews are now tracked automatically on every route change. + +## Configuration Options + +```tsx filename="pages/_app.tsx" +import { FathomProvider } from 'react-fathom' +import { NextFathomTrackViewPages } from 'react-fathom/next' + +function MyApp({ Component, pageProps }) { + return ( + + + + + ) +} + +export default MyApp +``` + +### FathomProvider Props + +| Prop | Type | Description | +|------|------|-------------| +| `siteId` | `string` | Your Fathom site ID | +| `clientOptions` | `LoadOptions` | Options passed to `fathom-client` | +| `defaultPageviewOptions` | `PageViewOptions` | Default options for all pageviews | +| `defaultEventOptions` | `EventOptions` | Default options for all events | + +### NextFathomTrackViewPages Props + +| Prop | Type | Description | +|------|------|-------------| +| `disableAutoTrack` | `boolean` | Disable automatic route tracking | + +## Tracking Events + +Use hooks in any component: + +```tsx filename="components/SignUpButton.tsx" +import { useFathom } from 'react-fathom' + +export function SignUpButton() { + const { trackEvent } = useFathom() + + return ( + + ) +} +``` + +Or use declarative components: + +```tsx filename="components/PricingSection.tsx" +import { TrackClick, TrackVisible } from 'react-fathom' + +export function PricingSection() { + return ( + +
+

Pricing

+ + + +
+
+ ) +} +``` + +## Page-Specific Tracking + +Track custom data on specific pages: + +```tsx filename="pages/product/[id].tsx" +import { useTrackOnMount } from 'react-fathom' +import { useRouter } from 'next/router' + +export default function ProductPage({ product }) { + const router = useRouter() + + useTrackOnMount({ + url: `/product/${router.query.id}`, + referrer: document.referrer, + }) + + return ( +
+

{product.name}

+ {/* ... */} +
+ ) +} +``` + +## Disabling Auto-Tracking + +To handle pageview tracking manually: + +```tsx filename="pages/_app.tsx" + + + + +``` + +Then implement your own tracking logic: + +```tsx filename="components/CustomTracker.tsx" +import { useFathom } from 'react-fathom' +import { useRouter } from 'next/router' +import { useEffect } from 'react' + +export function CustomTracker() { + const { trackPageview } = useFathom() + const router = useRouter() + + useEffect(() => { + const handleRouteChange = (url) => { + // Custom logic before tracking + if (!url.startsWith('/admin')) { + trackPageview({ url }) + } + } + + router.events.on('routeChangeComplete', handleRouteChange) + return () => { + router.events.off('routeChangeComplete', handleRouteChange) + } + }, [router.events, trackPageview]) + + return null +} +``` + +## Avoiding Duplicate Pageviews + +If you see duplicate pageviews, disable `fathom-client`'s built-in auto tracking: + +```tsx + + + + +``` + +## TypeScript + +The Pages Router setup is fully typed: + +```tsx filename="pages/_app.tsx" +import type { AppProps } from 'next/app' +import { FathomProvider } from 'react-fathom' +import { NextFathomTrackViewPages } from 'react-fathom/next' + +function MyApp({ Component, pageProps }: AppProps) { + return ( + + + + + ) +} + +export default MyApp +``` diff --git a/docs/content/react-native/_meta.ts b/docs/content/react-native/_meta.ts new file mode 100644 index 0000000..649ef4e --- /dev/null +++ b/docs/content/react-native/_meta.ts @@ -0,0 +1,6 @@ +export default { + index: 'Overview', + navigation: 'Navigation Tracking', + 'app-state': 'App State Tracking', + advanced: 'Advanced Setup', +} diff --git a/docs/content/react-native/advanced.mdx b/docs/content/react-native/advanced.mdx new file mode 100644 index 0000000..719d4ee --- /dev/null +++ b/docs/content/react-native/advanced.mdx @@ -0,0 +1,172 @@ +# Advanced Setup + +For advanced use cases, you can manually control the WebView and client. + +## Manual WebView Setup + +If you need full control over the WebView lifecycle: + +```tsx +import { useRef, useMemo, useCallback } from 'react' +import { + FathomWebView, + createWebViewClient, + FathomProvider, + type FathomWebViewRef, +} from 'react-fathom/native' + +function App() { + const webViewRef = useRef(null) + + const client = useMemo( + () => createWebViewClient(() => webViewRef.current, { debug: __DEV__ }), + [] + ) + + const handleReady = useCallback(() => { + client.setWebViewReady() + }, [client]) + + return ( + + + + + ) +} +``` + +## FathomWebView Props + +| Prop | Type | Description | +|------|------|-------------| +| `siteId` | `string` | Your Fathom site ID (required) | +| `loadOptions` | `LoadOptions` | Options passed to `fathom.load()` | +| `scriptDomain` | `string` | Custom domain (default: `cdn.usefathom.com`) | +| `onReady` | `() => void` | Called when script loads | +| `onError` | `(error: string) => void` | Called on error | +| `debug` | `boolean` | Enable debug logging | + +## FathomWebView Ref Methods + +Access these methods via the ref: + +| Method | Description | +|--------|-------------| +| `trackPageview(opts?)` | Track a pageview | +| `trackEvent(name, opts?)` | Track a custom event | +| `trackGoal(code, cents)` | Track a goal conversion | +| `blockTrackingForMe()` | Block tracking for user | +| `enableTrackingForMe()` | Enable tracking for user | +| `isReady()` | Check if WebView is ready | + +## createWebViewClient Options + +```tsx +const client = createWebViewClient(getWebViewRef, { + debug: true, // Enable debug logging + enableQueue: true, // Queue commands before ready (default: true) + maxQueueSize: 100, // Max queued commands (default: 100) +}) +``` + +### Client Methods + +The client includes additional methods for queue management: + +| Method | Description | +|--------|-------------| +| `processQueue()` | Manually process queued commands | +| `getQueueLength()` | Get current queue length | +| `setWebViewReady()` | Signal that WebView is ready | + +## Custom Domains + +If you use [Fathom's custom domains](https://usefathom.com/docs/script/custom-domains): + +```tsx + + + +``` + +Or with manual setup: + +```tsx + +``` + +## Parent Access via clientRef + +Access the client directly from a parent component: + +```tsx +import { useRef } from 'react' +import { NativeFathomProvider, WebViewFathomClient } from 'react-fathom/native' + +function App() { + const clientRef = useRef(null) + + const handleDeepLink = (url: string) => { + // Track from parent before provider is mounted in children + clientRef.current?.trackEvent('deep_link', { _url: url }) + + // Check queue status + console.log('Queued events:', clientRef.current?.getQueueLength()) + } + + return ( + + + + ) +} +``` + +## Debugging + +Enable debug mode to see all tracking activity: + +```tsx + console.log('WebView ready')} + onError={(err) => console.error('WebView error:', err)} +> +``` + +Debug mode logs: +- When events are queued +- When the queue is flushed +- All tracking calls +- Any errors + +## Offline Behavior + +Events are automatically queued when: +- The WebView hasn't loaded yet +- Network is unavailable (events stay in queue) + +The queue: +- Has a max size (default: 100 events) +- Is processed in order when WebView becomes ready +- Persists only in memory (not across app restarts) + +import { Callout } from 'nextra/components' + + + The queue does not persist across app restarts. Events queued when the app is killed will be lost. + diff --git a/docs/content/react-native/app-state.mdx b/docs/content/react-native/app-state.mdx new file mode 100644 index 0000000..41d996b --- /dev/null +++ b/docs/content/react-native/app-state.mdx @@ -0,0 +1,114 @@ +# App State Tracking + +Track when users foreground or background your app. + +## Automatic Tracking + +Enable with the `trackAppState` prop: + +```tsx + + + +``` + +This tracks: +- `app-foreground` when the app becomes active +- `app-background` when the app goes to background + +## Custom Hook + +For more control, use the `useAppStateTracking` hook: + +```tsx +import { useAppStateTracking } from 'react-fathom/native' + +function AppStateTracker() { + useAppStateTracking({ + foregroundEventName: 'app-resumed', + backgroundEventName: 'app-paused', + onStateChange: (state) => { + console.log('App state changed:', state) + }, + }) + + return null +} +``` + +## Options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `foregroundEventName` | `string` | `'app-foreground'` | Event name for foreground | +| `backgroundEventName` | `string` | `'app-background'` | Event name for background | +| `eventOptions` | `EventOptions` | `undefined` | Additional options for events | +| `onStateChange` | `(state) => void` | `undefined` | Callback on state change | + +## State Values + +The `onStateChange` callback receives one of: + +| State | Description | +|-------|-------------| +| `'active'` | App is in the foreground and interactive | +| `'background'` | App is in the background | +| `'inactive'` | App is transitioning (iOS only) | + +## Examples + +### Track with value + +```tsx +useAppStateTracking({ + foregroundEventName: 'session-resumed', + eventOptions: { _value: 1 }, +}) +``` + +### Custom logic on state change + +```tsx +useAppStateTracking({ + onStateChange: (state) => { + if (state === 'background') { + // Save draft, pause audio, etc. + saveDraft() + } + if (state === 'active') { + // Refresh data + refetchData() + } + }, +}) +``` + +### Conditional tracking + +```tsx +function ConditionalAppStateTracker({ isLoggedIn }) { + useAppStateTracking({ + foregroundEventName: isLoggedIn ? 'user-resumed' : 'visitor-resumed', + backgroundEventName: isLoggedIn ? 'user-paused' : 'visitor-paused', + }) + + return null +} +``` + +## Combining with Provider + +You can use both the provider's `trackAppState` and the hook together if you want different event names: + +```tsx +// Provider tracks default events + + {/* Hook tracks custom events */} + + +``` + +However, this will fire two events per state change. Usually you want one or the other. diff --git a/docs/content/react-native/index.mdx b/docs/content/react-native/index.mdx new file mode 100644 index 0000000..2bbc09a --- /dev/null +++ b/docs/content/react-native/index.mdx @@ -0,0 +1,95 @@ +# React Native + +react-fathom provides full React Native support with a hidden WebView that loads Fathom's official tracking script, ensuring compatibility with Fathom Analytics. + +import { Callout } from 'nextra/components' + + + React Native support uses a WebView-based approach. Events are queued until the WebView is ready, then sent automatically. + + +## Installation + +Install react-fathom and the required WebView dependency: + +```bash +npm install react-fathom react-native-webview +``` + +For iOS, install CocoaPods dependencies: + +```bash +cd ios && pod install +``` + +## Basic Setup + +```tsx +import { NativeFathomProvider } from 'react-fathom/native' + +function App() { + return ( + console.log('Fathom ready!')} + onError={(err) => console.error('Fathom error:', err)} + > + + + ) +} +``` + +## How It Works + +The `NativeFathomProvider`: + +1. Renders a hidden WebView (0x0 pixels) that loads the Fathom script +2. Queues all tracking calls until the WebView is ready +3. Automatically flushes the queue when the script loads +4. Provides the same `useFathom` hook API as web + +## Tracking Events + +Use the same hooks as web: + +```tsx +import { useFathom } from 'react-fathom/native' + +function PurchaseButton({ price }) { + const { trackEvent, trackGoal } = useFathom() + + const handlePurchase = () => { + trackEvent('purchase-tap', { _value: price }) + trackGoal('PURCHASE', price) + } + + return ( + + Buy Now + + ) +} +``` + +## Provider Props + +| Prop | Type | Description | +|------|------|-------------| +| `siteId` | `string` | Your Fathom site ID (required) | +| `debug` | `boolean` | Enable debug logging | +| `trackAppState` | `boolean` | Auto-track app foreground/background | +| `loadOptions` | `LoadOptions` | Options passed to `fathom.load()` | +| `scriptDomain` | `string` | Custom domain (default: `cdn.usefathom.com`) | +| `defaultPageviewOptions` | `PageViewOptions` | Default pageview options | +| `defaultEventOptions` | `EventOptions` | Default event options | +| `onReady` | `() => void` | Called when Fathom script loads | +| `onError` | `(error: string) => void` | Called on script load error | +| `clientRef` | `MutableRefObject` | Ref for direct client access | + +## Next Steps + +- [Navigation Tracking](/react-native/navigation) β€” Track React Navigation screen changes +- [App State Tracking](/react-native/app-state) β€” Track foreground/background +- [Advanced Setup](/react-native/advanced) β€” Custom WebView configuration diff --git a/docs/content/react-native/navigation.mdx b/docs/content/react-native/navigation.mdx new file mode 100644 index 0000000..253e25b --- /dev/null +++ b/docs/content/react-native/navigation.mdx @@ -0,0 +1,161 @@ +# Navigation Tracking + +Track React Navigation screen changes as pageviews. + +## Setup + +```tsx +import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native' +import { NativeFathomProvider, useNavigationTracking } from 'react-fathom/native' + +function App() { + const navigationRef = useNavigationContainerRef() + + return ( + + + + + + + ) +} + +function NavigationTracker({ navigationRef }) { + useNavigationTracking({ + navigationRef, + }) + return null +} +``` + +## Options + +```tsx +useNavigationTracking({ + navigationRef, + + // Transform route names before tracking + transformRouteName: (name) => `/screens/${name.toLowerCase()}`, + + // Filter which routes to track + shouldTrackRoute: (name, params) => !name.startsWith('Modal'), + + // Include route params in tracked URL + includeParams: true, +}) +``` + +### Option Reference + +| Option | Type | Description | +|--------|------|-------------| +| `navigationRef` | `RefObject` | React Navigation container ref (required) | +| `transformRouteName` | `(name: string) => string` | Transform route names before tracking | +| `shouldTrackRoute` | `(name: string, params?: object) => boolean` | Filter which routes to track | +| `includeParams` | `boolean` | Include route params in URL (default: `false`) | + +## Transform Examples + +### Prefix with app name + +```tsx +transformRouteName: (name) => `/myapp/${name}` + +// "Home" β†’ "/myapp/Home" +// "Settings" β†’ "/myapp/Settings" +``` + +### Convert to kebab-case URLs + +```tsx +transformRouteName: (name) => { + return '/' + name + .replace(/([a-z])([A-Z])/g, '$1-$2') + .toLowerCase() +} + +// "UserProfile" β†’ "/user-profile" +// "SettingsScreen" β†’ "/settings-screen" +``` + +### Add hierarchy from nested navigators + +```tsx +transformRouteName: (name, params) => { + const tab = params?.tab || 'main' + return `/${tab}/${name.toLowerCase()}` +} + +// With params { tab: 'profile' } +// "EditProfile" β†’ "/profile/editprofile" +``` + +## Filtering Routes + +### Skip modal screens + +```tsx +shouldTrackRoute: (name) => !name.includes('Modal') +``` + +### Only track specific screens + +```tsx +const trackedScreens = ['Home', 'Profile', 'Settings', 'Product'] + +shouldTrackRoute: (name) => trackedScreens.includes(name) +``` + +### Skip screens with certain params + +```tsx +shouldTrackRoute: (name, params) => { + // Don't track preview mode + if (params?.preview) return false + return true +} +``` + +## Full Example + +```tsx +import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native' +import { createNativeStackNavigator } from '@react-navigation/native-stack' +import { NativeFathomProvider, useNavigationTracking } from 'react-fathom/native' + +const Stack = createNativeStackNavigator() + +function NavigationTracker({ navigationRef }) { + useNavigationTracking({ + navigationRef, + transformRouteName: (name) => `/app/${name.toLowerCase()}`, + shouldTrackRoute: (name) => { + // Skip auth screens and modals + const skipScreens = ['Login', 'Register', 'ForgotPassword'] + return !skipScreens.includes(name) && !name.includes('Modal') + }, + }) + return null +} + +export default function App() { + const navigationRef = useNavigationContainerRef() + + return ( + + + + + + + + + + + ) +} +``` diff --git a/docs/content/react.mdx b/docs/content/react.mdx new file mode 100644 index 0000000..11f58d0 --- /dev/null +++ b/docs/content/react.mdx @@ -0,0 +1,217 @@ +# React + +This guide covers using react-fathom in standard React applications. For Next.js or React Native, see the dedicated guides. + +## Setup + +```tsx +import { FathomProvider } from 'react-fathom' + +function App() { + return ( + + + + ) +} +``` + +## Using the Hook + +The `useFathom` hook provides access to all tracking methods: + +```tsx +import { useFathom } from 'react-fathom' + +function MyComponent() { + const { trackPageview, trackEvent, trackGoal } = useFathom() + + const handleSignUp = () => { + trackEvent('signup-click', { _value: 100 }) // Optional: value in cents + } + + const handlePurchase = () => { + trackGoal('GOALCODE', 2999) // $29.99 in cents + } + + return ( + <> + + + + ) +} +``` + +### Available Methods + +| Method | Description | +|--------|-------------| +| `trackPageview(options?)` | Track a pageview | +| `trackEvent(eventName, options?)` | Track a custom event | +| `trackGoal(code, cents)` | Track a goal conversion | +| `load(siteId, options?)` | Load Fathom with a site ID | +| `setSite(siteId)` | Change the site ID | +| `blockTrackingForMe()` | Block tracking for current user | +| `enableTrackingForMe()` | Enable tracking for current user | +| `isTrackingEnabled()` | Check if tracking is enabled | + +## Convenience Hooks + +### useTrackOnMount + +Track a pageview when a component mounts: + +```tsx +import { useTrackOnMount } from 'react-fathom' + +function LandingPage() { + useTrackOnMount({ url: '/landing' }) + + return
Welcome!
+} +``` + +### useTrackOnClick + +Returns a click handler that tracks an event: + +```tsx +import { useTrackOnClick } from 'react-fathom' + +function CTAButton() { + const handleClick = useTrackOnClick({ + eventName: 'cta-click', + _value: 100, // Optional: value in cents + callback: (e) => { + console.log('Tracked!', e) + }, + }) + + return +} +``` + +### useTrackOnVisible + +Returns a ref that tracks an event when the element becomes visible: + +```tsx +import { useTrackOnVisible } from 'react-fathom' + +function HeroSection() { + const ref = useTrackOnVisible({ + eventName: 'hero-viewed', + threshold: 0.5, // 50% visible + callback: (entry) => { + console.log('Hero section visible!', entry) + }, + }) + + return ( +
+

Welcome to Our App

+
+ ) +} +``` + +## Declarative Components + +For a more declarative approach, use tracking components: + +### TrackPageview + +Track a pageview when the component mounts: + +```tsx +import { TrackPageview } from 'react-fathom' + +function Dashboard() { + return ( + +
Dashboard content
+
+ ) +} +``` + +### TrackClick + +Wrap any clickable element to track clicks: + +```tsx +import { TrackClick } from 'react-fathom' + +function SignUpSection() { + return ( + + + + ) +} +``` + +### TrackVisible + +Track when an element becomes visible (using IntersectionObserver): + +```tsx +import { TrackVisible } from 'react-fathom' + +function PricingSection() { + return ( + +
+

Pricing

+ {/* pricing content */} +
+
+ ) +} +``` + +You can customize the wrapper element: + +```tsx + +

Footer content

+
+``` + +## Combining Approaches + +Mix hooks and components based on your needs: + +```tsx +import { useFathom, TrackVisible } from 'react-fathom' + +function ProductPage({ product }) { + const { trackEvent } = useFathom() + + const handleAddToCart = () => { + trackEvent('add-to-cart', { _value: product.price }) + // ... add to cart logic + } + + return ( +
+ +

{product.name}

+

{product.description}

+
+ + +
+ ) +} +``` + +## Next Steps + +- [API Reference: Hooks](/api/hooks) β€” Full hook options +- [API Reference: Components](/api/components) β€” Full component props +- [Default Options Guide](/guides/default-options) β€” Set app-wide defaults +- [Testing Guide](/guides/testing) β€” Mock clients for testing diff --git a/docs/content/troubleshooting.mdx b/docs/content/troubleshooting.mdx new file mode 100644 index 0000000..885c1b2 --- /dev/null +++ b/docs/content/troubleshooting.mdx @@ -0,0 +1,225 @@ +# Troubleshooting + +Common issues and solutions. + +## Events Not Appearing + +### 1. Verify Your Site ID + +Your site ID should match exactly what's in your [Fathom dashboard](https://app.usefathom.com). It's an 8-character alphanumeric string like `ABCD1234`. + +```tsx +// Double-check this value + +``` + +### 2. Check for Ad Blockers + +Many ad blockers and privacy extensions block analytics scripts. To test: + +- Open an incognito/private window with extensions disabled +- Or temporarily whitelist your development domain + +Consider using [custom domains](/guides/custom-domains) to improve tracking reliability. + +### 3. Domain Restrictions + +Fathom only tracks events from configured domains. For local development: + +```tsx + +``` + +### 4. Inspect Network Requests + +Open your browser's Network tab and look for requests to `cdn.usefathom.com`: + +| Symptom | Cause | +|---------|-------| +| No requests | Script isn't loadingβ€”check provider setup | +| Blocked requests | Ad blocker interference | +| Failed requests | Check site ID and domain configuration | + +--- + +## Duplicate Pageviews + +If you see double pageviews, you have multiple tracking sources: + +```tsx +// WRONG: NextFathomTrackViewApp tracks AND clientOptions.auto defaults to true + + + + +// CORRECT: Disable fathom-client's built-in auto tracking + + + +``` + +Same applies to `NextFathomProviderApp`: + +```tsx + + {children} + +``` + +--- + +## Next.js Issues + +### "use client" Errors + +Server Components can't use hooks directly. Use the pre-configured client component: + +```tsx +// app/layout.tsx +import { NextFathomProviderApp } from 'react-fathom/next' + +export default function RootLayout({ children }) { + return ( + + + + {children} + + + + ) +} +``` + +Or create your own client component wrapper: + +```tsx +// components/AnalyticsProvider.tsx +'use client' +import { FathomProvider } from 'react-fathom' + +export function AnalyticsProvider({ children }) { + return ( + + {children} + + ) +} +``` + +### Environment Variables Not Loading + +Ensure your variable is prefixed with `NEXT_PUBLIC_`: + +```bash +# .env.local +NEXT_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID # βœ“ Works client-side +FATHOM_SITE_ID=YOUR_SITE_ID # βœ— Server-only +``` + +--- + +## React Native Issues + +### Events Not Sending + +#### 1. Verify react-native-webview is installed + +```bash +npm install react-native-webview + +# For iOS +cd ios && pod install +``` + +#### 2. Check WebView is ready + +Events are queued until the WebView loads: + +```tsx + console.log('Fathom WebView ready!')} + onError={(err) => console.error('Fathom error:', err)} +> +``` + +#### 3. Verify network connectivity + +The WebView needs network access to load the Fathom script from `cdn.usefathom.com` (or your custom domain). + +#### 4. Check for WebView restrictions + +Some enterprise MDM solutions or app configurations block WebViews from loading external scripts. + +### Queue Not Flushing + +If events stay queued: + +1. Check `onReady` is being called +2. Verify no `onError` is firing +3. Use `debug={true}` to see queue activity + +```tsx +const clientRef = useRef(null) + + + {/* Check queue length */} + {console.log('Queue:', clientRef.current?.getQueueLength())} + +``` + +--- + +## Debugging Tips + +### Enable Debug Logging + +For React Native: + +```tsx + +``` + +### Use a Console Client + +Replace the real client with one that logs everything: + +```tsx +const debugClient = { + load: (id, opts) => console.log('load:', id, opts), + trackPageview: (opts) => console.log('pageview:', opts), + trackEvent: (name, opts) => console.log('event:', name, opts), + trackGoal: (code, cents) => console.log('goal:', code, cents), + setSite: (id) => console.log('setSite:', id), + blockTrackingForMe: () => console.log('blocked'), + enableTrackingForMe: () => console.log('enabled'), + isTrackingEnabled: () => true, +} + + +``` + +### Verify Real-time in Dashboard + +Fathom's dashboard updates in real-time. Open it alongside your app to see events as they're tracked. + +--- + +## Getting Help + +- [Open an issue](https://github.com/ryanhefner/react-fathom/issues) on GitHub +- [Search existing issues](https://github.com/ryanhefner/react-fathom/issues?q=is%3Aissue) for solutions +- [Fathom Analytics docs](https://usefathom.com/docs) for platform-specific questions diff --git a/docs/mdx-components.tsx b/docs/mdx-components.tsx new file mode 100644 index 0000000..f6062b1 --- /dev/null +++ b/docs/mdx-components.tsx @@ -0,0 +1,10 @@ +import { useMDXComponents as getThemeMDXComponents } from 'nextra-theme-docs' + +const themeComponents = getThemeMDXComponents() + +export function useMDXComponents(components?: Record) { + return { + ...themeComponents, + ...components, + } +} diff --git a/docs/next.config.mjs b/docs/next.config.mjs new file mode 100644 index 0000000..ad860ae --- /dev/null +++ b/docs/next.config.mjs @@ -0,0 +1,10 @@ +import nextra from 'nextra' + +const withNextra = nextra() + +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +} + +export default withNextra(nextConfig) diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000..6ffd8a8 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,21 @@ +{ + "name": "react-fathom-docs", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start" + }, + "dependencies": { + "next": "^15.0.0", + "nextra": "^4.2.0", + "nextra-theme-docs": "^4.2.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "typescript": "^5.7.0" + } +} From 654eaa648bd0dc58e873600aa5fe455036f85df4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 11:57:14 +0000 Subject: [PATCH 02/54] Convert docs site from Nextra to Chakra UI - Replace Nextra with custom Chakra UI v3 docs components - Create DocsLayout, Sidebar, TableOfContents, Navbar components - Add MDX components: Callout, CodeBlock, Cards, Steps, Tabs - Set up next-mdx-remote for MDX processing - Add frontmatter support for all content pages - Remove Nextra-specific imports from MDX files - This serves as the prototype for a potential chakra-docs kit --- docs/app/[[...mdxPath]]/page.tsx | 26 ---- docs/app/[[...slug]]/page.tsx | 61 ++++++++ docs/app/color-mode.tsx | 11 ++ docs/app/globals.css | 13 ++ docs/app/layout.tsx | 35 +---- docs/app/provider.tsx | 12 ++ docs/components/docs/Callout.tsx | 65 ++++++++ docs/components/docs/Cards.tsx | 61 ++++++++ docs/components/docs/CodeBlock.tsx | 117 ++++++++++++++ docs/components/docs/ColorModeButton.tsx | 29 ++++ docs/components/docs/DocsLayout.tsx | 90 +++++++++++ docs/components/docs/MDXComponents.tsx | 187 +++++++++++++++++++++++ docs/components/docs/MobileNav.tsx | 119 +++++++++++++++ docs/components/docs/Navbar.tsx | 62 ++++++++ docs/components/docs/Sidebar.tsx | 95 ++++++++++++ docs/components/docs/Steps.tsx | 41 +++++ docs/components/docs/TableOfContents.tsx | 89 +++++++++++ docs/components/docs/Tabs.tsx | 106 +++++++++++++ docs/components/docs/index.ts | 12 ++ docs/content/api/components.mdx | 5 + docs/content/api/hooks.mdx | 5 + docs/content/api/native.mdx | 5 + docs/content/api/providers.mdx | 5 + docs/content/contributing.mdx | 5 + docs/content/getting-started.mdx | 5 + docs/content/guides/custom-client.mdx | 5 + docs/content/guides/default-options.mdx | 5 + docs/content/guides/testing.mdx | 5 + docs/content/index.mdx | 5 + docs/content/nextjs/app-router.mdx | 7 +- docs/content/nextjs/index.mdx | 11 +- docs/content/nextjs/pages-router.mdx | 5 + docs/content/react-native/advanced.mdx | 7 +- docs/content/react-native/app-state.mdx | 5 + docs/content/react-native/index.mdx | 7 +- docs/content/react-native/navigation.mdx | 5 + docs/content/react.mdx | 5 + docs/content/troubleshooting.mdx | 5 + docs/lib/docs.ts | 175 +++++++++++++++++++++ docs/mdx-components.tsx | 10 -- docs/next.config.mjs | 7 +- docs/package.json | 11 +- docs/tsconfig.json | 23 +++ 43 files changed, 1481 insertions(+), 83 deletions(-) delete mode 100644 docs/app/[[...mdxPath]]/page.tsx create mode 100644 docs/app/[[...slug]]/page.tsx create mode 100644 docs/app/color-mode.tsx create mode 100644 docs/app/globals.css create mode 100644 docs/app/provider.tsx create mode 100644 docs/components/docs/Callout.tsx create mode 100644 docs/components/docs/Cards.tsx create mode 100644 docs/components/docs/CodeBlock.tsx create mode 100644 docs/components/docs/ColorModeButton.tsx create mode 100644 docs/components/docs/DocsLayout.tsx create mode 100644 docs/components/docs/MDXComponents.tsx create mode 100644 docs/components/docs/MobileNav.tsx create mode 100644 docs/components/docs/Navbar.tsx create mode 100644 docs/components/docs/Sidebar.tsx create mode 100644 docs/components/docs/Steps.tsx create mode 100644 docs/components/docs/TableOfContents.tsx create mode 100644 docs/components/docs/Tabs.tsx create mode 100644 docs/components/docs/index.ts create mode 100644 docs/lib/docs.ts delete mode 100644 docs/mdx-components.tsx create mode 100644 docs/tsconfig.json diff --git a/docs/app/[[...mdxPath]]/page.tsx b/docs/app/[[...mdxPath]]/page.tsx deleted file mode 100644 index ec073e9..0000000 --- a/docs/app/[[...mdxPath]]/page.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { generateStaticParamsFor, importPage } from 'nextra/pages' -import { useMDXComponents } from '../../mdx-components' - -export const generateStaticParams = generateStaticParamsFor('mdxPath') - -export async function generateMetadata(props: PageProps) { - const params = await props.params - const { metadata } = await importPage(params.mdxPath) - return metadata -} - -type PageProps = { - params: Promise<{ mdxPath?: string[] }> -} - -export default async function Page(props: PageProps) { - const params = await props.params - const { default: MDXContent, toc, metadata } = await importPage(params.mdxPath) - const Wrapper = useMDXComponents().wrapper - - return ( - - - - ) -} diff --git a/docs/app/[[...slug]]/page.tsx b/docs/app/[[...slug]]/page.tsx new file mode 100644 index 0000000..f1372a2 --- /dev/null +++ b/docs/app/[[...slug]]/page.tsx @@ -0,0 +1,61 @@ +import { notFound } from 'next/navigation' +import { MDXRemote } from 'next-mdx-remote/rsc' +import { DocsLayout, MDXComponents } from '@/components/docs' +import { + getDocBySlug, + getAllDocSlugs, + getDocsNav, + extractTOC, + getAdjacentPages, +} from '@/lib/docs' + +export async function generateStaticParams() { + const slugs = getAllDocSlugs() + return slugs.map((slug) => ({ slug })) +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ slug?: string[] }> +}) { + const { slug = [] } = await params + const doc = getDocBySlug(slug) + + if (!doc) { + return { title: 'Not Found' } + } + + return { + title: doc.frontmatter.title, + description: doc.frontmatter.description, + } +} + +export default async function DocPage({ + params, +}: { + params: Promise<{ slug?: string[] }> +}) { + const { slug = [] } = await params + const doc = getDocBySlug(slug) + + if (!doc) { + notFound() + } + + const nav = getDocsNav() + const toc = extractTOC(doc.content) + const adjacentPages = getAdjacentPages(slug) + + return ( + + + + ) +} diff --git a/docs/app/color-mode.tsx b/docs/app/color-mode.tsx new file mode 100644 index 0000000..5911fde --- /dev/null +++ b/docs/app/color-mode.tsx @@ -0,0 +1,11 @@ +'use client' + +import { ThemeProvider } from 'next-themes' + +export function ColorModeProvider({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ) +} diff --git a/docs/app/globals.css b/docs/app/globals.css new file mode 100644 index 0000000..435b2cf --- /dev/null +++ b/docs/app/globals.css @@ -0,0 +1,13 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; +} diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx index 8fa0e2d..2b6f3c3 100644 --- a/docs/app/layout.tsx +++ b/docs/app/layout.tsx @@ -1,10 +1,9 @@ -import { Footer, Layout, Navbar } from 'nextra-theme-docs' -import { Head } from 'nextra/components' -import { getPageMap } from 'nextra/page-map' import type { ReactNode } from 'react' -import 'nextra-theme-docs/style.css' +import type { Metadata } from 'next' +import { Provider } from './provider' +import './globals.css' -export const metadata = { +export const metadata: Metadata = { title: { default: 'react-fathom', template: '%s – react-fathom', @@ -12,31 +11,11 @@ export const metadata = { description: 'Privacy-focused analytics for React, Next.js, and React Native', } -const navbar = ( - react-fathom} - projectLink="https://github.com/ryanhefner/react-fathom" - /> -) - -const footer =
MIT {new Date().getFullYear()} Β© Ryan Hefner
- -export default async function RootLayout({ children }: { children: ReactNode }) { +export default function RootLayout({ children }: { children: ReactNode }) { return ( - - + - - {children} - + {children} ) diff --git a/docs/app/provider.tsx b/docs/app/provider.tsx new file mode 100644 index 0000000..cc2b0c9 --- /dev/null +++ b/docs/app/provider.tsx @@ -0,0 +1,12 @@ +'use client' + +import { ChakraProvider, defaultSystem } from '@chakra-ui/react' +import { ColorModeProvider } from './color-mode' + +export function Provider({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ) +} diff --git a/docs/components/docs/Callout.tsx b/docs/components/docs/Callout.tsx new file mode 100644 index 0000000..0c94f7b --- /dev/null +++ b/docs/components/docs/Callout.tsx @@ -0,0 +1,65 @@ +'use client' + +import { Box, Flex, Text } from '@chakra-ui/react' +import type { ReactNode } from 'react' + +interface CalloutProps { + type?: 'info' | 'warning' | 'error' | 'tip' + title?: string + children: ReactNode +} + +const calloutStyles = { + info: { + bg: 'blue.50', + borderColor: 'blue.500', + icon: 'ℹ️', + _dark: { bg: 'blue.950' }, + }, + warning: { + bg: 'yellow.50', + borderColor: 'yellow.500', + icon: '⚠️', + _dark: { bg: 'yellow.950' }, + }, + error: { + bg: 'red.50', + borderColor: 'red.500', + icon: '🚫', + _dark: { bg: 'red.950' }, + }, + tip: { + bg: 'green.50', + borderColor: 'green.500', + icon: 'πŸ’‘', + _dark: { bg: 'green.950' }, + }, +} + +export function Callout({ type = 'info', title, children }: CalloutProps) { + const styles = calloutStyles[type] + + return ( + + + {styles.icon} + + {title && ( + {title} + )} + + {children} + + + + + ) +} diff --git a/docs/components/docs/Cards.tsx b/docs/components/docs/Cards.tsx new file mode 100644 index 0000000..6873586 --- /dev/null +++ b/docs/components/docs/Cards.tsx @@ -0,0 +1,61 @@ +'use client' + +import { Box, Grid, Link, Text } from '@chakra-ui/react' +import NextLink from 'next/link' +import type { ReactNode } from 'react' + +interface CardsProps { + children: ReactNode + cols?: number +} + +export function Cards({ children, cols = 2 }: CardsProps) { + return ( + + {children} + + ) +} + +interface CardProps { + title: string + href: string + children?: ReactNode +} + +export function Card({ title, href, children }: CardProps) { + const isExternal = href.startsWith('http') + + const content = ( + + {title} + {children && ( + {children} + )} + + ) + + if (isExternal) { + return ( + + {content} + + ) + } + + return ( + + {content} + + ) +} diff --git a/docs/components/docs/CodeBlock.tsx b/docs/components/docs/CodeBlock.tsx new file mode 100644 index 0000000..a88eda1 --- /dev/null +++ b/docs/components/docs/CodeBlock.tsx @@ -0,0 +1,117 @@ +'use client' + +import { Box, Flex, IconButton, Text } from '@chakra-ui/react' +import { useState } from 'react' + +interface CodeBlockProps { + children: string + language?: string + filename?: string + highlightedHtml?: string +} + +export function CodeBlock({ children, language, filename, highlightedHtml }: CodeBlockProps) { + const [copied, setCopied] = useState(false) + + const handleCopy = async () => { + await navigator.clipboard.writeText(children) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } + + return ( + + {(filename || language) && ( + + + {filename || language} + + + {copied ? 'βœ“' : 'πŸ“‹'} + + + )} + + {!filename && !language && ( + + {copied ? 'βœ“' : 'πŸ“‹'} + + )} + {highlightedHtml ? ( + + ) : ( + + {children} + + )} + + + ) +} + +// Simple pre wrapper for MDX +export function Pre({ children, ...props }: React.ComponentProps<'pre'>) { + return ( + + {children} + + ) +} diff --git a/docs/components/docs/ColorModeButton.tsx b/docs/components/docs/ColorModeButton.tsx new file mode 100644 index 0000000..5b1c361 --- /dev/null +++ b/docs/components/docs/ColorModeButton.tsx @@ -0,0 +1,29 @@ +'use client' + +import { IconButton } from '@chakra-ui/react' +import { useTheme } from 'next-themes' +import { useEffect, useState } from 'react' + +export function ColorModeButton() { + const { theme, setTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + useEffect(() => { + setMounted(true) + }, []) + + if (!mounted) { + return + } + + return ( + setTheme(theme === 'dark' ? 'light' : 'dark')} + > + {theme === 'dark' ? 'β˜€οΈ' : 'πŸŒ™'} + + ) +} diff --git a/docs/components/docs/DocsLayout.tsx b/docs/components/docs/DocsLayout.tsx new file mode 100644 index 0000000..dc2d7bc --- /dev/null +++ b/docs/components/docs/DocsLayout.tsx @@ -0,0 +1,90 @@ +'use client' + +import { Box, Container, Flex, Link, Text } from '@chakra-ui/react' +import NextLink from 'next/link' +import { Navbar } from './Navbar' +import { Sidebar } from './Sidebar' +import { TableOfContents } from './TableOfContents' +import type { NavItem, TOCItem, Frontmatter, AdjacentPages } from '@/lib/docs' + +interface DocsLayoutProps { + children: React.ReactNode + nav: NavItem[] + toc: TOCItem[] + frontmatter: Frontmatter + adjacentPages?: AdjacentPages +} + +export function DocsLayout({ + children, + nav, + toc, + frontmatter, + adjacentPages, +}: DocsLayoutProps) { + return ( + + + + + + + + {frontmatter.title && ( + + {frontmatter.title} + + )} + {frontmatter.description && ( + + {frontmatter.description} + + )} + + {children} + + {adjacentPages && ( + + {adjacentPages.prev ? ( + + + Previous + {adjacentPages.prev.title} + + + ) : } + {adjacentPages.next ? ( + + + Next + {adjacentPages.next.title} + + + ) : } + + )} + + + MIT {new Date().getFullYear()} Β© Ryan Hefner + + + + + + + + + ) +} diff --git a/docs/components/docs/MDXComponents.tsx b/docs/components/docs/MDXComponents.tsx new file mode 100644 index 0000000..aacfcb0 --- /dev/null +++ b/docs/components/docs/MDXComponents.tsx @@ -0,0 +1,187 @@ +'use client' + +import { + Box, + Code, + Heading, + Link, + ListItem, + OrderedList, + Table, + Text, + UnorderedList, +} from '@chakra-ui/react' +import NextLink from 'next/link' +import type { MDXComponents as MDXComponentsType } from 'mdx/types' +import { Callout } from './Callout' +import { Pre } from './CodeBlock' +import { Steps } from './Steps' +import { Tabs, Tab } from './Tabs' +import { Cards, Card } from './Cards' + +function slugify(text: string): string { + return text + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/(^-|-$)/g, '') +} + +export const MDXComponents: MDXComponentsType = { + h1: (props) => ( + + ), + h2: (props) => { + const id = props.children ? slugify(String(props.children)) : undefined + return ( + + {props.children} + {id && ( + + # + + )} + + ) + }, + h3: (props) => { + const id = props.children ? slugify(String(props.children)) : undefined + return ( + + {props.children} + {id && ( + + # + + )} + + ) + }, + h4: (props) => ( + + ), + p: (props) => ( + + ), + a: ({ href, ...props }) => { + const isExternal = href?.startsWith('http') + if (isExternal) { + return ( + + ) + } + return ( + + + + ) + }, + ul: (props) => ( + + ), + ol: (props) => ( + + ), + li: (props) => ( + + ), + code: ({ children, className, ...props }) => { + // If it's inline code (no className), render as inline + if (!className) { + return ( + + {children} + + ) + } + // Block code is handled by pre + return {children} + }, + pre: Pre, + table: (props) => ( + + + + ), + thead: Table.Header, + tbody: Table.Body, + tr: Table.Row, + th: (props) => ( + + ), + td: Table.Cell, + blockquote: (props) => ( + + ), + hr: () => , + // Custom components + Callout, + Steps, + Tabs, + Tab, + Cards, + Card, +} diff --git a/docs/components/docs/MobileNav.tsx b/docs/components/docs/MobileNav.tsx new file mode 100644 index 0000000..93ae15e --- /dev/null +++ b/docs/components/docs/MobileNav.tsx @@ -0,0 +1,119 @@ +'use client' + +import { + Box, + IconButton, + Link, + Text, + VStack, +} from '@chakra-ui/react' +import NextLink from 'next/link' +import { usePathname } from 'next/navigation' +import { useState } from 'react' +import type { NavItem } from '@/lib/docs' + +interface MobileNavProps { + nav: NavItem[] +} + +export function MobileNav({ nav }: MobileNavProps) { + const [isOpen, setIsOpen] = useState(false) + const pathname = usePathname() + + return ( + + setIsOpen(!isOpen)} + > + {isOpen ? 'βœ•' : '☰'} + + {isOpen && ( + + + {nav.map((item) => ( + setIsOpen(false)} + /> + ))} + + + )} + + ) +} + +interface MobileNavItemProps { + item: NavItem + pathname: string + onClose: () => void + depth?: number +} + +function MobileNavItem({ item, pathname, onClose, depth = 0 }: MobileNavItemProps) { + const isActive = pathname === item.href + const hasChildren = item.children && item.children.length > 0 + + return ( + + {item.href ? ( + 0 ? `${depth * 16 + 12}px` : 3} + borderRadius="md" + fontWeight={isActive ? 'semibold' : 'normal'} + color={isActive ? 'fg' : 'fg.muted'} + bg={isActive ? 'bg.muted' : 'transparent'} + onClick={onClose} + > + {item.title} + + ) : ( + 0 ? `${depth * 16 + 12}px` : 3} + fontSize="sm" + fontWeight="semibold" + textTransform="uppercase" + letterSpacing="wider" + color="fg.muted" + mt={depth === 0 ? 4 : 0} + > + {item.title} + + )} + {hasChildren && ( + + {item.children!.map((child) => ( + + ))} + + )} + + ) +} diff --git a/docs/components/docs/Navbar.tsx b/docs/components/docs/Navbar.tsx new file mode 100644 index 0000000..ef51c18 --- /dev/null +++ b/docs/components/docs/Navbar.tsx @@ -0,0 +1,62 @@ +'use client' + +import { + Box, + Container, + Flex, + HStack, + IconButton, + Link, + Text, +} from '@chakra-ui/react' +import NextLink from 'next/link' +import { ColorModeButton } from './ColorModeButton' +import { MobileNav } from './MobileNav' +import type { NavItem } from '@/lib/docs' + +interface NavbarProps { + nav: NavItem[] +} + +export function Navbar({ nav }: NavbarProps) { + return ( + + + + + + react-fathom + + + + Docs + + + API + + + GitHub + + + + + + + + + + + ) +} diff --git a/docs/components/docs/Sidebar.tsx b/docs/components/docs/Sidebar.tsx new file mode 100644 index 0000000..52a4702 --- /dev/null +++ b/docs/components/docs/Sidebar.tsx @@ -0,0 +1,95 @@ +'use client' + +import { Box, Link, Text, VStack } from '@chakra-ui/react' +import NextLink from 'next/link' +import { usePathname } from 'next/navigation' +import type { NavItem } from '@/lib/docs' + +interface SidebarProps { + nav: NavItem[] +} + +export function Sidebar({ nav }: SidebarProps) { + const pathname = usePathname() + + return ( + + + {nav.map((item) => ( + + ))} + + + ) +} + +interface SidebarItemProps { + item: NavItem + pathname: string + depth?: number +} + +function SidebarItem({ item, pathname, depth = 0 }: SidebarItemProps) { + const isActive = pathname === item.href + const hasChildren = item.children && item.children.length > 0 + + return ( + + {item.href ? ( + 0 ? `${depth * 12 + 12}px` : 3} + borderRadius="md" + fontSize="sm" + fontWeight={isActive ? 'semibold' : 'normal'} + color={isActive ? 'fg' : 'fg.muted'} + bg={isActive ? 'bg.muted' : 'transparent'} + _hover={{ bg: 'bg.muted', color: 'fg' }} + > + {item.title} + + ) : ( + 0 ? `${depth * 12 + 12}px` : 3} + fontSize="xs" + fontWeight="semibold" + textTransform="uppercase" + letterSpacing="wider" + color="fg.muted" + mt={depth === 0 ? 4 : 0} + _first={{ mt: 0 }} + > + {item.title} + + )} + {hasChildren && ( + + {item.children!.map((child) => ( + + ))} + + )} + + ) +} diff --git a/docs/components/docs/Steps.tsx b/docs/components/docs/Steps.tsx new file mode 100644 index 0000000..719a998 --- /dev/null +++ b/docs/components/docs/Steps.tsx @@ -0,0 +1,41 @@ +'use client' + +import { Box, Flex, Text } from '@chakra-ui/react' +import type { ReactNode } from 'react' + +interface StepsProps { + children: ReactNode +} + +export function Steps({ children }: StepsProps) { + return ( + + {children} + + ) +} + +interface StepProps { + title: string + children: ReactNode +} + +export function Step({ title, children }: StepProps) { + return ( + + + {title} + + {children} + + + ) +} diff --git a/docs/components/docs/TableOfContents.tsx b/docs/components/docs/TableOfContents.tsx new file mode 100644 index 0000000..23c2d25 --- /dev/null +++ b/docs/components/docs/TableOfContents.tsx @@ -0,0 +1,89 @@ +'use client' + +import { Box, Link, Text, VStack } from '@chakra-ui/react' +import { useEffect, useState } from 'react' +import type { TOCItem } from '@/lib/docs' + +interface TableOfContentsProps { + toc: TOCItem[] +} + +export function TableOfContents({ toc }: TableOfContentsProps) { + const [activeId, setActiveId] = useState('') + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setActiveId(entry.target.id) + } + }) + }, + { rootMargin: '-80px 0px -80% 0px' } + ) + + const headings = document.querySelectorAll('h2, h3') + headings.forEach((heading) => observer.observe(heading)) + + return () => observer.disconnect() + }, []) + + if (toc.length === 0) { + return null + } + + return ( + + + On this page + + + {toc.map((item) => ( + + ))} + + + ) +} + +interface TOCLinkProps { + item: TOCItem + activeId: string +} + +function TOCLink({ item, activeId }: TOCLinkProps) { + const isActive = activeId === item.id + + return ( + <> + + {item.title} + + {item.children?.map((child) => ( + + ))} + + ) +} diff --git a/docs/components/docs/Tabs.tsx b/docs/components/docs/Tabs.tsx new file mode 100644 index 0000000..d057a7f --- /dev/null +++ b/docs/components/docs/Tabs.tsx @@ -0,0 +1,106 @@ +'use client' + +import { Box, Flex } from '@chakra-ui/react' +import { useState, createContext, useContext, type ReactNode } from 'react' + +interface TabsContextValue { + activeTab: string + setActiveTab: (tab: string) => void +} + +const TabsContext = createContext(null) + +interface TabsProps { + defaultValue?: string + children: ReactNode +} + +export function Tabs({ defaultValue, children }: TabsProps) { + const [activeTab, setActiveTab] = useState(defaultValue || '') + + return ( + + {children} + + ) +} + +interface TabListProps { + children: ReactNode +} + +export function TabList({ children }: TabListProps) { + return ( + + {children} + + ) +} + +interface TabTriggerProps { + value: string + children: ReactNode +} + +export function TabTrigger({ value, children }: TabTriggerProps) { + const context = useContext(TabsContext) + if (!context) throw new Error('TabTrigger must be used within Tabs') + + const isActive = context.activeTab === value + + return ( + context.setActiveTab(value)} + > + {children} + + ) +} + +interface TabContentProps { + value: string + children: ReactNode +} + +export function TabContent({ value, children }: TabContentProps) { + const context = useContext(TabsContext) + if (!context) throw new Error('TabContent must be used within Tabs') + + if (context.activeTab !== value) return null + + return {children} +} + +// Simple Tab component for MDX +interface TabProps { + label: string + children: ReactNode +} + +export function Tab({ label, children }: TabProps) { + return ( + + + {label} + + {children} + + ) +} diff --git a/docs/components/docs/index.ts b/docs/components/docs/index.ts new file mode 100644 index 0000000..a914c6d --- /dev/null +++ b/docs/components/docs/index.ts @@ -0,0 +1,12 @@ +export { Callout } from './Callout' +export { Cards, Card } from './Cards' +export { CodeBlock, Pre } from './CodeBlock' +export { ColorModeButton } from './ColorModeButton' +export { DocsLayout } from './DocsLayout' +export { MDXComponents } from './MDXComponents' +export { MobileNav } from './MobileNav' +export { Navbar } from './Navbar' +export { Sidebar } from './Sidebar' +export { Steps, Step } from './Steps' +export { TableOfContents } from './TableOfContents' +export { Tabs, TabList, TabTrigger, TabContent, Tab } from './Tabs' diff --git a/docs/content/api/components.mdx b/docs/content/api/components.mdx index 5517ac1..80182ea 100644 --- a/docs/content/api/components.mdx +++ b/docs/content/api/components.mdx @@ -1,3 +1,8 @@ +--- +title: "Components" +description: "API reference for TrackPageview, TrackClick, and TrackVisible components." +--- + # Components ## TrackPageview diff --git a/docs/content/api/hooks.mdx b/docs/content/api/hooks.mdx index 4e1b721..06995bb 100644 --- a/docs/content/api/hooks.mdx +++ b/docs/content/api/hooks.mdx @@ -1,3 +1,8 @@ +--- +title: "Hooks" +description: "API reference for useFathom, useTrackOnMount, useTrackOnClick, and useTrackOnVisible hooks." +--- + # Hooks ## useFathom diff --git a/docs/content/api/native.mdx b/docs/content/api/native.mdx index 529bd0a..f1ba2c5 100644 --- a/docs/content/api/native.mdx +++ b/docs/content/api/native.mdx @@ -1,3 +1,8 @@ +--- +title: "Native API" +description: "API reference for React Native-specific exports from react-fathom/native." +--- + # Native API React Native-specific exports from `react-fathom/native`. diff --git a/docs/content/api/providers.mdx b/docs/content/api/providers.mdx index 8023321..e55e992 100644 --- a/docs/content/api/providers.mdx +++ b/docs/content/api/providers.mdx @@ -1,3 +1,8 @@ +--- +title: "Providers" +description: "API reference for FathomProvider, NextFathomProviderApp, and NativeFathomProvider." +--- + # Providers ## FathomProvider diff --git a/docs/content/contributing.mdx b/docs/content/contributing.mdx index 6d9fe79..fe0897f 100644 --- a/docs/content/contributing.mdx +++ b/docs/content/contributing.mdx @@ -1,3 +1,8 @@ +--- +title: "Contributing" +description: "How to contribute to the react-fathom project." +--- + # Contributing Contributions are welcome! Whether it's bug fixes, new features, documentation improvements, or examples. diff --git a/docs/content/getting-started.mdx b/docs/content/getting-started.mdx index ebea586..8a3eaed 100644 --- a/docs/content/getting-started.mdx +++ b/docs/content/getting-started.mdx @@ -1,3 +1,8 @@ +--- +title: "Getting Started" +description: "Install and set up react-fathom in your application." +--- + # Getting Started ## Installation diff --git a/docs/content/guides/custom-client.mdx b/docs/content/guides/custom-client.mdx index 6148600..0a4c967 100644 --- a/docs/content/guides/custom-client.mdx +++ b/docs/content/guides/custom-client.mdx @@ -1,3 +1,8 @@ +--- +title: "Custom Client" +description: "Provide a custom Fathom client implementation for testing, SSR, or custom analytics pipelines." +--- + # Custom Client Provide a custom Fathom client implementation for testing, SSR, or custom analytics pipelines. diff --git a/docs/content/guides/default-options.mdx b/docs/content/guides/default-options.mdx index d1e9412..92f8bc7 100644 --- a/docs/content/guides/default-options.mdx +++ b/docs/content/guides/default-options.mdx @@ -1,3 +1,8 @@ +--- +title: "Default Options" +description: "Set app-wide defaults that automatically merge into all tracking calls." +--- + # Default Options Set app-wide defaults that automatically merge into all tracking calls. diff --git a/docs/content/guides/testing.mdx b/docs/content/guides/testing.mdx index a98fd87..88df6a0 100644 --- a/docs/content/guides/testing.mdx +++ b/docs/content/guides/testing.mdx @@ -1,3 +1,8 @@ +--- +title: "Testing" +description: "Use mock clients to test tracking behavior without sending real analytics." +--- + # Testing Use mock clients to test tracking behavior without sending real analytics. diff --git a/docs/content/index.mdx b/docs/content/index.mdx index 8361293..34c2ac4 100644 --- a/docs/content/index.mdx +++ b/docs/content/index.mdx @@ -1,3 +1,8 @@ +--- +title: "react-fathom" +description: "Privacy-focused analytics for React, Next.js, and React Native." +--- + # react-fathom **Privacy-focused analytics for React, Next.js, and React Native.** diff --git a/docs/content/nextjs/app-router.mdx b/docs/content/nextjs/app-router.mdx index 4c8b62f..497cc21 100644 --- a/docs/content/nextjs/app-router.mdx +++ b/docs/content/nextjs/app-router.mdx @@ -1,3 +1,8 @@ +--- +title: App Router +description: Integrating react-fathom with Next.js App Router +--- + # App Router This guide covers integrating react-fathom with Next.js 13.4+ using the App Router (`app/` directory). @@ -81,8 +86,6 @@ export default function RootLayout({ children }) { } ``` -import { Callout } from 'nextra/components' - Since `FathomProvider` uses React hooks, you may need to wrap it in your own Client Component when using this approach directly in a Server Component layout. diff --git a/docs/content/nextjs/index.mdx b/docs/content/nextjs/index.mdx index cb2a97a..68212f2 100644 --- a/docs/content/nextjs/index.mdx +++ b/docs/content/nextjs/index.mdx @@ -1,3 +1,8 @@ +--- +title: Next.js +description: First-class Next.js support with automatic pageview tracking +--- + # Next.js react-fathom provides first-class support for Next.js with automatic pageview tracking on route changes. @@ -6,8 +11,6 @@ react-fathom provides first-class support for Next.js with automatic pageview tr Next.js has two routing systems. Choose the guide that matches your project: -import { Cards, Card } from 'nextra/components' - Next.js 13.4+ with the `app/` directory. Uses Server Components and the new routing system. @@ -48,7 +51,7 @@ import { FathomProvider, useFathom, TrackClick } from 'react-fathom' Store your site ID in an environment variable for production: -```bash filename=".env.local" +```bash NEXT_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID ``` @@ -56,8 +59,6 @@ NEXT_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID ``` -import { Callout } from 'nextra/components' - Environment variables must be prefixed with `NEXT_PUBLIC_` to be available on the client side. diff --git a/docs/content/nextjs/pages-router.mdx b/docs/content/nextjs/pages-router.mdx index ef5d9ec..90dabd9 100644 --- a/docs/content/nextjs/pages-router.mdx +++ b/docs/content/nextjs/pages-router.mdx @@ -1,3 +1,8 @@ +--- +title: "Pages Router" +description: "Integrate react-fathom with Next.js Pages Router." +--- + # Pages Router This guide covers integrating react-fathom with Next.js using the Pages Router (`pages/` directory). diff --git a/docs/content/react-native/advanced.mdx b/docs/content/react-native/advanced.mdx index 719d4ee..1cb1115 100644 --- a/docs/content/react-native/advanced.mdx +++ b/docs/content/react-native/advanced.mdx @@ -1,3 +1,8 @@ +--- +title: Advanced Setup +description: Manual WebView configuration and advanced use cases +--- + # Advanced Setup For advanced use cases, you can manually control the WebView and client. @@ -165,8 +170,6 @@ The queue: - Is processed in order when WebView becomes ready - Persists only in memory (not across app restarts) -import { Callout } from 'nextra/components' - The queue does not persist across app restarts. Events queued when the app is killed will be lost. diff --git a/docs/content/react-native/app-state.mdx b/docs/content/react-native/app-state.mdx index 41d996b..83d4722 100644 --- a/docs/content/react-native/app-state.mdx +++ b/docs/content/react-native/app-state.mdx @@ -1,3 +1,8 @@ +--- +title: "App State Tracking" +description: "Track when users foreground or background your React Native app." +--- + # App State Tracking Track when users foreground or background your app. diff --git a/docs/content/react-native/index.mdx b/docs/content/react-native/index.mdx index 2bbc09a..9fe0a9a 100644 --- a/docs/content/react-native/index.mdx +++ b/docs/content/react-native/index.mdx @@ -1,9 +1,12 @@ +--- +title: React Native +description: Full React Native support with offline event queuing +--- + # React Native react-fathom provides full React Native support with a hidden WebView that loads Fathom's official tracking script, ensuring compatibility with Fathom Analytics. -import { Callout } from 'nextra/components' - React Native support uses a WebView-based approach. Events are queued until the WebView is ready, then sent automatically. diff --git a/docs/content/react-native/navigation.mdx b/docs/content/react-native/navigation.mdx index 253e25b..45a60fd 100644 --- a/docs/content/react-native/navigation.mdx +++ b/docs/content/react-native/navigation.mdx @@ -1,3 +1,8 @@ +--- +title: "Navigation Tracking" +description: "Track React Navigation screen changes as pageviews in React Native." +--- + # Navigation Tracking Track React Navigation screen changes as pageviews. diff --git a/docs/content/react.mdx b/docs/content/react.mdx index 11f58d0..4d2579b 100644 --- a/docs/content/react.mdx +++ b/docs/content/react.mdx @@ -1,3 +1,8 @@ +--- +title: "React" +description: "Using react-fathom hooks and components in standard React applications." +--- + # React This guide covers using react-fathom in standard React applications. For Next.js or React Native, see the dedicated guides. diff --git a/docs/content/troubleshooting.mdx b/docs/content/troubleshooting.mdx index 885c1b2..f9f95f4 100644 --- a/docs/content/troubleshooting.mdx +++ b/docs/content/troubleshooting.mdx @@ -1,3 +1,8 @@ +--- +title: "Troubleshooting" +description: "Common issues and solutions for react-fathom." +--- + # Troubleshooting Common issues and solutions. diff --git a/docs/lib/docs.ts b/docs/lib/docs.ts new file mode 100644 index 0000000..848acbb --- /dev/null +++ b/docs/lib/docs.ts @@ -0,0 +1,175 @@ +import fs from 'fs' +import path from 'path' +import matter from 'gray-matter' + +export interface NavItem { + title: string + href?: string + children?: NavItem[] +} + +export interface TOCItem { + id: string + title: string + level: number + children?: TOCItem[] +} + +export interface Frontmatter { + title?: string + description?: string + [key: string]: unknown +} + +export interface AdjacentPages { + prev?: { title: string; href: string } + next?: { title: string; href: string } +} + +export interface DocPage { + content: string + frontmatter: Frontmatter + slug: string[] +} + +const CONTENT_DIR = path.join(process.cwd(), 'content') + +// Navigation structure - defined manually for now +// This could be auto-generated from file structure +export function getDocsNav(): NavItem[] { + return [ + { + title: 'Getting Started', + children: [ + { title: 'Introduction', href: '/' }, + { title: 'Installation', href: '/getting-started' }, + ], + }, + { + title: 'Guides', + children: [ + { title: 'React', href: '/react' }, + { title: 'Next.js', href: '/nextjs' }, + { title: 'App Router', href: '/nextjs/app-router' }, + { title: 'Pages Router', href: '/nextjs/pages-router' }, + { title: 'React Native', href: '/react-native' }, + { title: 'Navigation', href: '/react-native/navigation' }, + { title: 'App State', href: '/react-native/app-state' }, + { title: 'Advanced', href: '/react-native/advanced' }, + ], + }, + { + title: 'API Reference', + children: [ + { title: 'Providers', href: '/api/providers' }, + { title: 'Hooks', href: '/api/hooks' }, + { title: 'Components', href: '/api/components' }, + { title: 'Native API', href: '/api/native' }, + ], + }, + { + title: 'More', + children: [ + { title: 'Default Options', href: '/guides/default-options' }, + { title: 'Custom Client', href: '/guides/custom-client' }, + { title: 'Testing', href: '/guides/testing' }, + { title: 'Custom Domains', href: '/guides/custom-domains' }, + { title: 'Troubleshooting', href: '/troubleshooting' }, + { title: 'Contributing', href: '/contributing' }, + ], + }, + ] +} + +export function getDocBySlug(slug: string[]): DocPage | null { + const slugPath = slug.length === 0 ? 'index' : slug.join('/') + const filePath = path.join(CONTENT_DIR, `${slugPath}.mdx`) + + if (!fs.existsSync(filePath)) { + return null + } + + const fileContent = fs.readFileSync(filePath, 'utf-8') + const { data, content } = matter(fileContent) + + return { + content, + frontmatter: data as Frontmatter, + slug, + } +} + +export function getAllDocSlugs(): string[][] { + const slugs: string[][] = [] + + function walkDir(dir: string, prefix: string[] = []) { + const files = fs.readdirSync(dir) + + for (const file of files) { + const filePath = path.join(dir, file) + const stat = fs.statSync(filePath) + + if (stat.isDirectory()) { + walkDir(filePath, [...prefix, file]) + } else if (file.endsWith('.mdx')) { + const name = file.replace('.mdx', '') + if (name === 'index') { + slugs.push(prefix) + } else { + slugs.push([...prefix, name]) + } + } + } + } + + walkDir(CONTENT_DIR) + return slugs +} + +export function extractTOC(content: string): TOCItem[] { + const headingRegex = /^(#{2,3})\s+(.+)$/gm + const toc: TOCItem[] = [] + + let match + while ((match = headingRegex.exec(content)) !== null) { + const level = match[1].length + const title = match[2].trim() + const id = title + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/(^-|-$)/g, '') + + toc.push({ id, title, level }) + } + + return toc +} + +export function getAdjacentPages(slug: string[]): AdjacentPages { + const nav = getDocsNav() + const flatNav: { title: string; href: string }[] = [] + + function flatten(items: NavItem[]) { + for (const item of items) { + if (item.href) { + flatNav.push({ title: item.title, href: item.href }) + } + if (item.children) { + flatten(item.children) + } + } + } + + flatten(nav) + + const currentHref = '/' + slug.join('/') + const normalizedHref = currentHref === '/' ? '/' : currentHref + const currentIndex = flatNav.findIndex( + (item) => item.href === normalizedHref || (normalizedHref === '/' && item.href === '/') + ) + + return { + prev: currentIndex > 0 ? flatNav[currentIndex - 1] : undefined, + next: currentIndex < flatNav.length - 1 ? flatNav[currentIndex + 1] : undefined, + } +} diff --git a/docs/mdx-components.tsx b/docs/mdx-components.tsx deleted file mode 100644 index f6062b1..0000000 --- a/docs/mdx-components.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useMDXComponents as getThemeMDXComponents } from 'nextra-theme-docs' - -const themeComponents = getThemeMDXComponents() - -export function useMDXComponents(components?: Record) { - return { - ...themeComponents, - ...components, - } -} diff --git a/docs/next.config.mjs b/docs/next.config.mjs index ad860ae..1773459 100644 --- a/docs/next.config.mjs +++ b/docs/next.config.mjs @@ -1,10 +1,7 @@ -import nextra from 'nextra' - -const withNextra = nextra() - /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + pageExtensions: ['js', 'jsx', 'ts', 'tsx'], } -export default withNextra(nextConfig) +export default nextConfig diff --git a/docs/package.json b/docs/package.json index 6ffd8a8..d9dea1c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -8,11 +8,16 @@ "start": "next start" }, "dependencies": { + "@chakra-ui/react": "^3.2.0", + "@emotion/react": "^11.13.0", + "gray-matter": "^4.0.3", "next": "^15.0.0", - "nextra": "^4.2.0", - "nextra-theme-docs": "^4.2.0", + "next-themes": "^0.4.0", + "next-mdx-remote": "^5.0.0", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "reading-time": "^1.5.0", + "shiki": "^1.24.0" }, "devDependencies": { "@types/node": "^22.0.0", diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 0000000..bd3ee29 --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "plugins": [{ "name": "next" }], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} From 5b5030a52fd88fea4a11e1f2b6d537190c3f8aa6 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 12:04:38 +0000 Subject: [PATCH 03/54] Add Nextra feature parity: syntax highlighting, search, auto-sidebar - Add rehype-pretty-code + shiki for syntax highlighting - Add code filename support via MDX fence meta - Add Edit on GitHub link to all pages - Add auto sidebar generation from file structure with _meta.ts - Add Pagefind search with Cmd+K keyboard shortcut - Update CodeBlock with Figure/Figcaption for filename display - Add data-pagefind-body for search indexing --- docs/app/[[...slug]]/page.tsx | 21 +- docs/components/docs/CodeBlock.tsx | 197 ++++++++++-------- docs/components/docs/DocsLayout.tsx | 55 ++++- docs/components/docs/MDXComponents.tsx | 10 +- docs/components/docs/Navbar.tsx | 4 +- docs/components/docs/Search.tsx | 275 +++++++++++++++++++++++++ docs/components/docs/index.ts | 3 +- docs/lib/docs.ts | 216 ++++++++++++++----- docs/package.json | 3 + 9 files changed, 627 insertions(+), 157 deletions(-) create mode 100644 docs/components/docs/Search.tsx diff --git a/docs/app/[[...slug]]/page.tsx b/docs/app/[[...slug]]/page.tsx index f1372a2..b6df24f 100644 --- a/docs/app/[[...slug]]/page.tsx +++ b/docs/app/[[...slug]]/page.tsx @@ -1,5 +1,6 @@ import { notFound } from 'next/navigation' import { MDXRemote } from 'next-mdx-remote/rsc' +import rehypePrettyCode from 'rehype-pretty-code' import { DocsLayout, MDXComponents } from '@/components/docs' import { getDocBySlug, @@ -32,6 +33,15 @@ export async function generateMetadata({ } } +const rehypePrettyCodeOptions = { + theme: { + dark: 'github-dark', + light: 'github-light', + }, + keepBackground: false, + defaultLang: 'plaintext', +} + export default async function DocPage({ params, }: { @@ -54,8 +64,17 @@ export default async function DocPage({ toc={toc} frontmatter={doc.frontmatter} adjacentPages={adjacentPages} + slug={slug} > - + ) } diff --git a/docs/components/docs/CodeBlock.tsx b/docs/components/docs/CodeBlock.tsx index a88eda1..315a23f 100644 --- a/docs/components/docs/CodeBlock.tsx +++ b/docs/components/docs/CodeBlock.tsx @@ -1,117 +1,134 @@ 'use client' -import { Box, Flex, IconButton, Text } from '@chakra-ui/react' -import { useState } from 'react' +import { Box, Flex, IconButton } from '@chakra-ui/react' +import { useState, useRef, type ReactNode, type ComponentProps } from 'react' -interface CodeBlockProps { - children: string - language?: string - filename?: string - highlightedHtml?: string -} - -export function CodeBlock({ children, language, filename, highlightedHtml }: CodeBlockProps) { +// Pre component for rehype-pretty-code +export function Pre({ children, ...props }: ComponentProps<'pre'>) { const [copied, setCopied] = useState(false) + const preRef = useRef(null) const handleCopy = async () => { - await navigator.clipboard.writeText(children) + const text = preRef.current?.textContent || '' + await navigator.clipboard.writeText(text) setCopied(true) setTimeout(() => setCopied(false), 2000) } return ( - - {(filename || language) && ( - - - {filename || language} - - - {copied ? 'βœ“' : 'πŸ“‹'} - - - )} - - {!filename && !language && ( - - {copied ? 'βœ“' : 'πŸ“‹'} - - )} - {highlightedHtml ? ( - - ) : ( - - {children} - - )} + + + {copied ? 'βœ“' : 'πŸ“‹'} + + [data-line]::before': { + counterIncrement: 'line', + content: 'counter(line)', + display: 'inline-block', + width: '1rem', + marginRight: '1.5rem', + textAlign: 'right', + color: 'rgb(100, 100, 100)', + }, + }} + {...props} + > + {children} ) } -// Simple pre wrapper for MDX -export function Pre({ children, ...props }: React.ComponentProps<'pre'>) { +// Figure wrapper for code blocks with filename (from rehype-pretty-code) +export function Figure({ children, ...props }: ComponentProps<'figure'>) { + const isCodeBlock = 'data-rehype-pretty-code-figure' in props + + if (!isCodeBlock) { + return
{children}
+ } + return ( {children} ) } + +// Figcaption for filename +export function Figcaption({ children, ...props }: ComponentProps<'figcaption'>) { + const isCodeTitle = 'data-rehype-pretty-code-title' in props + + if (!isCodeTitle) { + return
{children}
+ } + + return ( + + {children} + + ) +} diff --git a/docs/components/docs/DocsLayout.tsx b/docs/components/docs/DocsLayout.tsx index dc2d7bc..66d6c09 100644 --- a/docs/components/docs/DocsLayout.tsx +++ b/docs/components/docs/DocsLayout.tsx @@ -1,18 +1,27 @@ 'use client' -import { Box, Container, Flex, Link, Text } from '@chakra-ui/react' +import { Box, Container, Flex, HStack, Link, Text } from '@chakra-ui/react' import NextLink from 'next/link' import { Navbar } from './Navbar' import { Sidebar } from './Sidebar' import { TableOfContents } from './TableOfContents' import type { NavItem, TOCItem, Frontmatter, AdjacentPages } from '@/lib/docs' +const GITHUB_REPO = 'https://github.com/ryanhefner/react-fathom' +const DOCS_PATH = 'docs/content' + interface DocsLayoutProps { children: React.ReactNode nav: NavItem[] toc: TOCItem[] frontmatter: Frontmatter adjacentPages?: AdjacentPages + slug?: string[] +} + +function getEditUrl(slug: string[]): string { + const filePath = slug.length === 0 ? 'index' : slug.join('/') + return `${GITHUB_REPO}/edit/main/${DOCS_PATH}/${filePath}.mdx` } export function DocsLayout({ @@ -21,7 +30,10 @@ export function DocsLayout({ toc, frontmatter, adjacentPages, + slug = [], }: DocsLayoutProps) { + const editUrl = getEditUrl(slug) + return ( @@ -46,30 +58,55 @@ export function DocsLayout({ {frontmatter.description}
)} - + {children} + + + Edit this page on GitHub β†’ + + {adjacentPages && ( {adjacentPages.prev ? ( - + - Previous - {adjacentPages.prev.title} + + ← Previous + {adjacentPages.prev.title} + ) : } {adjacentPages.next ? ( - + - Next - {adjacentPages.next.title} + + Next β†’ + {adjacentPages.next.title} + ) : } diff --git a/docs/components/docs/MDXComponents.tsx b/docs/components/docs/MDXComponents.tsx index aacfcb0..029bdf3 100644 --- a/docs/components/docs/MDXComponents.tsx +++ b/docs/components/docs/MDXComponents.tsx @@ -14,7 +14,7 @@ import { import NextLink from 'next/link' import type { MDXComponents as MDXComponentsType } from 'mdx/types' import { Callout } from './Callout' -import { Pre } from './CodeBlock' +import { Pre, Figure, Figcaption } from './CodeBlock' import { Steps } from './Steps' import { Tabs, Tab } from './Tabs' import { Cards, Card } from './Cards' @@ -130,6 +130,10 @@ export const MDXComponents: MDXComponentsType = { ), code: ({ children, className, ...props }) => { + // If it has data-theme, it's from rehype-pretty-code - pass through + if ('data-theme' in props || 'data-language' in props) { + return {children} + } // If it's inline code (no className), render as inline if (!className) { return ( @@ -144,10 +148,12 @@ export const MDXComponents: MDXComponentsType = { ) } - // Block code is handled by pre + // Block code without highlighting return {children} }, pre: Pre, + figure: Figure, + figcaption: Figcaption, table: (props) => ( diff --git a/docs/components/docs/Navbar.tsx b/docs/components/docs/Navbar.tsx index ef51c18..79492bc 100644 --- a/docs/components/docs/Navbar.tsx +++ b/docs/components/docs/Navbar.tsx @@ -5,13 +5,12 @@ import { Container, Flex, HStack, - IconButton, Link, - Text, } from '@chakra-ui/react' import NextLink from 'next/link' import { ColorModeButton } from './ColorModeButton' import { MobileNav } from './MobileNav' +import { Search } from './Search' import type { NavItem } from '@/lib/docs' interface NavbarProps { @@ -52,6 +51,7 @@ export function Navbar({ nav }: NavbarProps) { + diff --git a/docs/components/docs/Search.tsx b/docs/components/docs/Search.tsx new file mode 100644 index 0000000..9cd99b2 --- /dev/null +++ b/docs/components/docs/Search.tsx @@ -0,0 +1,275 @@ +'use client' + +import { + Box, + Flex, + Input, + Link, + Modal, + Text, + VStack, +} from '@chakra-ui/react' +import NextLink from 'next/link' +import { useCallback, useEffect, useRef, useState } from 'react' + +interface SearchResult { + url: string + title: string + excerpt: string +} + +interface PagefindResult { + url: string + meta: { title?: string } + excerpt: string +} + +interface PagefindUI { + search: (query: string) => Promise<{ results: { data: () => Promise }[] }> +} + +export function Search() { + const [isOpen, setIsOpen] = useState(false) + const [query, setQuery] = useState('') + const [results, setResults] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const pagefindRef = useRef(null) + const inputRef = useRef(null) + + // Load Pagefind on first open + useEffect(() => { + if (isOpen && !pagefindRef.current) { + const loadPagefind = async () => { + try { + // @ts-expect-error - Pagefind is loaded from static files + const pagefind = await import('/pagefind/pagefind.js') + await pagefind.init() + pagefindRef.current = pagefind + } catch (e) { + console.warn('Pagefind not available (run `npm run build` first)') + } + } + loadPagefind() + } + }, [isOpen]) + + // Focus input when modal opens + useEffect(() => { + if (isOpen && inputRef.current) { + setTimeout(() => inputRef.current?.focus(), 100) + } + }, [isOpen]) + + // Keyboard shortcut (Cmd/Ctrl + K) + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault() + setIsOpen(true) + } + if (e.key === 'Escape') { + setIsOpen(false) + } + } + document.addEventListener('keydown', handleKeyDown) + return () => document.removeEventListener('keydown', handleKeyDown) + }, []) + + // Search handler + const handleSearch = useCallback(async (searchQuery: string) => { + setQuery(searchQuery) + + if (!searchQuery.trim() || !pagefindRef.current) { + setResults([]) + return + } + + setIsLoading(true) + try { + const search = await pagefindRef.current.search(searchQuery) + const searchResults = await Promise.all( + search.results.slice(0, 8).map(async (result) => { + const data = await result.data() + return { + url: data.url, + title: data.meta?.title || 'Untitled', + excerpt: data.excerpt, + } + }) + ) + setResults(searchResults) + } catch (e) { + console.error('Search error:', e) + setResults([]) + } finally { + setIsLoading(false) + } + }, []) + + const handleResultClick = () => { + setIsOpen(false) + setQuery('') + setResults([]) + } + + return ( + <> + {/* Search trigger button */} + setIsOpen(true)} + > + πŸ” + Search... + + ⌘K + + + + {/* Search modal */} + {isOpen && ( + setIsOpen(false)} + > + {/* Backdrop */} + + + {/* Modal content */} + + e.stopPropagation()} + > + {/* Search input */} + + πŸ” + handleSearch(e.target.value)} + placeholder="Search documentation..." + variant="unstyled" + fontSize="lg" + /> + setIsOpen(false)} + > + ESC + + + + {/* Results */} + + {isLoading ? ( + + Searching... + + ) : results.length > 0 ? ( + + {results.map((result, i) => ( + + + + + {result.title} + + + + + + ))} + + ) : query ? ( + + No results found for "{query}" + + ) : ( + + Start typing to search... + + )} + + + {/* Footer */} + + ↑↓ to navigate + ↡ to select + + + + + )} + + ) +} diff --git a/docs/components/docs/index.ts b/docs/components/docs/index.ts index a914c6d..8a47d99 100644 --- a/docs/components/docs/index.ts +++ b/docs/components/docs/index.ts @@ -1,11 +1,12 @@ export { Callout } from './Callout' export { Cards, Card } from './Cards' -export { CodeBlock, Pre } from './CodeBlock' +export { Pre, Figure, Figcaption } from './CodeBlock' export { ColorModeButton } from './ColorModeButton' export { DocsLayout } from './DocsLayout' export { MDXComponents } from './MDXComponents' export { MobileNav } from './MobileNav' export { Navbar } from './Navbar' +export { Search } from './Search' export { Sidebar } from './Sidebar' export { Steps, Step } from './Steps' export { TableOfContents } from './TableOfContents' diff --git a/docs/lib/docs.ts b/docs/lib/docs.ts index 848acbb..471a735 100644 --- a/docs/lib/docs.ts +++ b/docs/lib/docs.ts @@ -32,61 +32,174 @@ export interface DocPage { slug: string[] } +// Meta file structure: { [filename]: title | { title, ... } } +type MetaValue = string | { title: string; [key: string]: unknown } +type MetaFile = Record + const CONTENT_DIR = path.join(process.cwd(), 'content') -// Navigation structure - defined manually for now -// This could be auto-generated from file structure +function loadMeta(dir: string): MetaFile | null { + const metaPath = path.join(dir, '_meta.ts') + const metaJsonPath = path.join(dir, '_meta.json') + + // Try _meta.ts first (as exported default) + if (fs.existsSync(metaPath)) { + try { + // Read the file and extract the default export + const content = fs.readFileSync(metaPath, 'utf-8') + // Simple parsing for `export default { ... }` + const match = content.match(/export\s+default\s+(\{[\s\S]*\})/) + if (match) { + // Use Function constructor to evaluate (safe for static config) + const fn = new Function(`return ${match[1]}`) + return fn() as MetaFile + } + } catch { + // Fall through to JSON + } + } + + // Try _meta.json + if (fs.existsSync(metaJsonPath)) { + try { + return JSON.parse(fs.readFileSync(metaJsonPath, 'utf-8')) as MetaFile + } catch { + return null + } + } + + return null +} + +function getTitleFromMeta(meta: MetaFile | null, key: string): string | null { + if (!meta || !(key in meta)) return null + const value = meta[key] + return typeof value === 'string' ? value : value.title +} + +function getTitleFromFrontmatter(filePath: string): string | null { + try { + const content = fs.readFileSync(filePath, 'utf-8') + const { data } = matter(content) + return data.title || null + } catch { + return null + } +} + +function formatTitle(name: string): string { + return name + .replace(/-/g, ' ') + .replace(/\b\w/g, (c) => c.toUpperCase()) +} + +function buildNavFromDir(dir: string, basePath: string = ''): NavItem[] { + const items: NavItem[] = [] + const meta = loadMeta(dir) + + // Get all files and directories + const entries = fs.readdirSync(dir, { withFileTypes: true }) + + // Separate files and directories + const files: string[] = [] + const dirs: string[] = [] + + for (const entry of entries) { + if (entry.name.startsWith('_')) continue // Skip meta files + if (entry.isDirectory()) { + dirs.push(entry.name) + } else if (entry.name.endsWith('.mdx')) { + files.push(entry.name.replace('.mdx', '')) + } + } + + // Get order from meta file, or use alphabetical + const allKeys = [...new Set([...Object.keys(meta || {}), ...files, ...dirs])] + const orderedKeys = meta ? Object.keys(meta) : allKeys.sort() + + // Add remaining items not in meta + for (const key of allKeys) { + if (!orderedKeys.includes(key)) { + orderedKeys.push(key) + } + } + + for (const key of orderedKeys) { + const dirPath = path.join(dir, key) + const filePath = path.join(dir, `${key}.mdx`) + const isDir = dirs.includes(key) + const isFile = files.includes(key) + + if (isDir) { + // It's a directory - check for index.mdx + const indexPath = path.join(dirPath, 'index.mdx') + const hasIndex = fs.existsSync(indexPath) + const children = buildNavFromDir(dirPath, `${basePath}/${key}`) + + // Get title from meta, index frontmatter, or format from name + const title = getTitleFromMeta(meta, key) + || (hasIndex ? getTitleFromFrontmatter(indexPath) : null) + || formatTitle(key) + + if (hasIndex) { + // Directory with index - link to index, children are sub-pages + items.push({ + title, + href: `${basePath}/${key}`, + children: children.length > 0 ? children : undefined, + }) + } else if (children.length > 0) { + // Directory without index - just a group + items.push({ + title, + children, + }) + } + } else if (isFile && key !== 'index') { + // Regular file (not index) + const title = getTitleFromMeta(meta, key) + || getTitleFromFrontmatter(filePath) + || formatTitle(key) + + items.push({ + title, + href: `${basePath}/${key}`, + }) + } + } + + return items +} + +// Generate navigation from file structure export function getDocsNav(): NavItem[] { - return [ - { - title: 'Getting Started', - children: [ - { title: 'Introduction', href: '/' }, - { title: 'Installation', href: '/getting-started' }, - ], - }, - { - title: 'Guides', - children: [ - { title: 'React', href: '/react' }, - { title: 'Next.js', href: '/nextjs' }, - { title: 'App Router', href: '/nextjs/app-router' }, - { title: 'Pages Router', href: '/nextjs/pages-router' }, - { title: 'React Native', href: '/react-native' }, - { title: 'Navigation', href: '/react-native/navigation' }, - { title: 'App State', href: '/react-native/app-state' }, - { title: 'Advanced', href: '/react-native/advanced' }, - ], - }, - { - title: 'API Reference', - children: [ - { title: 'Providers', href: '/api/providers' }, - { title: 'Hooks', href: '/api/hooks' }, - { title: 'Components', href: '/api/components' }, - { title: 'Native API', href: '/api/native' }, - ], - }, - { - title: 'More', - children: [ - { title: 'Default Options', href: '/guides/default-options' }, - { title: 'Custom Client', href: '/guides/custom-client' }, - { title: 'Testing', href: '/guides/testing' }, - { title: 'Custom Domains', href: '/guides/custom-domains' }, - { title: 'Troubleshooting', href: '/troubleshooting' }, - { title: 'Contributing', href: '/contributing' }, - ], - }, - ] + // Handle root index separately + const indexPath = path.join(CONTENT_DIR, 'index.mdx') + const rootItems: NavItem[] = [] + + if (fs.existsSync(indexPath)) { + const title = getTitleFromFrontmatter(indexPath) || 'Introduction' + rootItems.push({ title, href: '/' }) + } + + // Build from directory structure + const dirItems = buildNavFromDir(CONTENT_DIR, '') + + // Combine - put index first, then dir items + return [...rootItems, ...dirItems] } export function getDocBySlug(slug: string[]): DocPage | null { const slugPath = slug.length === 0 ? 'index' : slug.join('/') - const filePath = path.join(CONTENT_DIR, `${slugPath}.mdx`) + let filePath = path.join(CONTENT_DIR, `${slugPath}.mdx`) + // Try direct file first if (!fs.existsSync(filePath)) { - return null + // Try as directory with index.mdx + filePath = path.join(CONTENT_DIR, slugPath, 'index.mdx') + if (!fs.existsSync(filePath)) { + return null + } } const fileContent = fs.readFileSync(filePath, 'utf-8') @@ -106,6 +219,8 @@ export function getAllDocSlugs(): string[][] { const files = fs.readdirSync(dir) for (const file of files) { + if (file.startsWith('_')) continue // Skip meta files + const filePath = path.join(dir, file) const stat = fs.statSync(filePath) @@ -114,7 +229,7 @@ export function getAllDocSlugs(): string[][] { } else if (file.endsWith('.mdx')) { const name = file.replace('.mdx', '') if (name === 'index') { - slugs.push(prefix) + slugs.push(prefix.length === 0 ? [] : prefix) } else { slugs.push([...prefix, name]) } @@ -162,11 +277,8 @@ export function getAdjacentPages(slug: string[]): AdjacentPages { flatten(nav) - const currentHref = '/' + slug.join('/') - const normalizedHref = currentHref === '/' ? '/' : currentHref - const currentIndex = flatNav.findIndex( - (item) => item.href === normalizedHref || (normalizedHref === '/' && item.href === '/') - ) + const currentHref = slug.length === 0 ? '/' : '/' + slug.join('/') + const currentIndex = flatNav.findIndex((item) => item.href === currentHref) return { prev: currentIndex > 0 ? flatNav[currentIndex - 1] : undefined, diff --git a/docs/package.json b/docs/package.json index d9dea1c..63b8dcf 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,6 +5,7 @@ "scripts": { "dev": "next dev", "build": "next build", + "postbuild": "pagefind --site .next/server/app --output-path public/pagefind", "start": "next start" }, "dependencies": { @@ -14,9 +15,11 @@ "next": "^15.0.0", "next-themes": "^0.4.0", "next-mdx-remote": "^5.0.0", + "pagefind": "^1.3.0", "react": "^19.0.0", "react-dom": "^19.0.0", "reading-time": "^1.5.0", + "rehype-pretty-code": "^0.14.0", "shiki": "^1.24.0" }, "devDependencies": { From d670acb5c008ee7efb521c40365e242500b98942 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 12:11:43 +0000 Subject: [PATCH 04/54] Add breadcrumbs, last updated, and package manager tabs - Add Breadcrumbs component for navigation context - Add PackageInstall and NpmToYarn components for package manager switching - Add getLastUpdated() using git log for file modification dates - Add getBreadcrumbs() to build navigation breadcrumb trail - Integrate breadcrumbs and last updated display into DocsLayout - Export new components from index.ts and add to MDXComponents --- docs/app/[[...slug]]/page.tsx | 6 + docs/components/docs/Breadcrumbs.tsx | 50 ++++++ docs/components/docs/DocsLayout.tsx | 29 +++- docs/components/docs/MDXComponents.tsx | 3 + docs/components/docs/PackageInstall.tsx | 204 ++++++++++++++++++++++++ docs/components/docs/index.ts | 2 + docs/lib/docs.ts | 76 +++++++++ 7 files changed, 367 insertions(+), 3 deletions(-) create mode 100644 docs/components/docs/Breadcrumbs.tsx create mode 100644 docs/components/docs/PackageInstall.tsx diff --git a/docs/app/[[...slug]]/page.tsx b/docs/app/[[...slug]]/page.tsx index b6df24f..b1adb8b 100644 --- a/docs/app/[[...slug]]/page.tsx +++ b/docs/app/[[...slug]]/page.tsx @@ -8,6 +8,8 @@ import { getDocsNav, extractTOC, getAdjacentPages, + getBreadcrumbs, + getLastUpdated, } from '@/lib/docs' export async function generateStaticParams() { @@ -57,6 +59,8 @@ export default async function DocPage({ const nav = getDocsNav() const toc = extractTOC(doc.content) const adjacentPages = getAdjacentPages(slug) + const breadcrumbs = getBreadcrumbs(slug) + const lastUpdated = getLastUpdated(slug) return ( + {items.map((item, index) => { + const isLast = index === items.length - 1 + + return ( + + {index > 0 && /} + {isLast || !item.href ? ( + {item.title} + ) : ( + + {item.title} + + )} + + ) + })} + + ) +} diff --git a/docs/components/docs/DocsLayout.tsx b/docs/components/docs/DocsLayout.tsx index 66d6c09..957f50d 100644 --- a/docs/components/docs/DocsLayout.tsx +++ b/docs/components/docs/DocsLayout.tsx @@ -5,7 +5,8 @@ import NextLink from 'next/link' import { Navbar } from './Navbar' import { Sidebar } from './Sidebar' import { TableOfContents } from './TableOfContents' -import type { NavItem, TOCItem, Frontmatter, AdjacentPages } from '@/lib/docs' +import { Breadcrumbs } from './Breadcrumbs' +import type { NavItem, TOCItem, Frontmatter, AdjacentPages, BreadcrumbItem } from '@/lib/docs' const GITHUB_REPO = 'https://github.com/ryanhefner/react-fathom' const DOCS_PATH = 'docs/content' @@ -17,6 +18,8 @@ interface DocsLayoutProps { frontmatter: Frontmatter adjacentPages?: AdjacentPages slug?: string[] + breadcrumbs?: BreadcrumbItem[] + lastUpdated?: string | null } function getEditUrl(slug: string[]): string { @@ -24,6 +27,15 @@ function getEditUrl(slug: string[]): string { return `${GITHUB_REPO}/edit/main/${DOCS_PATH}/${filePath}.mdx` } +function formatDate(dateString: string): string { + const date = new Date(dateString) + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }) +} + export function DocsLayout({ children, nav, @@ -31,6 +43,8 @@ export function DocsLayout({ frontmatter, adjacentPages, slug = [], + breadcrumbs, + lastUpdated, }: DocsLayoutProps) { const editUrl = getEditUrl(slug) @@ -48,6 +62,9 @@ export function DocsLayout({ px={{ base: 4, lg: 8 }} > + {breadcrumbs && breadcrumbs.length > 1 && ( + + )} {frontmatter.title && ( {frontmatter.title} @@ -61,16 +78,22 @@ export function DocsLayout({ {children} - + + {lastUpdated && ( + + Last updated: {formatDate(lastUpdated)} + + )} Edit this page on GitHub β†’ - + {adjacentPages && ( = { + npm: { install: 'npm install', devFlag: '-D' }, + yarn: { install: 'yarn add', devFlag: '-D' }, + pnpm: { install: 'pnpm add', devFlag: '-D' }, + bun: { install: 'bun add', devFlag: '-d' }, +} + +export function PackageInstall({ packages, dev = false }: PackageInstallProps) { + const [manager, setManager] = useState('npm') + const [copied, setCopied] = useState(false) + + const packageList = Array.isArray(packages) ? packages.join(' ') : packages + const { install, devFlag } = commands[manager] + const command = dev ? `${install} ${devFlag} ${packageList}` : `${install} ${packageList}` + + const handleCopy = async () => { + await navigator.clipboard.writeText(command) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } + + const managers: PackageManager[] = ['npm', 'yarn', 'pnpm', 'bun'] + + return ( + + {/* Tab header */} + + {managers.map((m) => ( + setManager(m)} + > + {m} + + ))} + + + {copied ? 'βœ“' : 'πŸ“‹'} + + + + {/* Command */} + + {command} + + + ) +} + +// Simpler version that just transforms npm commands to other managers +interface NpmToYarnProps { + children: string +} + +export function NpmToYarn({ children }: NpmToYarnProps) { + const [manager, setManager] = useState('npm') + const [copied, setCopied] = useState(false) + + // Transform npm command to other package managers + const transformCommand = (cmd: string, to: PackageManager): string => { + let result = cmd.trim() + + if (to === 'npm') return result + + // npm install -> yarn/pnpm add/bun add + result = result.replace(/^npm install/, commands[to].install) + result = result.replace(/^npm i /, `${commands[to].install} `) + + // npm run -> yarn/pnpm/bun (no run needed for yarn) + if (to === 'yarn') { + result = result.replace(/^npm run /, 'yarn ') + } else { + result = result.replace(/^npm run /, `${to} run `) + } + + // npm init -> yarn init/pnpm init/bun init + result = result.replace(/^npm init/, `${to} init`) + + // npm ci -> yarn install --frozen-lockfile/pnpm install --frozen-lockfile + if (to === 'yarn') { + result = result.replace(/^npm ci$/, 'yarn install --frozen-lockfile') + } else if (to === 'pnpm') { + result = result.replace(/^npm ci$/, 'pnpm install --frozen-lockfile') + } else if (to === 'bun') { + result = result.replace(/^npm ci$/, 'bun install --frozen-lockfile') + } + + // -D flag + result = result.replace(/ -D /, ` ${commands[to].devFlag} `) + result = result.replace(/ --save-dev /, ` ${commands[to].devFlag} `) + + return result + } + + const command = transformCommand(children, manager) + + const handleCopy = async () => { + await navigator.clipboard.writeText(command) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } + + const managers: PackageManager[] = ['npm', 'yarn', 'pnpm', 'bun'] + + return ( + + + {managers.map((m) => ( + setManager(m)} + > + {m} + + ))} + + + {copied ? 'βœ“' : 'πŸ“‹'} + + + + {command} + + + ) +} diff --git a/docs/components/docs/index.ts b/docs/components/docs/index.ts index 8a47d99..2d56e73 100644 --- a/docs/components/docs/index.ts +++ b/docs/components/docs/index.ts @@ -1,3 +1,4 @@ +export { Breadcrumbs } from './Breadcrumbs' export { Callout } from './Callout' export { Cards, Card } from './Cards' export { Pre, Figure, Figcaption } from './CodeBlock' @@ -6,6 +7,7 @@ export { DocsLayout } from './DocsLayout' export { MDXComponents } from './MDXComponents' export { MobileNav } from './MobileNav' export { Navbar } from './Navbar' +export { PackageInstall, NpmToYarn } from './PackageInstall' export { Search } from './Search' export { Sidebar } from './Sidebar' export { Steps, Step } from './Steps' diff --git a/docs/lib/docs.ts b/docs/lib/docs.ts index 471a735..f1bbda9 100644 --- a/docs/lib/docs.ts +++ b/docs/lib/docs.ts @@ -1,5 +1,6 @@ import fs from 'fs' import path from 'path' +import { execSync } from 'child_process' import matter from 'gray-matter' export interface NavItem { @@ -26,6 +27,11 @@ export interface AdjacentPages { next?: { title: string; href: string } } +export interface BreadcrumbItem { + title: string + href?: string +} + export interface DocPage { content: string frontmatter: Frontmatter @@ -285,3 +291,73 @@ export function getAdjacentPages(slug: string[]): AdjacentPages { next: currentIndex < flatNav.length - 1 ? flatNav[currentIndex + 1] : undefined, } } + +export function getLastUpdated(slug: string[]): string | null { + const slugPath = slug.length === 0 ? 'index' : slug.join('/') + let filePath = path.join(CONTENT_DIR, `${slugPath}.mdx`) + + // Try direct file first + if (!fs.existsSync(filePath)) { + // Try as directory with index.mdx + filePath = path.join(CONTENT_DIR, slugPath, 'index.mdx') + if (!fs.existsSync(filePath)) { + return null + } + } + + try { + // Get the last commit date for this file + const result = execSync( + `git log -1 --format=%cI -- "${filePath}"`, + { encoding: 'utf-8', cwd: process.cwd() } + ).trim() + + if (!result) return null + + return result + } catch { + return null + } +} + +export function getBreadcrumbs(slug: string[]): BreadcrumbItem[] { + if (slug.length === 0) { + return [{ title: 'Docs', href: '/' }] + } + + const nav = getDocsNav() + const breadcrumbs: BreadcrumbItem[] = [{ title: 'Docs', href: '/' }] + + // Find the path through the navigation + function findPath(items: NavItem[], targetPath: string, currentPath: BreadcrumbItem[] = []): BreadcrumbItem[] | null { + for (const item of items) { + if (item.href === targetPath) { + return [...currentPath, { title: item.title, href: item.href }] + } + if (item.children) { + const result = findPath(item.children, targetPath, [...currentPath, { title: item.title, href: item.href }]) + if (result) return result + } + } + return null + } + + const targetHref = '/' + slug.join('/') + const path = findPath(nav, targetHref) + + if (path) { + return [...breadcrumbs, ...path] + } + + // Fallback: build breadcrumbs from slug segments + let href = '' + for (const segment of slug) { + href += `/${segment}` + breadcrumbs.push({ + title: segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' '), + href: href, + }) + } + + return breadcrumbs +} From 87a9bab08f23c29c95557c3994dfbcc7d7889b0d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 12:12:34 +0000 Subject: [PATCH 05/54] Add tsbuildinfo to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 98e3153..f4bb6ad 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ node_modules coverage dist types +*.tsbuildinfo # Include Rollup config !rollup.config.js From 6d5cb6c001f2cdf5aa19a75e00f9a76fe35c779f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 12:26:04 +0000 Subject: [PATCH 06/54] Add Accordion and FileTree components - Add Accordion, AccordionItem, and Collapsible components for collapsible content - Add FileTree, Folder, and File components for visualizing directory structures - FileTree supports file type icons and highlighting for added/removed files - Export new components from index.ts and add to MDXComponents --- docs/components/docs/Accordion.tsx | 76 ++++++++++++++++ docs/components/docs/FileTree.tsx | 119 +++++++++++++++++++++++++ docs/components/docs/MDXComponents.tsx | 8 ++ docs/components/docs/index.ts | 2 + 4 files changed, 205 insertions(+) create mode 100644 docs/components/docs/Accordion.tsx create mode 100644 docs/components/docs/FileTree.tsx diff --git a/docs/components/docs/Accordion.tsx b/docs/components/docs/Accordion.tsx new file mode 100644 index 0000000..6740667 --- /dev/null +++ b/docs/components/docs/Accordion.tsx @@ -0,0 +1,76 @@ +'use client' + +import { Box, Flex, Text } from '@chakra-ui/react' +import { useState, type ReactNode } from 'react' + +interface AccordionItemProps { + title: string + children: ReactNode + defaultOpen?: boolean +} + +export function AccordionItem({ title, children, defaultOpen = false }: AccordionItemProps) { + const [isOpen, setIsOpen] = useState(defaultOpen) + + return ( + + setIsOpen(!isOpen)} + cursor="pointer" + textAlign="left" + > + {title} + + β–Ό + + + {isOpen && ( + + {children} + + )} + + ) +} + +interface AccordionProps { + children: ReactNode +} + +export function Accordion({ children }: AccordionProps) { + return ( + + {children} + + ) +} + +// Collapsible is an alias for single-item usage +interface CollapsibleProps { + title: string + children: ReactNode + defaultOpen?: boolean +} + +export function Collapsible({ title, children, defaultOpen = false }: CollapsibleProps) { + return ( + + + {children} + + + ) +} diff --git a/docs/components/docs/FileTree.tsx b/docs/components/docs/FileTree.tsx new file mode 100644 index 0000000..c1c2225 --- /dev/null +++ b/docs/components/docs/FileTree.tsx @@ -0,0 +1,119 @@ +'use client' + +import { Box, Flex, Text } from '@chakra-ui/react' +import { useState, type ReactNode } from 'react' + +interface FileTreeProps { + children: ReactNode +} + +export function FileTree({ children }: FileTreeProps) { + return ( + + {children} + + ) +} + +interface FolderProps { + name: string + children?: ReactNode + defaultOpen?: boolean +} + +export function Folder({ name, children, defaultOpen = true }: FolderProps) { + const [isOpen, setIsOpen] = useState(defaultOpen) + const hasChildren = Boolean(children) + + return ( + + hasChildren && setIsOpen(!isOpen)} + > + + {hasChildren ? (isOpen ? 'β–Ό' : 'β–Ά') : ''} + + πŸ“ + {name} + + {isOpen && children && ( + + {children} + + )} + + ) +} + +interface FileProps { + name: string + highlight?: boolean + added?: boolean + removed?: boolean +} + +export function File({ name, highlight, added, removed }: FileProps) { + // Determine file icon based on extension + const getIcon = (filename: string) => { + const ext = filename.split('.').pop()?.toLowerCase() + switch (ext) { + case 'ts': + case 'tsx': + return 'πŸ“˜' + case 'js': + case 'jsx': + return 'πŸ“’' + case 'json': + return 'πŸ“‹' + case 'md': + case 'mdx': + return 'πŸ“' + case 'css': + case 'scss': + return '🎨' + case 'html': + return '🌐' + case 'svg': + case 'png': + case 'jpg': + case 'gif': + return 'πŸ–ΌοΈ' + case 'env': + return 'πŸ”' + case 'gitignore': + return '🚫' + default: + return 'πŸ“„' + } + } + + let color = 'inherit' + if (highlight) color = 'blue.400' + if (added) color = 'green.400' + if (removed) color = 'red.400' + + return ( + + + {getIcon(name)} + + {name} + {added && +} + {removed && -} + + + ) +} diff --git a/docs/components/docs/MDXComponents.tsx b/docs/components/docs/MDXComponents.tsx index 102470d..892bb30 100644 --- a/docs/components/docs/MDXComponents.tsx +++ b/docs/components/docs/MDXComponents.tsx @@ -13,8 +13,10 @@ import { } from '@chakra-ui/react' import NextLink from 'next/link' import type { MDXComponents as MDXComponentsType } from 'mdx/types' +import { Accordion, AccordionItem, Collapsible } from './Accordion' import { Callout } from './Callout' import { Pre, Figure, Figcaption } from './CodeBlock' +import { FileTree, Folder, File } from './FileTree' import { Steps } from './Steps' import { Tabs, Tab } from './Tabs' import { Cards, Card } from './Cards' @@ -185,7 +187,13 @@ export const MDXComponents: MDXComponentsType = { ), hr: () => , // Custom components + Accordion, + AccordionItem, Callout, + Collapsible, + FileTree, + Folder, + File, Steps, Tabs, Tab, diff --git a/docs/components/docs/index.ts b/docs/components/docs/index.ts index 2d56e73..d471956 100644 --- a/docs/components/docs/index.ts +++ b/docs/components/docs/index.ts @@ -1,9 +1,11 @@ +export { Accordion, AccordionItem, Collapsible } from './Accordion' export { Breadcrumbs } from './Breadcrumbs' export { Callout } from './Callout' export { Cards, Card } from './Cards' export { Pre, Figure, Figcaption } from './CodeBlock' export { ColorModeButton } from './ColorModeButton' export { DocsLayout } from './DocsLayout' +export { FileTree, Folder, File } from './FileTree' export { MDXComponents } from './MDXComponents' export { MobileNav } from './MobileNav' export { Navbar } from './Navbar' From 19078ed6c64aea47299f9f9f2e952ad1107fb869 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 12:30:58 +0000 Subject: [PATCH 07/54] Add polish features: OG images, sitemap, print styles, changelog - Add dynamic OG image generation for social sharing - Add sitemap.ts and robots.ts for SEO - Add print styles for clean documentation printing - Add changelog page with release history - Add Changelog component for displaying version history - Enhance layout metadata with OpenGraph and Twitter card support --- docs/app/[[...slug]]/opengraph-image.tsx | 92 ++++++++++++++++++++++++ docs/app/globals.css | 78 ++++++++++++++++++++ docs/app/layout.tsx | 14 ++++ docs/app/robots.ts | 13 ++++ docs/app/sitemap.ts | 17 +++++ docs/components/docs/Changelog.tsx | 85 ++++++++++++++++++++++ docs/components/docs/index.ts | 1 + docs/content/_meta.ts | 1 + docs/content/changelog.mdx | 26 +++++++ docs/lib/changelog.ts | 74 +++++++++++++++++++ 10 files changed, 401 insertions(+) create mode 100644 docs/app/[[...slug]]/opengraph-image.tsx create mode 100644 docs/app/robots.ts create mode 100644 docs/app/sitemap.ts create mode 100644 docs/components/docs/Changelog.tsx create mode 100644 docs/content/changelog.mdx create mode 100644 docs/lib/changelog.ts diff --git a/docs/app/[[...slug]]/opengraph-image.tsx b/docs/app/[[...slug]]/opengraph-image.tsx new file mode 100644 index 0000000..a6eeb76 --- /dev/null +++ b/docs/app/[[...slug]]/opengraph-image.tsx @@ -0,0 +1,92 @@ +import { ImageResponse } from 'next/og' +import { getDocBySlug } from '@/lib/docs' + +export const runtime = 'edge' +export const alt = 'react-fathom documentation' +export const size = { width: 1200, height: 630 } +export const contentType = 'image/png' + +export default async function OGImage({ params }: { params: Promise<{ slug?: string[] }> }) { + const { slug = [] } = await params + const doc = getDocBySlug(slug) + + const title = doc?.frontmatter.title || 'react-fathom' + const description = doc?.frontmatter.description || 'Privacy-focused analytics for React, Next.js, and React Native' + + return new ImageResponse( + ( +
+ {/* Logo/Brand */} +
+
+ react-fathom +
+
+ + {/* Title */} +
+ {title} +
+ + {/* Description */} +
+ {description} +
+ + {/* Footer gradient line */} +
+
+ ), + { + ...size, + } + ) +} diff --git a/docs/app/globals.css b/docs/app/globals.css index 435b2cf..4569ca7 100644 --- a/docs/app/globals.css +++ b/docs/app/globals.css @@ -11,3 +11,81 @@ html { body { font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } + +/* Print styles */ +@media print { + /* Hide navigation elements */ + header, + nav, + aside, + [data-pagefind-body]::before, + [aria-label="Search"], + [aria-label="Table of contents"], + [aria-label="Breadcrumb"] { + display: none !important; + } + + /* Hide interactive elements */ + button, + [role="button"] { + display: none !important; + } + + /* Reset layout */ + body { + background: white !important; + color: black !important; + } + + main { + max-width: 100% !important; + padding: 0 !important; + margin: 0 !important; + } + + /* Typography for print */ + h1, h2, h3, h4, h5, h6 { + page-break-after: avoid; + color: black !important; + } + + p, li { + orphans: 3; + widows: 3; + } + + /* Code blocks */ + pre, code { + background: #f5f5f5 !important; + color: black !important; + border: 1px solid #ddd !important; + white-space: pre-wrap !important; + word-wrap: break-word !important; + } + + pre { + page-break-inside: avoid; + } + + /* Links */ + a { + color: black !important; + text-decoration: underline !important; + } + + a[href^="http"]::after { + content: " (" attr(href) ")"; + font-size: 0.8em; + color: #666; + } + + /* Hide footer navigation */ + footer { + display: none !important; + } + + /* Page margins */ + @page { + margin: 2cm; + } +} diff --git a/docs/app/layout.tsx b/docs/app/layout.tsx index 2b6f3c3..96a46a9 100644 --- a/docs/app/layout.tsx +++ b/docs/app/layout.tsx @@ -3,12 +3,26 @@ import type { Metadata } from 'next' import { Provider } from './provider' import './globals.css' +const SITE_URL = process.env.SITE_URL || 'https://react-fathom.com' + export const metadata: Metadata = { title: { default: 'react-fathom', template: '%s – react-fathom', }, description: 'Privacy-focused analytics for React, Next.js, and React Native', + metadataBase: new URL(SITE_URL), + openGraph: { + type: 'website', + locale: 'en_US', + siteName: 'react-fathom', + }, + twitter: { + card: 'summary_large_image', + }, + alternates: { + canonical: SITE_URL, + }, } export default function RootLayout({ children }: { children: ReactNode }) { diff --git a/docs/app/robots.ts b/docs/app/robots.ts new file mode 100644 index 0000000..3681416 --- /dev/null +++ b/docs/app/robots.ts @@ -0,0 +1,13 @@ +import { MetadataRoute } from 'next' + +const SITE_URL = process.env.SITE_URL || 'https://react-fathom.com' + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: '*', + allow: '/', + }, + sitemap: `${SITE_URL}/sitemap.xml`, + } +} diff --git a/docs/app/sitemap.ts b/docs/app/sitemap.ts new file mode 100644 index 0000000..9b76ebf --- /dev/null +++ b/docs/app/sitemap.ts @@ -0,0 +1,17 @@ +import { MetadataRoute } from 'next' +import { getAllDocSlugs } from '@/lib/docs' + +const SITE_URL = process.env.SITE_URL || 'https://react-fathom.com' + +export default function sitemap(): MetadataRoute.Sitemap { + const slugs = getAllDocSlugs() + + const docPages = slugs.map((slug) => ({ + url: slug.length === 0 ? SITE_URL : `${SITE_URL}/${slug.join('/')}`, + lastModified: new Date(), + changeFrequency: 'weekly' as const, + priority: slug.length === 0 ? 1 : 0.8, + })) + + return docPages +} diff --git a/docs/components/docs/Changelog.tsx b/docs/components/docs/Changelog.tsx new file mode 100644 index 0000000..066e9a3 --- /dev/null +++ b/docs/components/docs/Changelog.tsx @@ -0,0 +1,85 @@ +'use client' + +import { Box, Flex, Heading, Link, Text, VStack } from '@chakra-ui/react' + +interface Commit { + hash: string + message: string + author: string +} + +interface ChangelogEntryProps { + version: string + date: string + commits: Commit[] + repoUrl?: string +} + +export function ChangelogEntry({ version, date, commits, repoUrl = 'https://github.com/ryanhefner/react-fathom' }: ChangelogEntryProps) { + return ( + + + + + {version} + + + + {date} + + + + {commits.map((commit) => ( + + + {commit.hash} + + {commit.message} + + {commit.author} + + + ))} + + + ) +} + +interface ChangelogProps { + entries: { + version: string + date: string + commits: Commit[] + }[] + repoUrl?: string +} + +export function Changelog({ entries, repoUrl }: ChangelogProps) { + if (entries.length === 0) { + return ( + + No releases found. + + ) + } + + return ( + + {entries.map((entry) => ( + + ))} + + ) +} diff --git a/docs/components/docs/index.ts b/docs/components/docs/index.ts index d471956..982aec1 100644 --- a/docs/components/docs/index.ts +++ b/docs/components/docs/index.ts @@ -2,6 +2,7 @@ export { Accordion, AccordionItem, Collapsible } from './Accordion' export { Breadcrumbs } from './Breadcrumbs' export { Callout } from './Callout' export { Cards, Card } from './Cards' +export { Changelog, ChangelogEntry } from './Changelog' export { Pre, Figure, Figcaption } from './CodeBlock' export { ColorModeButton } from './ColorModeButton' export { DocsLayout } from './DocsLayout' diff --git a/docs/content/_meta.ts b/docs/content/_meta.ts index c002b9e..4063dc2 100644 --- a/docs/content/_meta.ts +++ b/docs/content/_meta.ts @@ -8,4 +8,5 @@ export default { guides: 'Guides', troubleshooting: 'Troubleshooting', contributing: 'Contributing', + changelog: 'Changelog', } diff --git a/docs/content/changelog.mdx b/docs/content/changelog.mdx new file mode 100644 index 0000000..ea74f5c --- /dev/null +++ b/docs/content/changelog.mdx @@ -0,0 +1,26 @@ +--- +title: Changelog +description: Release history and version changes for react-fathom +--- + +View the full release history on [GitHub Releases](https://github.com/ryanhefner/react-fathom/releases). + +## Recent Releases + +### 0.2.0 + +**January 2026** + +- Add Nextra documentation site and slim down README +- Upgrade dependencies +- Bump globals to 17.0.0 + +### 0.1.0 + +**Initial Release** + +- FathomProvider for React applications +- useFathom hook for tracking events +- Next.js App Router and Pages Router support +- React Native support with navigation tracking +- TypeScript support diff --git a/docs/lib/changelog.ts b/docs/lib/changelog.ts new file mode 100644 index 0000000..ecd7868 --- /dev/null +++ b/docs/lib/changelog.ts @@ -0,0 +1,74 @@ +import { execSync } from 'child_process' + +export interface ChangelogEntry { + version: string + date: string + commits: { + hash: string + message: string + author: string + }[] +} + +export function getChangelog(): ChangelogEntry[] { + try { + // Get all tags sorted by version + const tagsOutput = execSync('git tag --sort=-version:refname', { + encoding: 'utf-8', + cwd: process.cwd(), + }).trim() + + if (!tagsOutput) { + return [] + } + + const tags = tagsOutput.split('\n').filter(Boolean) + const changelog: ChangelogEntry[] = [] + + for (let i = 0; i < tags.length; i++) { + const tag = tags[i] + const prevTag = tags[i + 1] + + // Get tag date + const dateOutput = execSync(`git log -1 --format=%cI ${tag}`, { + encoding: 'utf-8', + cwd: process.cwd(), + }).trim() + + // Get commits between this tag and the previous one + const range = prevTag ? `${prevTag}..${tag}` : tag + const commitsOutput = execSync( + `git log ${range} --format="%H|%s|%an" --no-merges`, + { encoding: 'utf-8', cwd: process.cwd() } + ).trim() + + const commits = commitsOutput + .split('\n') + .filter(Boolean) + .map((line) => { + const [hash, message, author] = line.split('|') + return { hash: hash.slice(0, 7), message, author } + }) + + changelog.push({ + version: tag, + date: dateOutput, + commits, + }) + } + + return changelog + } catch (error) { + console.error('Failed to get changelog:', error) + return [] + } +} + +export function formatChangelogDate(dateString: string): string { + const date = new Date(dateString) + return date.toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric', + }) +} From 8fd6fdc800817fb7c800b6438009bdd72433fe51 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 12:39:30 +0000 Subject: [PATCH 08/54] Add announcement banner, 404 page, and keyboard shortcuts - Add AnnouncementBanner component with dismiss persistence - Add custom 404 page with search and popular links - Add keyboard shortcuts: j/k for prev/next page, / to focus search - Update Search to listen for custom open-search event - Integrate KeyboardShortcuts and AnnouncementBanner into DocsLayout --- docs/app/not-found.tsx | 77 ++++++++++++++++ docs/components/docs/AnnouncementBanner.tsx | 97 +++++++++++++++++++++ docs/components/docs/DocsLayout.tsx | 25 ++++++ docs/components/docs/KeyboardShortcuts.tsx | 63 +++++++++++++ docs/components/docs/Search.tsx | 10 ++- docs/components/docs/index.ts | 2 + 6 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 docs/app/not-found.tsx create mode 100644 docs/components/docs/AnnouncementBanner.tsx create mode 100644 docs/components/docs/KeyboardShortcuts.tsx diff --git a/docs/app/not-found.tsx b/docs/app/not-found.tsx new file mode 100644 index 0000000..0be0f33 --- /dev/null +++ b/docs/app/not-found.tsx @@ -0,0 +1,77 @@ +'use client' + +import { Box, Container, Heading, Text, VStack, Link, Flex } from '@chakra-ui/react' +import NextLink from 'next/link' +import { Navbar } from '@/components/docs/Navbar' +import { Search } from '@/components/docs/Search' + +const popularPages = [ + { title: 'Getting Started', href: '/getting-started' }, + { title: 'React', href: '/react' }, + { title: 'Next.js', href: '/nextjs' }, + { title: 'API Reference', href: '/api' }, +] + +export default function NotFound() { + return ( + <> + + + + + + 404 + + + Page not found + + + The page you're looking for doesn't exist or has been moved. + + + + + + Try searching for what you need: + + + + + + + Or check out these popular pages: + + + {popularPages.map((page) => ( + + {page.title} + + ))} + + + + + ← Back to home + + + + + ) +} diff --git a/docs/components/docs/AnnouncementBanner.tsx b/docs/components/docs/AnnouncementBanner.tsx new file mode 100644 index 0000000..0cf7572 --- /dev/null +++ b/docs/components/docs/AnnouncementBanner.tsx @@ -0,0 +1,97 @@ +'use client' + +import { Box, CloseButton, Flex, Link, Text } from '@chakra-ui/react' +import { useState, useEffect } from 'react' + +interface AnnouncementBannerProps { + id: string + message: string + linkText?: string + linkHref?: string + variant?: 'info' | 'warning' | 'success' + dismissible?: boolean +} + +const variantStyles = { + info: { + bg: 'blue.600', + color: 'white', + }, + warning: { + bg: 'yellow.500', + color: 'black', + }, + success: { + bg: 'green.600', + color: 'white', + }, +} + +export function AnnouncementBanner({ + id, + message, + linkText, + linkHref, + variant = 'info', + dismissible = true, +}: AnnouncementBannerProps) { + const [isDismissed, setIsDismissed] = useState(true) // Start hidden to prevent flash + const storageKey = `announcement-dismissed-${id}` + + useEffect(() => { + // Check localStorage after mount + const dismissed = localStorage.getItem(storageKey) + setIsDismissed(dismissed === 'true') + }, [storageKey]) + + const handleDismiss = () => { + localStorage.setItem(storageKey, 'true') + setIsDismissed(true) + } + + if (isDismissed) { + return null + } + + const styles = variantStyles[variant] + + return ( + + + + {message} + {linkText && linkHref && ( + <> + {' '} + + {linkText} β†’ + + + )} + + {dismissible && ( + + )} + + + ) +} diff --git a/docs/components/docs/DocsLayout.tsx b/docs/components/docs/DocsLayout.tsx index 957f50d..172ad7e 100644 --- a/docs/components/docs/DocsLayout.tsx +++ b/docs/components/docs/DocsLayout.tsx @@ -2,15 +2,25 @@ import { Box, Container, Flex, HStack, Link, Text } from '@chakra-ui/react' import NextLink from 'next/link' +import { AnnouncementBanner } from './AnnouncementBanner' import { Navbar } from './Navbar' import { Sidebar } from './Sidebar' import { TableOfContents } from './TableOfContents' import { Breadcrumbs } from './Breadcrumbs' +import { KeyboardShortcuts } from './KeyboardShortcuts' import type { NavItem, TOCItem, Frontmatter, AdjacentPages, BreadcrumbItem } from '@/lib/docs' const GITHUB_REPO = 'https://github.com/ryanhefner/react-fathom' const DOCS_PATH = 'docs/content' +interface Announcement { + id: string + message: string + linkText?: string + linkHref?: string + variant?: 'info' | 'warning' | 'success' +} + interface DocsLayoutProps { children: React.ReactNode nav: NavItem[] @@ -20,6 +30,7 @@ interface DocsLayoutProps { slug?: string[] breadcrumbs?: BreadcrumbItem[] lastUpdated?: string | null + announcement?: Announcement } function getEditUrl(slug: string[]): string { @@ -45,11 +56,25 @@ export function DocsLayout({ slug = [], breadcrumbs, lastUpdated, + announcement, }: DocsLayoutProps) { const editUrl = getEditUrl(slug) return ( + + {announcement && ( + + )} diff --git a/docs/components/docs/KeyboardShortcuts.tsx b/docs/components/docs/KeyboardShortcuts.tsx new file mode 100644 index 0000000..e0d6857 --- /dev/null +++ b/docs/components/docs/KeyboardShortcuts.tsx @@ -0,0 +1,63 @@ +'use client' + +import { useEffect } from 'react' +import { useRouter } from 'next/navigation' + +interface KeyboardShortcutsProps { + prevHref?: string + nextHref?: string +} + +export function KeyboardShortcuts({ prevHref, nextHref }: KeyboardShortcutsProps) { + const router = useRouter() + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + // Ignore if user is typing in an input, textarea, or contenteditable + const target = e.target as HTMLElement + if ( + target.tagName === 'INPUT' || + target.tagName === 'TEXTAREA' || + target.isContentEditable + ) { + return + } + + // Ignore if modifier keys are pressed (except for Cmd/Ctrl+K which Search handles) + if (e.altKey || e.shiftKey) { + return + } + + switch (e.key) { + case 'j': + // Next page + if (nextHref) { + e.preventDefault() + router.push(nextHref) + } + break + case 'k': + // Previous page + if (prevHref) { + e.preventDefault() + router.push(prevHref) + } + break + case '/': + // Focus search (Cmd+K is handled by Search component) + if (!e.metaKey && !e.ctrlKey) { + e.preventDefault() + // Dispatch a custom event that Search component listens to + window.dispatchEvent(new CustomEvent('open-search')) + } + break + } + } + + window.addEventListener('keydown', handleKeyDown) + return () => window.removeEventListener('keydown', handleKeyDown) + }, [router, prevHref, nextHref]) + + // This component doesn't render anything + return null +} diff --git a/docs/components/docs/Search.tsx b/docs/components/docs/Search.tsx index 9cd99b2..57e89d5 100644 --- a/docs/components/docs/Search.tsx +++ b/docs/components/docs/Search.tsx @@ -60,7 +60,7 @@ export function Search() { } }, [isOpen]) - // Keyboard shortcut (Cmd/Ctrl + K) + // Keyboard shortcut (Cmd/Ctrl + K) and custom event useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { @@ -71,8 +71,14 @@ export function Search() { setIsOpen(false) } } + const handleOpenSearch = () => setIsOpen(true) + document.addEventListener('keydown', handleKeyDown) - return () => document.removeEventListener('keydown', handleKeyDown) + window.addEventListener('open-search', handleOpenSearch) + return () => { + document.removeEventListener('keydown', handleKeyDown) + window.removeEventListener('open-search', handleOpenSearch) + } }, []) // Search handler diff --git a/docs/components/docs/index.ts b/docs/components/docs/index.ts index 982aec1..9d9ff85 100644 --- a/docs/components/docs/index.ts +++ b/docs/components/docs/index.ts @@ -1,4 +1,5 @@ export { Accordion, AccordionItem, Collapsible } from './Accordion' +export { AnnouncementBanner } from './AnnouncementBanner' export { Breadcrumbs } from './Breadcrumbs' export { Callout } from './Callout' export { Cards, Card } from './Cards' @@ -7,6 +8,7 @@ export { Pre, Figure, Figcaption } from './CodeBlock' export { ColorModeButton } from './ColorModeButton' export { DocsLayout } from './DocsLayout' export { FileTree, Folder, File } from './FileTree' +export { KeyboardShortcuts } from './KeyboardShortcuts' export { MDXComponents } from './MDXComponents' export { MobileNav } from './MobileNav' export { Navbar } from './Navbar' From eb13b4dadfa475dfd654027022bf05890e3384f1 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 12:57:03 +0000 Subject: [PATCH 09/54] Add landing page and move documentation under /docs - Create LandingPage component with hero, framework cards, features, and code example - Move documentation route from / to /docs - Update all internal links to use /docs prefix - Update navigation, sitemap, and breadcrumbs for new structure - Update 404 page with simplified navbar - Landing page at / showcases React, Next.js, and React Native support --- .../[[...slug]]/opengraph-image.tsx | 0 docs/app/{ => docs}/[[...slug]]/page.tsx | 0 docs/app/not-found.tsx | 42 +- docs/app/page.tsx | 5 + docs/app/sitemap.ts | 15 +- docs/components/LandingPage.tsx | 399 ++++++++++++++++++ docs/components/docs/Navbar.tsx | 4 +- docs/content/api/native.mdx | 4 +- docs/content/api/providers.mdx | 2 +- docs/content/getting-started.mdx | 8 +- docs/content/index.mdx | 6 +- docs/content/nextjs/index.mdx | 4 +- docs/content/react-native/index.mdx | 6 +- docs/content/react.mdx | 8 +- docs/content/troubleshooting.mdx | 2 +- docs/lib/docs.ts | 21 +- 16 files changed, 484 insertions(+), 42 deletions(-) rename docs/app/{ => docs}/[[...slug]]/opengraph-image.tsx (100%) rename docs/app/{ => docs}/[[...slug]]/page.tsx (100%) create mode 100644 docs/app/page.tsx create mode 100644 docs/components/LandingPage.tsx diff --git a/docs/app/[[...slug]]/opengraph-image.tsx b/docs/app/docs/[[...slug]]/opengraph-image.tsx similarity index 100% rename from docs/app/[[...slug]]/opengraph-image.tsx rename to docs/app/docs/[[...slug]]/opengraph-image.tsx diff --git a/docs/app/[[...slug]]/page.tsx b/docs/app/docs/[[...slug]]/page.tsx similarity index 100% rename from docs/app/[[...slug]]/page.tsx rename to docs/app/docs/[[...slug]]/page.tsx diff --git a/docs/app/not-found.tsx b/docs/app/not-found.tsx index 0be0f33..6b67bb9 100644 --- a/docs/app/not-found.tsx +++ b/docs/app/not-found.tsx @@ -1,21 +1,49 @@ 'use client' -import { Box, Container, Heading, Text, VStack, Link, Flex } from '@chakra-ui/react' +import { Box, Container, Heading, Text, VStack, Link, Flex, HStack } from '@chakra-ui/react' import NextLink from 'next/link' -import { Navbar } from '@/components/docs/Navbar' import { Search } from '@/components/docs/Search' +import { ColorModeButton } from '@/components/docs/ColorModeButton' const popularPages = [ - { title: 'Getting Started', href: '/getting-started' }, - { title: 'React', href: '/react' }, - { title: 'Next.js', href: '/nextjs' }, - { title: 'API Reference', href: '/api' }, + { title: 'Getting Started', href: '/docs/getting-started' }, + { title: 'React', href: '/docs/react' }, + { title: 'Next.js', href: '/docs/nextjs' }, + { title: 'API Reference', href: '/docs/api' }, ] +function SimpleNavbar() { + return ( + + + + + react-fathom + + + + Docs + + + + + + + ) +} + export default function NotFound() { return ( <> - + diff --git a/docs/app/page.tsx b/docs/app/page.tsx new file mode 100644 index 0000000..d878673 --- /dev/null +++ b/docs/app/page.tsx @@ -0,0 +1,5 @@ +import { LandingPage } from '@/components/LandingPage' + +export default function Home() { + return +} diff --git a/docs/app/sitemap.ts b/docs/app/sitemap.ts index 9b76ebf..4208dd4 100644 --- a/docs/app/sitemap.ts +++ b/docs/app/sitemap.ts @@ -6,12 +6,21 @@ const SITE_URL = process.env.SITE_URL || 'https://react-fathom.com' export default function sitemap(): MetadataRoute.Sitemap { const slugs = getAllDocSlugs() + // Landing page + const landingPage = { + url: SITE_URL, + lastModified: new Date(), + changeFrequency: 'monthly' as const, + priority: 1, + } + + // Documentation pages under /docs const docPages = slugs.map((slug) => ({ - url: slug.length === 0 ? SITE_URL : `${SITE_URL}/${slug.join('/')}`, + url: slug.length === 0 ? `${SITE_URL}/docs` : `${SITE_URL}/docs/${slug.join('/')}`, lastModified: new Date(), changeFrequency: 'weekly' as const, - priority: slug.length === 0 ? 1 : 0.8, + priority: slug.length === 0 ? 0.9 : 0.8, })) - return docPages + return [landingPage, ...docPages] } diff --git a/docs/components/LandingPage.tsx b/docs/components/LandingPage.tsx new file mode 100644 index 0000000..1627862 --- /dev/null +++ b/docs/components/LandingPage.tsx @@ -0,0 +1,399 @@ +'use client' + +import { + Box, + Container, + Flex, + Grid, + Heading, + HStack, + Link, + Text, + VStack, +} from '@chakra-ui/react' +import NextLink from 'next/link' +import { Navbar } from '@/components/docs/Navbar' +import { ColorModeButton } from '@/components/docs/ColorModeButton' +import { useState } from 'react' + +const frameworks = [ + { + name: 'React', + icon: 'βš›οΈ', + description: 'Drop-in provider and hooks for any React app', + href: '/docs/react', + }, + { + name: 'Next.js', + icon: 'β–²', + description: 'App Router and Pages Router support with SSR handling', + href: '/docs/nextjs', + }, + { + name: 'React Native', + icon: 'πŸ“±', + description: 'Navigation tracking and app state handling for mobile', + href: '/docs/react-native', + }, +] + +const features = [ + { + icon: 'πŸ”’', + title: 'Privacy-First', + description: 'GDPR, CCPA, and PECR compliant. No cookies required.', + }, + { + icon: '⚑', + title: 'Lightweight', + description: 'Tiny bundle size with zero dependencies.', + }, + { + icon: 'πŸ“˜', + title: 'TypeScript', + description: 'Full type safety with comprehensive type definitions.', + }, + { + icon: '🎯', + title: 'Simple API', + description: 'Intuitive hooks and components that just work.', + }, +] + +const installCommand = 'npm install react-fathom' + +function CopyButton({ text }: { text: string }) { + const [copied, setCopied] = useState(false) + + const handleCopy = async () => { + await navigator.clipboard.writeText(text) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } + + return ( + + {copied ? 'βœ“' : 'πŸ“‹'} + + ) +} + +function LandingNavbar() { + return ( + + + + + react-fathom + + + + Documentation + + + API + + + GitHub + + + + + + + ) +} + +export function LandingPage() { + return ( + + + + {/* Hero Section */} + + + + Privacy-focused analytics +
+ + for React + +
+ + A lightweight React integration for Fathom Analytics. + Track page views and custom events while respecting user privacy. + + + {/* Install command */} + + $ + {installCommand} + + + + {/* CTA buttons */} + + + Get Started + + + View on GitHub + + +
+
+ + {/* Frameworks Section */} + + + + + + Works with your stack + + + First-class support for popular React frameworks + + + + + {frameworks.map((framework) => ( + + + + + {framework.icon} + + + {framework.name} + + + {framework.description} + + + + + ))} + + + + + + {/* Features Section */} + + + + + Why react-fathom? + + + Simple, privacy-focused analytics integration + + + + + {features.map((feature) => ( + + {feature.icon} + + + {feature.title} + + + {feature.description} + + + + ))} + + + + + {/* Code Example Section */} + + + + + + Get started in minutes + + + Just wrap your app and start tracking + + + + + + + + + + + import + {'{ '} + FathomProvider + {' }'} + from + 'react-fathom' + ; + export default function + App + () {'{'} + return + ( + {'<'} + FathomProvider + siteId + = + "ABCDEFGH" + {'>'} + {'<'} + YourApp + /{'>'} + {' + FathomProvider + {'>'} + ); + {'}'} + + + + + + Read the full documentation β†’ + + + + + + + {/* Footer */} + + + + + MIT {new Date().getFullYear()} Β© Ryan Hefner + + + + Documentation + + + GitHub + + + Fathom Analytics + + + + + +
+ ) +} diff --git a/docs/components/docs/Navbar.tsx b/docs/components/docs/Navbar.tsx index 79492bc..856cce7 100644 --- a/docs/components/docs/Navbar.tsx +++ b/docs/components/docs/Navbar.tsx @@ -36,10 +36,10 @@ export function Navbar({ nav }: NavbarProps) { - Docs + Docs - API + API boolean` | No | Filter routes to track | | `includeParams` | `boolean` | No | Include params in URL (default: `false`) | -See [Navigation Tracking](/react-native/navigation) for detailed usage. +See [Navigation Tracking](/docs/react-native/navigation) for detailed usage. --- @@ -137,7 +137,7 @@ useAppStateTracking({ | `eventOptions` | `EventOptions` | - | Additional event options | | `onStateChange` | `(state) => void` | - | Callback on state change | -See [App State Tracking](/react-native/app-state) for detailed usage. +See [App State Tracking](/docs/react-native/app-state) for detailed usage. --- diff --git a/docs/content/api/providers.mdx b/docs/content/api/providers.mdx index e55e992..ac9a3b7 100644 --- a/docs/content/api/providers.mdx +++ b/docs/content/api/providers.mdx @@ -191,4 +191,4 @@ import { NativeFathomProvider } from 'react-fathom/native' | `clientRef` | `MutableRefObject` | No | Ref for direct client access | | `children` | `ReactNode` | Yes | Child components | -See the [React Native guide](/react-native) for detailed usage. +See the [React Native guide](/docs/react-native) for detailed usage. diff --git a/docs/content/getting-started.mdx b/docs/content/getting-started.mdx index 8a3eaed..c635912 100644 --- a/docs/content/getting-started.mdx +++ b/docs/content/getting-started.mdx @@ -68,10 +68,10 @@ Pageviews are tracked automatically by default. ## Next Steps -- [React usage guide](/react) β€” Hooks and declarative components -- [Next.js integration](/nextjs) β€” App Router and Pages Router -- [React Native setup](/react-native) β€” Mobile apps with offline queuing -- [API Reference](/api/providers) β€” Full provider props and options +- [React usage guide](/docs/react) β€” Hooks and declarative components +- [Next.js integration](/docs/nextjs) β€” App Router and Pages Router +- [React Native setup](/docs/react-native) β€” Mobile apps with offline queuing +- [API Reference](/docs/api/providers) β€” Full provider props and options ## Environment Variables diff --git a/docs/content/index.mdx b/docs/content/index.mdx index 34c2ac4..a6b4926 100644 --- a/docs/content/index.mdx +++ b/docs/content/index.mdx @@ -77,6 +77,6 @@ That's it! Pageviews are tracked automatically. ## Platform Guides -- [React](/react) β€” Basic React setup with hooks and components -- [Next.js](/nextjs) β€” App Router and Pages Router integration -- [React Native](/react-native) β€” Mobile apps with offline queuing +- [React](/docs/react) β€” Basic React setup with hooks and components +- [Next.js](/docs/nextjs) β€” App Router and Pages Router integration +- [React Native](/docs/react-native) β€” Mobile apps with offline queuing diff --git a/docs/content/nextjs/index.mdx b/docs/content/nextjs/index.mdx index 68212f2..a5c8f3a 100644 --- a/docs/content/nextjs/index.mdx +++ b/docs/content/nextjs/index.mdx @@ -12,10 +12,10 @@ react-fathom provides first-class support for Next.js with automatic pageview tr Next.js has two routing systems. Choose the guide that matches your project: - + Next.js 13.4+ with the `app/` directory. Uses Server Components and the new routing system. - + Traditional Next.js with the `pages/` directory. Works with all Next.js versions. diff --git a/docs/content/react-native/index.mdx b/docs/content/react-native/index.mdx index 9fe0a9a..b520960 100644 --- a/docs/content/react-native/index.mdx +++ b/docs/content/react-native/index.mdx @@ -93,6 +93,6 @@ function PurchaseButton({ price }) { ## Next Steps -- [Navigation Tracking](/react-native/navigation) β€” Track React Navigation screen changes -- [App State Tracking](/react-native/app-state) β€” Track foreground/background -- [Advanced Setup](/react-native/advanced) β€” Custom WebView configuration +- [Navigation Tracking](/docs/react-native/navigation) β€” Track React Navigation screen changes +- [App State Tracking](/docs/react-native/app-state) β€” Track foreground/background +- [Advanced Setup](/docs/react-native/advanced) β€” Custom WebView configuration diff --git a/docs/content/react.mdx b/docs/content/react.mdx index 4d2579b..80f5778 100644 --- a/docs/content/react.mdx +++ b/docs/content/react.mdx @@ -216,7 +216,7 @@ function ProductPage({ product }) { ## Next Steps -- [API Reference: Hooks](/api/hooks) β€” Full hook options -- [API Reference: Components](/api/components) β€” Full component props -- [Default Options Guide](/guides/default-options) β€” Set app-wide defaults -- [Testing Guide](/guides/testing) β€” Mock clients for testing +- [API Reference: Hooks](/docs/api/hooks) β€” Full hook options +- [API Reference: Components](/docs/api/components) β€” Full component props +- [Default Options Guide](/docs/guides/default-options) β€” Set app-wide defaults +- [Testing Guide](/docs/guides/testing) β€” Mock clients for testing diff --git a/docs/content/troubleshooting.mdx b/docs/content/troubleshooting.mdx index f9f95f4..591979c 100644 --- a/docs/content/troubleshooting.mdx +++ b/docs/content/troubleshooting.mdx @@ -25,7 +25,7 @@ Many ad blockers and privacy extensions block analytics scripts. To test: - Open an incognito/private window with extensions disabled - Or temporarily whitelist your development domain -Consider using [custom domains](/guides/custom-domains) to improve tracking reliability. +Consider using [custom domains](/docs/guides/custom-domains) to improve tracking reliability. ### 3. Domain Restrictions diff --git a/docs/lib/docs.ts b/docs/lib/docs.ts index f1bbda9..03c7e99 100644 --- a/docs/lib/docs.ts +++ b/docs/lib/docs.ts @@ -43,6 +43,7 @@ type MetaValue = string | { title: string; [key: string]: unknown } type MetaFile = Record const CONTENT_DIR = path.join(process.cwd(), 'content') +const DOCS_BASE_PATH = '/docs' function loadMeta(dir: string): MetaFile | null { const metaPath = path.join(dir, '_meta.ts') @@ -185,11 +186,11 @@ export function getDocsNav(): NavItem[] { if (fs.existsSync(indexPath)) { const title = getTitleFromFrontmatter(indexPath) || 'Introduction' - rootItems.push({ title, href: '/' }) + rootItems.push({ title, href: DOCS_BASE_PATH }) } // Build from directory structure - const dirItems = buildNavFromDir(CONTENT_DIR, '') + const dirItems = buildNavFromDir(CONTENT_DIR, DOCS_BASE_PATH) // Combine - put index first, then dir items return [...rootItems, ...dirItems] @@ -283,7 +284,7 @@ export function getAdjacentPages(slug: string[]): AdjacentPages { flatten(nav) - const currentHref = slug.length === 0 ? '/' : '/' + slug.join('/') + const currentHref = slug.length === 0 ? DOCS_BASE_PATH : `${DOCS_BASE_PATH}/${slug.join('/')}` const currentIndex = flatNav.findIndex((item) => item.href === currentHref) return { @@ -322,11 +323,11 @@ export function getLastUpdated(slug: string[]): string | null { export function getBreadcrumbs(slug: string[]): BreadcrumbItem[] { if (slug.length === 0) { - return [{ title: 'Docs', href: '/' }] + return [{ title: 'Docs', href: DOCS_BASE_PATH }] } const nav = getDocsNav() - const breadcrumbs: BreadcrumbItem[] = [{ title: 'Docs', href: '/' }] + const breadcrumbs: BreadcrumbItem[] = [{ title: 'Docs', href: DOCS_BASE_PATH }] // Find the path through the navigation function findPath(items: NavItem[], targetPath: string, currentPath: BreadcrumbItem[] = []): BreadcrumbItem[] | null { @@ -342,15 +343,15 @@ export function getBreadcrumbs(slug: string[]): BreadcrumbItem[] { return null } - const targetHref = '/' + slug.join('/') - const path = findPath(nav, targetHref) + const targetHref = `${DOCS_BASE_PATH}/${slug.join('/')}` + const foundPath = findPath(nav, targetHref) - if (path) { - return [...breadcrumbs, ...path] + if (foundPath) { + return [...breadcrumbs, ...foundPath] } // Fallback: build breadcrumbs from slug segments - let href = '' + let href = DOCS_BASE_PATH for (const segment of slug) { href += `/${segment}` breadcrumbs.push({ From d0a17b18a45ae160b8abc1ed08a6494e2b7cb093 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 14:25:29 +0000 Subject: [PATCH 10/54] Integrate react-fathom into docs site - Add react-fathom and fathom-client as dependencies - Set up NextFathomProviderApp in the root provider - Analytics enabled when NEXT_PUBLIC_FATHOM_SITE_ID is set - Add .env.example with configuration template --- docs/.env.example | 6 ++++++ docs/app/provider.tsx | 13 ++++++++++++- docs/package.json | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 docs/.env.example diff --git a/docs/.env.example b/docs/.env.example new file mode 100644 index 0000000..b04238c --- /dev/null +++ b/docs/.env.example @@ -0,0 +1,6 @@ +# Fathom Analytics +# Get your site ID from https://app.usefathom.com β†’ Settings β†’ Site ID +NEXT_PUBLIC_FATHOM_SITE_ID= + +# Site URL (for sitemap and canonical URLs) +SITE_URL=https://react-fathom.com diff --git a/docs/app/provider.tsx b/docs/app/provider.tsx index cc2b0c9..a97b6d5 100644 --- a/docs/app/provider.tsx +++ b/docs/app/provider.tsx @@ -1,12 +1,23 @@ 'use client' import { ChakraProvider, defaultSystem } from '@chakra-ui/react' +import { NextFathomProviderApp } from 'react-fathom/next' import { ColorModeProvider } from './color-mode' export function Provider({ children }: { children: React.ReactNode }) { + const siteId = process.env.NEXT_PUBLIC_FATHOM_SITE_ID + return ( - {children} + + {siteId ? ( + + {children} + + ) : ( + children + )} + ) } diff --git a/docs/package.json b/docs/package.json index 63b8dcf..7a81e48 100644 --- a/docs/package.json +++ b/docs/package.json @@ -11,6 +11,7 @@ "dependencies": { "@chakra-ui/react": "^3.2.0", "@emotion/react": "^11.13.0", + "fathom-client": "^3.6.0", "gray-matter": "^4.0.3", "next": "^15.0.0", "next-themes": "^0.4.0", @@ -18,6 +19,7 @@ "pagefind": "^1.3.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-fathom": "^0.2.0", "reading-time": "^1.5.0", "rehype-pretty-code": "^0.14.0", "shiki": "^1.24.0" From 4dfbb61f3f90da3d7eeb5dda2db7bb5cd4aa051f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 14:34:43 +0000 Subject: [PATCH 11/54] Add React and React Native examples with self-documenting docs - Create React (Vite + React Router) example with: - FathomProvider setup - Pageview tracking on route changes - Custom event tracking demo - Self-documenting docs page - Create React Native (Expo) example with: - FathomProvider from react-fathom/native - Event tracking demo - Offline support documentation - Navigation tracking example - Update examples README with live demo links - Update landing page to link to example subdomains - Examples will be hosted at: - react.react-fathom.com - native.react-fathom.com - next-app.react-fathom.com - next-pages.react-fathom.com --- docs/components/LandingPage.tsx | 68 ++++---- examples/README.md | 46 +++--- examples/react-native/.gitignore | 12 ++ examples/react-native/README.md | 102 ++++++++++++ examples/react-native/app.json | 24 +++ examples/react-native/app/_layout.tsx | 42 +++++ examples/react-native/app/about.tsx | 101 ++++++++++++ examples/react-native/app/docs.tsx | 180 +++++++++++++++++++++ examples/react-native/app/events.tsx | 194 +++++++++++++++++++++++ examples/react-native/app/index.tsx | 149 +++++++++++++++++ examples/react-native/package.json | 30 ++++ examples/react-native/tsconfig.json | 10 ++ examples/react/.env.example | 3 + examples/react/.gitignore | 4 + examples/react/README.md | 60 +++++++ examples/react/index.html | 12 ++ examples/react/package.json | 27 ++++ examples/react/src/App.tsx | 19 +++ examples/react/src/components/Layout.tsx | 61 +++++++ examples/react/src/main.tsx | 24 +++ examples/react/src/pages/About.tsx | 50 ++++++ examples/react/src/pages/Docs.tsx | 154 ++++++++++++++++++ examples/react/src/pages/Events.tsx | 150 ++++++++++++++++++ examples/react/src/pages/Home.tsx | 70 ++++++++ examples/react/tsconfig.json | 20 +++ examples/react/vite.config.ts | 6 + 26 files changed, 1570 insertions(+), 48 deletions(-) create mode 100644 examples/react-native/.gitignore create mode 100644 examples/react-native/README.md create mode 100644 examples/react-native/app.json create mode 100644 examples/react-native/app/_layout.tsx create mode 100644 examples/react-native/app/about.tsx create mode 100644 examples/react-native/app/docs.tsx create mode 100644 examples/react-native/app/events.tsx create mode 100644 examples/react-native/app/index.tsx create mode 100644 examples/react-native/package.json create mode 100644 examples/react-native/tsconfig.json create mode 100644 examples/react/.env.example create mode 100644 examples/react/.gitignore create mode 100644 examples/react/README.md create mode 100644 examples/react/index.html create mode 100644 examples/react/package.json create mode 100644 examples/react/src/App.tsx create mode 100644 examples/react/src/components/Layout.tsx create mode 100644 examples/react/src/main.tsx create mode 100644 examples/react/src/pages/About.tsx create mode 100644 examples/react/src/pages/Docs.tsx create mode 100644 examples/react/src/pages/Events.tsx create mode 100644 examples/react/src/pages/Home.tsx create mode 100644 examples/react/tsconfig.json create mode 100644 examples/react/vite.config.ts diff --git a/docs/components/LandingPage.tsx b/docs/components/LandingPage.tsx index 1627862..ecca774 100644 --- a/docs/components/LandingPage.tsx +++ b/docs/components/LandingPage.tsx @@ -22,18 +22,21 @@ const frameworks = [ icon: 'βš›οΈ', description: 'Drop-in provider and hooks for any React app', href: '/docs/react', + demo: 'https://react.react-fathom.com', }, { name: 'Next.js', icon: 'β–²', description: 'App Router and Pages Router support with SSR handling', href: '/docs/nextjs', + demo: 'https://next-app.react-fathom.com', }, { name: 'React Native', icon: 'πŸ“±', description: 'Navigation tracking and app state handling for mobile', href: '/docs/react-native', + demo: 'https://native.react-fathom.com', }, ] @@ -214,36 +217,47 @@ export function LandingPage() { width="100%" > {frameworks.map((framework) => ( - - - + {framework.icon} + + + {framework.name} + + + {framework.description} + + + - - {framework.icon} - - - {framework.name} - - - {framework.description} - - - - + Documentation + + + Live Demo β†’ + + +
))}
diff --git a/examples/README.md b/examples/README.md index 81f9dc9..b821244 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,42 +2,53 @@ This directory contains example applications demonstrating how to integrate `react-fathom` for privacy-focused analytics in your React projects. +## Live Demos + +Each example is hosted as a subdomain of react-fathom.com: + +| Example | Live Demo | Description | +|---------|-----------|-------------| +| React | [react.react-fathom.com](https://react.react-fathom.com) | Standard React with Vite and React Router | +| Next.js App Router | [next-app.react-fathom.com](https://next-app.react-fathom.com) | Modern Next.js with Server Components | +| Next.js Pages Router | [next-pages.react-fathom.com](https://next-pages.react-fathom.com) | Traditional Next.js routing | + ## Available Examples | Example | Framework | Router | Description | |---------|-----------|--------|-------------| +| [react](./react/) | React + Vite | React Router | Standard React SPA | | [next-app](./next-app/) | Next.js 13+ | App Router | Modern Next.js with React Server Components | | [next-pages](./next-pages/) | Next.js | Pages Router | Traditional Next.js routing | ## Which Example Should I Use? -- **Starting a new Next.js project?** Use [next-app](./next-app/) - the App Router is the recommended approach for new Next.js applications -- **Existing Next.js project with Pages Router?** Use [next-pages](./next-pages/) - works with the traditional `pages/` directory structure -- **Plain React or other frameworks?** Check the main [README](../README.md) for generic React setup instructions +- **Standard React application?** Use [react](./react/) - Vite + React Router setup +- **Starting a new Next.js project?** Use [next-app](./next-app/) - the App Router is the recommended approach +- **Existing Next.js with Pages Router?** Use [next-pages](./next-pages/) - works with the traditional `pages/` directory ## Getting Started ### 1. Navigate to an example ```bash -cd examples/next-app # or next-pages +cd examples/react # or next-app, next-pages ``` ### 2. Install dependencies ```bash npm install -# or -yarn install -# or -pnpm install ``` ### 3. Configure your Fathom site ID -Create a `.env.local` file: +Create a `.env` or `.env.local` file: ```bash +# For React (Vite) +VITE_FATHOM_SITE_ID=your-site-id-here + +# For Next.js NEXT_PUBLIC_FATHOM_SITE_ID=your-site-id-here ``` @@ -51,27 +62,20 @@ npm run dev ### 5. Open your browser -Navigate to [http://localhost:3000](http://localhost:3000) and explore the example. +Navigate to the local URL shown in the terminal. ## What's Demonstrated -Each example demonstrates: +Each example includes self-documenting documentation and demonstrates: - **Automatic pageview tracking** - Navigate between pages to see tracking in action - **Manual event tracking** - Click buttons to track custom events -- **Goal tracking** - Track conversions and goals +- **Declarative components** - Use `` for click tracking - **TypeScript integration** - Full type safety - -## Development Notes - -These examples use `workspace:*` to reference the local `react-fathom` package, making them ideal for: - -- Testing changes to the library -- Debugging integration issues -- Exploring different configuration options +- **Revenue tracking** - Track events with monetary values ## Learn More -- [react-fathom Documentation](../README.md) +- [react-fathom Documentation](https://react-fathom.com/docs) - [Fathom Analytics Documentation](https://usefathom.com/docs) - [Next.js Documentation](https://nextjs.org/docs) diff --git a/examples/react-native/.gitignore b/examples/react-native/.gitignore new file mode 100644 index 0000000..2894da4 --- /dev/null +++ b/examples/react-native/.gitignore @@ -0,0 +1,12 @@ +node_modules +.expo +dist +.env +.env.local +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision +*.orig.* +web-build/ diff --git a/examples/react-native/README.md b/examples/react-native/README.md new file mode 100644 index 0000000..28b2f38 --- /dev/null +++ b/examples/react-native/README.md @@ -0,0 +1,102 @@ +# react-fathom React Native Example + +A demonstration of integrating react-fathom into a React Native application using Expo. + +## Live Demo + +Visit [native.react-fathom.com](https://native.react-fathom.com) to see the web version of this example. + +## Features + +- **Offline Event Queue** β€” Events are queued when offline and sent when connected +- **Navigation Tracking** β€” Automatic screen tracking with Expo Router +- **Custom Event Tracking** β€” Track user interactions with `useFathom` hook +- **Revenue Tracking** β€” Track events with monetary values + +## Getting Started + +1. Clone the repository: + ```bash + git clone https://github.com/ryanhefner/react-fathom.git + cd react-fathom/examples/react-native + ``` + +2. Install dependencies: + ```bash + npm install + ``` + +3. Create a `.env` file with your Fathom site ID: + ```bash + EXPO_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID + ``` + +4. Start the development server: + ```bash + npm start + ``` + +5. Run on your device or simulator: + - Press `i` for iOS simulator + - Press `a` for Android emulator + - Scan QR code with Expo Go app + +## Project Structure + +``` +app/ +β”œβ”€β”€ _layout.tsx # Root layout with FathomProvider +β”œβ”€β”€ index.tsx # Home screen +β”œβ”€β”€ about.tsx # About screen +β”œβ”€β”€ docs.tsx # Documentation screen +└── events.tsx # Event tracking demo +``` + +## React Native Specific Features + +### Offline Support + +Events are automatically queued when the device is offline: + +```tsx +// Events track normally - they'll be queued if offline +trackEvent('user-action') +``` + +### Navigation Tracking + +Track screen views with React Navigation: + +```tsx +import { useNavigationTracking } from 'react-fathom/native' + +function App() { + const navigationRef = useNavigationContainerRef() + useNavigationTracking(navigationRef) + + return ... +} +``` + +### App State Tracking + +Track when the app enters foreground/background: + +```tsx +import { useAppStateTracking } from 'react-fathom/native' + +function App() { + useAppStateTracking({ + foregroundEventName: 'app-opened', + backgroundEventName: 'app-closed', + }) + + return +} +``` + +## Learn More + +- [react-fathom Documentation](https://react-fathom.com/docs/react-native) +- [Fathom Analytics](https://usefathom.com/ref/EKONBS) +- [Expo Documentation](https://docs.expo.dev) diff --git a/examples/react-native/app.json b/examples/react-native/app.json new file mode 100644 index 0000000..7583c51 --- /dev/null +++ b/examples/react-native/app.json @@ -0,0 +1,24 @@ +{ + "expo": { + "name": "react-fathom-example", + "slug": "react-fathom-example", + "version": "1.0.0", + "orientation": "portrait", + "scheme": "reactfathom", + "userInterfaceStyle": "automatic", + "newArchEnabled": true, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "backgroundColor": "#ffffff" + } + }, + "web": { + "bundler": "metro", + "output": "static" + }, + "plugins": ["expo-router"] + } +} diff --git a/examples/react-native/app/_layout.tsx b/examples/react-native/app/_layout.tsx new file mode 100644 index 0000000..440d34c --- /dev/null +++ b/examples/react-native/app/_layout.tsx @@ -0,0 +1,42 @@ +import { Stack } from 'expo-router' +import { StatusBar } from 'expo-status-bar' +import { FathomProvider } from 'react-fathom/native' + +const SITE_ID = process.env.EXPO_PUBLIC_FATHOM_SITE_ID + +export default function RootLayout() { + return ( + <> + + {SITE_ID ? ( + + + + + + + + + ) : ( + + + + + + + )} + + ) +} diff --git a/examples/react-native/app/about.tsx b/examples/react-native/app/about.tsx new file mode 100644 index 0000000..5c55bbc --- /dev/null +++ b/examples/react-native/app/about.tsx @@ -0,0 +1,101 @@ +import { View, Text, StyleSheet, ScrollView, Linking, TouchableOpacity } from 'react-native' + +export default function About() { + return ( + + + About This Example + + This is a demonstration of react-fathom integration in a React Native + application using Expo. + + + + + Tech Stack + + β€’ Expo β€” React Native framework + β€’ Expo Router β€” File-based routing + β€’ React Native β€” Mobile UI + β€’ react-fathom β€” Privacy-focused analytics + + + + + Why Fathom Analytics? + + Fathom Analytics is a privacy-focused alternative to traditional analytics + that respects user privacy while still providing valuable insights. + + + β€’ No cookies required β€” GDPR compliant + β€’ No personal data collection + β€’ Simple, actionable dashboard + β€’ Fast and lightweight + + + + + React Native Features + + react-fathom provides special features for React Native: + + + β€’ Offline event queue β€” events sent when online + β€’ Navigation tracking β€” automatic screen tracking + β€’ App state tracking β€” foreground/background events + β€’ Hidden WebView β€” no visible UI component + + + + + Linking.openURL('https://usefathom.com/ref/EKONBS')} + > + Try Fathom Analytics (Get $10 credit) β†’ + + + + ) +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#fff', + }, + section: { + padding: 24, + borderBottomWidth: 1, + borderBottomColor: '#eee', + }, + title: { + fontSize: 24, + fontWeight: 'bold', + marginBottom: 8, + }, + sectionTitle: { + fontSize: 18, + fontWeight: '600', + marginBottom: 12, + }, + description: { + fontSize: 14, + color: '#666', + marginBottom: 16, + lineHeight: 20, + }, + list: { + gap: 8, + }, + listItem: { + fontSize: 14, + color: '#333', + lineHeight: 20, + }, + link: { + fontSize: 16, + color: '#3b82f6', + fontWeight: '500', + }, +}) diff --git a/examples/react-native/app/docs.tsx b/examples/react-native/app/docs.tsx new file mode 100644 index 0000000..849f0ab --- /dev/null +++ b/examples/react-native/app/docs.tsx @@ -0,0 +1,180 @@ +import { View, Text, StyleSheet, ScrollView, Linking, TouchableOpacity } from 'react-native' + +export default function Docs() { + return ( + + + Documentation + + How to integrate react-fathom into your React Native application. + + + + {/* Installation */} + + Installation + + + npm install react-fathom react-native-webview + + + + + {/* Setup */} + + Setup + + Wrap your app with FathomProvider from react-fathom/native: + + + + {`import { FathomProvider } from 'react-fathom/native' + +function App() { + return ( + + + + ) +}`} + + + + + {/* Event Tracking */} + + Event Tracking + + Use the useFathom hook to track events: + + + + {`import { useFathom } from 'react-fathom/native' + +function MyComponent() { + const { trackEvent } = useFathom() + + return ( + + ) +}`} +
+ + + {/* Declarative Tracking */} + + + Declarative Tracking + + + Use {''} for declarative event tracking: + + +{`import { TrackClick } from 'react-fathom' + +function CTAButton() { + return ( + + + + ) +}`} + + + + {/* Environment Variables */} + + + Environment Variables + + + Store your site ID in an environment variable: + + + # .env + VITE_FATHOM_SITE_ID=YOUR_SITE_ID + + +{` + +`} + + + + {/* More Info */} + + + Learn More + + + + Full Documentation β†’ + + + API Reference β†’ + + + GitHub Repository β†’ + + + + + ) +} diff --git a/examples/react/src/pages/Events.tsx b/examples/react/src/pages/Events.tsx new file mode 100644 index 0000000..8907ddd --- /dev/null +++ b/examples/react/src/pages/Events.tsx @@ -0,0 +1,150 @@ +import { Box, Button, Heading, Text, VStack, HStack, Code, Input } from '@chakra-ui/react' +import { useState } from 'react' +import { useFathom, TrackClick } from 'react-fathom' + +export function Events() { + const { trackEvent } = useFathom() + const [customEventName, setCustomEventName] = useState('custom-event') + const [eventCount, setEventCount] = useState(0) + + const handleTrackEvent = (eventName: string) => { + trackEvent(eventName) + setEventCount((c) => c + 1) + } + + return ( + + + + Event Tracking Demo + + + This page demonstrates different ways to track custom events with react-fathom. + + + Events tracked this session: {eventCount} + + + + {/* useFathom Hook */} + + + Using the useFathom Hook + + + Call trackEvent() imperatively from any component. + + + + + + + const + {'{ '}trackEvent{' }'} = + useFathom + () + trackEvent('button-click') + + + + {/* TrackClick Component */} + + + Using the {''} Component + + + Wrap any clickable element to track clicks declaratively. + + + setEventCount((c) => c + 1)}> + + + setEventCount((c) => c + 1)}> + + + + + {'<'}TrackClick + eventName="cta-click" + {'>'} + {'<'}Button{'>'}Click Me{'Button{'>'} + {'TrackClick{'>'} + + + + {/* Custom Event */} + + + Custom Event Name + + + Track any event name you want. + + + setCustomEventName(e.target.value)} + placeholder="Event name" + maxW="200px" + /> + + + + + {/* Event with Value */} + + + Events with Monetary Value + + + Track events with an associated value (in cents) for revenue tracking. + + + + + + + trackEvent( + 'purchase', + {'{ '}_value: 1999{' }'} + ) + + + + ) +} diff --git a/examples/react/src/pages/Home.tsx b/examples/react/src/pages/Home.tsx new file mode 100644 index 0000000..fe41386 --- /dev/null +++ b/examples/react/src/pages/Home.tsx @@ -0,0 +1,70 @@ +import { Box, Heading, Text, VStack, Link, Code } from '@chakra-ui/react' +import { Link as RouterLink } from 'react-router-dom' + +export function Home() { + return ( + + + + react-fathom React Example + + + This example demonstrates how to integrate react-fathom into a standard React application + using Vite and React Router. + + + + + + Features Demonstrated + + + + Automatic Pageview Tracking + + Navigate between pages to see pageviews tracked automatically via React Router integration. + + + + Custom Event Tracking + + Visit the Events Demo page + to see custom event tracking in action. + + + + useFathom Hook + + Access trackEvent and trackPageview from anywhere in your app. + + + + + + + + Quick Setup + + + import + {'{ '} + FathomProvider + {' }'} + from + 'react-fathom' + {'<'}FathomProvider + siteId="YOUR_SITE_ID" + {'>'} + {'<'}App /{'>'} + {'FathomProvider{'>'} + + + + + + Read the full documentation β†’ + + + + ) +} diff --git a/examples/react/tsconfig.json b/examples/react/tsconfig.json new file mode 100644 index 0000000..a4c834a --- /dev/null +++ b/examples/react/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/examples/react/vite.config.ts b/examples/react/vite.config.ts new file mode 100644 index 0000000..9ffcc67 --- /dev/null +++ b/examples/react/vite.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + plugins: [react()], +}) From 9446097a650c449ab0186911b553b7abd4607439 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 14:40:22 +0000 Subject: [PATCH 12/54] Add docs and events pages to Next.js examples - Add self-documenting docs page to next-app (App Router) example - Add interactive events demo page to next-app example - Add self-documenting docs page to next-pages (Pages Router) example - Add interactive events demo page to next-pages example - Update navigation in both examples to include new pages - Update README files with live demo links - Update landing page to show both Next.js demo links --- docs/components/LandingPage.tsx | 30 +-- examples/next-app/README.md | 10 +- examples/next-app/app/docs/page.tsx | 181 ++++++++++++++++++ examples/next-app/app/events/page.tsx | 253 ++++++++++++++++++++++++++ examples/next-app/app/layout.tsx | 2 + examples/next-pages/README.md | 8 +- examples/next-pages/pages/_app.tsx | 2 + examples/next-pages/pages/docs.tsx | 199 ++++++++++++++++++++ examples/next-pages/pages/events.tsx | 249 +++++++++++++++++++++++++ 9 files changed, 920 insertions(+), 14 deletions(-) create mode 100644 examples/next-app/app/docs/page.tsx create mode 100644 examples/next-app/app/events/page.tsx create mode 100644 examples/next-pages/pages/docs.tsx create mode 100644 examples/next-pages/pages/events.tsx diff --git a/docs/components/LandingPage.tsx b/docs/components/LandingPage.tsx index ecca774..f0d160f 100644 --- a/docs/components/LandingPage.tsx +++ b/docs/components/LandingPage.tsx @@ -22,21 +22,24 @@ const frameworks = [ icon: 'βš›οΈ', description: 'Drop-in provider and hooks for any React app', href: '/docs/react', - demo: 'https://react.react-fathom.com', + demos: [{ label: 'Live Demo', url: 'https://react.react-fathom.com' }], }, { name: 'Next.js', icon: 'β–²', description: 'App Router and Pages Router support with SSR handling', href: '/docs/nextjs', - demo: 'https://next-app.react-fathom.com', + demos: [ + { label: 'App Router', url: 'https://next-app.react-fathom.com' }, + { label: 'Pages Router', url: 'https://next-pages.react-fathom.com' }, + ], }, { name: 'React Native', icon: 'πŸ“±', description: 'Navigation tracking and app state handling for mobile', href: '/docs/react-native', - demo: 'https://native.react-fathom.com', + demos: [{ label: 'Live Demo', url: 'https://native.react-fathom.com' }], }, ] @@ -238,7 +241,7 @@ export function LandingPage() { {framework.description} - + Documentation - - Live Demo β†’ - + {framework.demos.map((demo, idx) => ( + + {demo.label} β†’ + + ))} ))} diff --git a/examples/next-app/README.md b/examples/next-app/README.md index 369554e..99b4abc 100644 --- a/examples/next-app/README.md +++ b/examples/next-app/README.md @@ -2,6 +2,10 @@ A complete example of integrating privacy-focused analytics into a **Next.js 13+ App Router** application using `react-fathom`. +## Live Demo + +Visit [next-app.react-fathom.com](https://next-app.react-fathom.com) to see this example in action. + ## Why This Approach? The Next.js App Router introduces React Server Components, which require special handling for client-side analytics. This example shows the recommended pattern using `NextFathomProviderApp`, a pre-configured Client Component that works seamlessly in Server Component layouts. @@ -94,6 +98,10 @@ export default function MyComponent() { app/ β”œβ”€β”€ layout.tsx # FathomProvider setup β”œβ”€β”€ page.tsx # Home page with event tracking +β”œβ”€β”€ docs/ +β”‚ └── page.tsx # Self-documenting integration guide +β”œβ”€β”€ events/ +β”‚ └── page.tsx # Interactive event tracking demo β”œβ”€β”€ about/ β”‚ └── page.tsx # Static page (auto pageview tracking) └── contact/ @@ -102,6 +110,6 @@ app/ ## Learn More -- [react-fathom Documentation](../../README.md) +- [react-fathom Documentation](https://react-fathom.com/docs/nextjs) - [Next.js App Router Guide](https://nextjs.org/docs/app) - [Fathom Analytics](https://usefathom.com/ref/EKONBS) diff --git a/examples/next-app/app/docs/page.tsx b/examples/next-app/app/docs/page.tsx new file mode 100644 index 0000000..6c22d84 --- /dev/null +++ b/examples/next-app/app/docs/page.tsx @@ -0,0 +1,181 @@ +export default function Docs() { + return ( +
+
+

Documentation

+

How to integrate react-fathom into your Next.js App Router application

+
+
+
+

Installation

+
+            npm install react-fathom
+          
+
+ +
+

Setup

+

+ Wrap your app with NextFathomProviderApp in your root layout: +

+
+            {`// app/layout.tsx
+import { NextFathomProviderApp } from 'react-fathom/next'
+
+export default function RootLayout({ children }) {
+  return (
+    
+      
+        
+          {children}
+        
+      
+    
+  )
+}`}
+          
+
+ +
+

Event Tracking

+

+ Use the useFathom hook to track custom events: +

+
+            {`'use client'
+
+import { useFathom } from 'react-fathom'
+
+export default function MyComponent() {
+  const { trackEvent, trackGoal } = useFathom()
+
+  return (
+    <>
+      
+      
+    
+  )
+}`}
+          
+
+ +
+

Automatic Pageview Tracking

+

+ The NextFathomProviderApp component automatically tracks pageviews + when the route changes. This is done using Next.js App Router's{' '} + usePathname and useSearchParams hooks. +

+

No additional configuration is needed for pageview tracking.

+
+ +
+

Environment Variables

+

Store your site ID in an environment variable:

+
+            {`# .env.local
+NEXT_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID`}
+          
+

Then use it in your layout:

+
+            {`const siteId = process.env.NEXT_PUBLIC_FATHOM_SITE_ID`}
+          
+
+ +
+

Learn More

+ +
+
+
+ ) +} diff --git a/examples/next-app/app/events/page.tsx b/examples/next-app/app/events/page.tsx new file mode 100644 index 0000000..87fbafa --- /dev/null +++ b/examples/next-app/app/events/page.tsx @@ -0,0 +1,253 @@ +'use client' + +import { useFathom } from 'react-fathom' +import { useState } from 'react' + +type TrackedEvent = { + id: number + type: string + name: string + value?: number + timestamp: Date +} + +export default function Events() { + const { trackEvent, trackGoal } = useFathom() + const [events, setEvents] = useState([]) + const [eventId, setEventId] = useState(0) + + const addEvent = (type: string, name: string, value?: number) => { + setEvents((prev) => [ + { + id: eventId, + type, + name, + value, + timestamp: new Date(), + }, + ...prev, + ]) + setEventId((prev) => prev + 1) + } + + const handleTrackEvent = (name: string) => { + trackEvent(name) + addEvent('Event', name) + } + + const handleTrackGoal = (name: string, value: number) => { + trackGoal(name, value) + addEvent('Goal', name, value) + } + + return ( +
+
+

Event Tracking Demo

+

Test different event tracking methods

+
+
+

+ Click the buttons below to track different types of events. Events will be + logged below and sent to your Fathom Analytics dashboard. +

+ +
+

Custom Events

+

+ Track user interactions with custom event names +

+
+ + + +
+
+ +
+

Goal Tracking

+

+ Track conversions with optional monetary values (in cents) +

+
+ + + +
+
+ +
+

Event Log

+
+ {events.length === 0 ? ( +

+ No events tracked yet. Click a button above to get started. +

+ ) : ( +
    + {events.map((event) => ( +
  • + + {event.type} + + {event.name} + {event.value !== undefined && event.value > 0 && ( + + ${(event.value / 100).toFixed(2)} + + )} + + {event.timestamp.toLocaleTimeString()} + +
  • + ))} +
+ )} +
+
+ +
+

Code Example

+
+            {`'use client'
+
+import { useFathom } from 'react-fathom'
+
+export default function MyComponent() {
+  const { trackEvent, trackGoal } = useFathom()
+
+  return (
+    <>
+      {/* Track a custom event */}
+      
+
+      {/* Track a goal with monetary value (in cents) */}
+      
+    
+  )
+}`}
+          
+
+
+
+ ) +} diff --git a/examples/next-app/app/layout.tsx b/examples/next-app/app/layout.tsx index 0635909..bed04e6 100644 --- a/examples/next-app/app/layout.tsx +++ b/examples/next-app/app/layout.tsx @@ -27,6 +27,8 @@ export default function RootLayout({
Home + Docs + Events About Contact
diff --git a/examples/next-pages/README.md b/examples/next-pages/README.md index c7f9d05..a5fc461 100644 --- a/examples/next-pages/README.md +++ b/examples/next-pages/README.md @@ -2,6 +2,10 @@ A complete example of integrating privacy-focused analytics into a **Next.js Pages Router** application using `react-fathom`. +## Live Demo + +Visit [next-pages.react-fathom.com](https://next-pages.react-fathom.com) to see this example in action. + ## Why This Approach? The Pages Router is the traditional Next.js routing system using the `pages/` directory. This example demonstrates the recommended pattern for adding Fathom Analytics to existing Pages Router applications or new projects that prefer this routing approach. @@ -89,6 +93,8 @@ export default function MyPage() { pages/ β”œβ”€β”€ _app.tsx # FathomProvider setup β”œβ”€β”€ index.tsx # Home page with event tracking +β”œβ”€β”€ docs.tsx # Self-documenting integration guide +β”œβ”€β”€ events.tsx # Interactive event tracking demo β”œβ”€β”€ about.tsx # Static page (auto pageview tracking) └── contact.tsx # Form with event tracking ``` @@ -99,6 +105,6 @@ If you're planning to migrate to the App Router, check out the [next-app example ## Learn More -- [react-fathom Documentation](../../README.md) +- [react-fathom Documentation](https://react-fathom.com/docs/nextjs) - [Next.js Pages Router Guide](https://nextjs.org/docs/pages) - [Fathom Analytics](https://usefathom.com/ref/EKONBS) diff --git a/examples/next-pages/pages/_app.tsx b/examples/next-pages/pages/_app.tsx index 3735ec3..f4439b3 100644 --- a/examples/next-pages/pages/_app.tsx +++ b/examples/next-pages/pages/_app.tsx @@ -19,6 +19,8 @@ export default function App({ Component, pageProps }: AppProps) {
Home + Docs + Events About Contact
diff --git a/examples/next-pages/pages/docs.tsx b/examples/next-pages/pages/docs.tsx new file mode 100644 index 0000000..e481b74 --- /dev/null +++ b/examples/next-pages/pages/docs.tsx @@ -0,0 +1,199 @@ +export default function Docs() { + return ( +
+
+

Documentation

+

How to integrate react-fathom into your Next.js Pages Router application

+
+
+
+

Installation

+
+            npm install react-fathom
+          
+
+ +
+

Setup

+

+ Wrap your app with FathomProvider and add{' '} + NextFathomTrackViewPages in your _app.tsx: +

+
+            {`// pages/_app.tsx
+import { FathomProvider } from 'react-fathom'
+import { NextFathomTrackViewPages } from 'react-fathom/next'
+
+export default function App({ Component, pageProps }) {
+  return (
+    
+      
+      
+    
+  )
+}`}
+          
+
+ +
+

Event Tracking

+

+ Use the useFathom hook to track custom events: +

+
+            {`import { useFathom } from 'react-fathom'
+
+export default function MyComponent() {
+  const { trackEvent, trackGoal } = useFathom()
+
+  return (
+    <>
+      
+      
+    
+  )
+}`}
+          
+
+ +
+

Automatic Pageview Tracking

+

+ The NextFathomTrackViewPages component automatically tracks + pageviews when the route changes. It listens to Next.js Router events to + detect navigation. +

+

No additional configuration is needed for pageview tracking.

+
+ +
+

+ Differences from App Router +

+

The Pages Router setup differs from App Router in a few ways:

+
    +
  • + Provider: Use FathomProvider instead of{' '} + NextFathomProviderApp +
  • +
  • + Pageview tracking: Use NextFathomTrackViewPages{' '} + instead of the built-in tracking in NextFathomProviderApp +
  • +
  • + Location: Setup in pages/_app.tsx instead of{' '} + app/layout.tsx +
  • +
+
+ +
+

Environment Variables

+

Store your site ID in an environment variable:

+
+            {`# .env.local
+NEXT_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID`}
+          
+

Then use it in your _app.tsx:

+
+            {`const siteId = process.env.NEXT_PUBLIC_FATHOM_SITE_ID`}
+          
+
+ +
+

Learn More

+ +
+
+
+ ) +} diff --git a/examples/next-pages/pages/events.tsx b/examples/next-pages/pages/events.tsx new file mode 100644 index 0000000..7e55c87 --- /dev/null +++ b/examples/next-pages/pages/events.tsx @@ -0,0 +1,249 @@ +import { useFathom } from 'react-fathom' +import { useState } from 'react' + +type TrackedEvent = { + id: number + type: string + name: string + value?: number + timestamp: Date +} + +export default function Events() { + const { trackEvent, trackGoal } = useFathom() + const [events, setEvents] = useState([]) + const [eventId, setEventId] = useState(0) + + const addEvent = (type: string, name: string, value?: number) => { + setEvents((prev) => [ + { + id: eventId, + type, + name, + value, + timestamp: new Date(), + }, + ...prev, + ]) + setEventId((prev) => prev + 1) + } + + const handleTrackEvent = (name: string) => { + trackEvent(name) + addEvent('Event', name) + } + + const handleTrackGoal = (name: string, value: number) => { + trackGoal(name, value) + addEvent('Goal', name, value) + } + + return ( +
+
+

Event Tracking Demo

+

Test different event tracking methods

+
+
+

+ Click the buttons below to track different types of events. Events will be + logged below and sent to your Fathom Analytics dashboard. +

+ +
+

Custom Events

+

+ Track user interactions with custom event names +

+
+ + + +
+
+ +
+

Goal Tracking

+

+ Track conversions with optional monetary values (in cents) +

+
+ + + +
+
+ +
+

Event Log

+
+ {events.length === 0 ? ( +

+ No events tracked yet. Click a button above to get started. +

+ ) : ( +
    + {events.map((event) => ( +
  • + + {event.type} + + {event.name} + {event.value !== undefined && event.value > 0 && ( + + ${(event.value / 100).toFixed(2)} + + )} + + {event.timestamp.toLocaleTimeString()} + +
  • + ))} +
+ )} +
+
+ +
+

Code Example

+
+            {`import { useFathom } from 'react-fathom'
+
+export default function MyComponent() {
+  const { trackEvent, trackGoal } = useFathom()
+
+  return (
+    <>
+      {/* Track a custom event */}
+      
+
+      {/* Track a goal with monetary value (in cents) */}
+      
+    
+  )
+}`}
+          
+
+
+
+ ) +} From 62e11ed9607e7f676f4554b0933972f5a30adf11 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 14:57:38 +0000 Subject: [PATCH 13/54] Fix docs site build issues for Chakra UI v3 compatibility - Fix List components (UnorderedList/OrderedList -> List.Root/List.Item) - Fix duplicate _light props in PackageInstall component - Remove unused Modal import from Search component - Fix Input variant (unstyled -> flushed) in Search component - Use native HTML elements for CodeBlock to avoid type issues - Change opengraph-image runtime from edge to nodejs for fs access - Add getMDXComponents function for RSC compatibility - Remove 'use client' from MDXComponents to work with RSC - Add eslint.ignoreDuringBuilds to next.config.mjs - Add webpackIgnore comment for pagefind dynamic import --- docs/app/docs/[[...slug]]/opengraph-image.tsx | 2 +- docs/app/docs/[[...slug]]/page.tsx | 5 +- docs/components/docs/CodeBlock.tsx | 144 ++++++++++-------- docs/components/docs/MDXComponents.tsx | 16 +- docs/components/docs/MDXContent.tsx | 12 ++ docs/components/docs/PackageInstall.tsx | 12 +- docs/components/docs/Search.tsx | 10 +- docs/components/docs/index.ts | 1 + docs/next.config.mjs | 9 ++ 9 files changed, 128 insertions(+), 83 deletions(-) create mode 100644 docs/components/docs/MDXContent.tsx diff --git a/docs/app/docs/[[...slug]]/opengraph-image.tsx b/docs/app/docs/[[...slug]]/opengraph-image.tsx index a6eeb76..ab23215 100644 --- a/docs/app/docs/[[...slug]]/opengraph-image.tsx +++ b/docs/app/docs/[[...slug]]/opengraph-image.tsx @@ -1,7 +1,7 @@ import { ImageResponse } from 'next/og' import { getDocBySlug } from '@/lib/docs' -export const runtime = 'edge' +export const runtime = 'nodejs' export const alt = 'react-fathom documentation' export const size = { width: 1200, height: 630 } export const contentType = 'image/png' diff --git a/docs/app/docs/[[...slug]]/page.tsx b/docs/app/docs/[[...slug]]/page.tsx index b1adb8b..8c1e6bc 100644 --- a/docs/app/docs/[[...slug]]/page.tsx +++ b/docs/app/docs/[[...slug]]/page.tsx @@ -1,7 +1,7 @@ import { notFound } from 'next/navigation' import { MDXRemote } from 'next-mdx-remote/rsc' import rehypePrettyCode from 'rehype-pretty-code' -import { DocsLayout, MDXComponents } from '@/components/docs' +import { DocsLayout } from '@/components/docs' import { getDocBySlug, getAllDocSlugs, @@ -11,6 +11,7 @@ import { getBreadcrumbs, getLastUpdated, } from '@/lib/docs' +import { getMDXComponents } from '@/components/docs/MDXComponents' export async function generateStaticParams() { const slugs = getAllDocSlugs() @@ -74,7 +75,7 @@ export default async function DocPage({ > ) { > {copied ? 'βœ“' : 'πŸ“‹'} - [data-line]::before': { - counterIncrement: 'line', - content: 'counter(line)', - display: 'inline-block', - width: '1rem', - marginRight: '1.5rem', - textAlign: 'right', - color: 'rgb(100, 100, 100)', - }, + style={{ + padding: '1rem', + fontSize: '0.875rem', + lineHeight: '1.625', + overflowX: 'auto', + borderRadius: '0.5rem', }} {...props} > + {children} - + ) } @@ -91,17 +92,25 @@ export function Figure({ children, ...props }: ComponentProps<'figure'>) { } return ( - + {children} - + ) } @@ -114,21 +123,28 @@ export function Figcaption({ children, ...props }: ComponentProps<'figcaption'>) } return ( - + {children} - + ) } diff --git a/docs/components/docs/MDXComponents.tsx b/docs/components/docs/MDXComponents.tsx index 892bb30..087d344 100644 --- a/docs/components/docs/MDXComponents.tsx +++ b/docs/components/docs/MDXComponents.tsx @@ -1,15 +1,11 @@ -'use client' - import { Box, Code, Heading, Link, - ListItem, - OrderedList, + List, Table, Text, - UnorderedList, } from '@chakra-ui/react' import NextLink from 'next/link' import type { MDXComponents as MDXComponentsType } from 'mdx/types' @@ -29,6 +25,10 @@ function slugify(text: string): string { .replace(/(^-|-$)/g, '') } +export function getMDXComponents(): MDXComponentsType { + return MDXComponents +} + export const MDXComponents: MDXComponentsType = { h1: (props) => ( @@ -124,13 +124,13 @@ export const MDXComponents: MDXComponentsType = { ) }, ul: (props) => ( - + ), ol: (props) => ( - + ), li: (props) => ( - + ), code: ({ children, className, ...props }) => { // If it has data-theme, it's from rehype-pretty-code - pass through diff --git a/docs/components/docs/MDXContent.tsx b/docs/components/docs/MDXContent.tsx new file mode 100644 index 0000000..d6c6531 --- /dev/null +++ b/docs/components/docs/MDXContent.tsx @@ -0,0 +1,12 @@ +'use client' + +import { MDXRemote, type MDXRemoteSerializeResult } from 'next-mdx-remote' +import { MDXComponents } from './MDXComponents' + +interface MDXContentProps { + source: MDXRemoteSerializeResult +} + +export function MDXContent({ source }: MDXContentProps) { + return +} diff --git a/docs/components/docs/PackageInstall.tsx b/docs/components/docs/PackageInstall.tsx index 8728119..b322b02 100644 --- a/docs/components/docs/PackageInstall.tsx +++ b/docs/components/docs/PackageInstall.tsx @@ -50,9 +50,11 @@ export function PackageInstall({ packages, dev = false }: PackageInstallProps) { fontSize="sm" fontWeight={manager === m ? 'semibold' : 'normal'} color={manager === m ? 'white' : 'gray.400'} - _light={{ color: manager === m ? 'gray.900' : 'gray.500' }} bg={manager === m ? 'gray.800' : 'transparent'} - _light={{ bg: manager === m ? 'gray.100' : 'transparent' }} + _light={{ + color: manager === m ? 'gray.900' : 'gray.500', + bg: manager === m ? 'gray.100' : 'transparent', + }} borderBottomWidth="2px" borderBottomColor={manager === m ? 'blue.500' : 'transparent'} _hover={{ color: manager === m ? undefined : 'gray.200' }} @@ -163,9 +165,11 @@ export function NpmToYarn({ children }: NpmToYarnProps) { fontSize="sm" fontWeight={manager === m ? 'semibold' : 'normal'} color={manager === m ? 'white' : 'gray.400'} - _light={{ color: manager === m ? 'gray.900' : 'gray.500' }} bg={manager === m ? 'gray.800' : 'transparent'} - _light={{ bg: manager === m ? 'gray.100' : 'transparent' }} + _light={{ + color: manager === m ? 'gray.900' : 'gray.500', + bg: manager === m ? 'gray.100' : 'transparent', + }} borderBottomWidth="2px" borderBottomColor={manager === m ? 'blue.500' : 'transparent'} _hover={{ color: manager === m ? undefined : 'gray.200' }} diff --git a/docs/components/docs/Search.tsx b/docs/components/docs/Search.tsx index 57e89d5..8d42217 100644 --- a/docs/components/docs/Search.tsx +++ b/docs/components/docs/Search.tsx @@ -5,7 +5,6 @@ import { Flex, Input, Link, - Modal, Text, VStack, } from '@chakra-ui/react' @@ -41,8 +40,9 @@ export function Search() { if (isOpen && !pagefindRef.current) { const loadPagefind = async () => { try { - // @ts-expect-error - Pagefind is loaded from static files - const pagefind = await import('/pagefind/pagefind.js') + // Use dynamic import with webpackIgnore to prevent build-time resolution + const pagefindPath = '/pagefind/pagefind.js' + const pagefind = await import(/* webpackIgnore: true */ pagefindPath) await pagefind.init() pagefindRef.current = pagefind } catch (e) { @@ -189,8 +189,10 @@ export function Search() { value={query} onChange={(e) => handleSearch(e.target.value)} placeholder="Search documentation..." - variant="unstyled" + variant="flushed" + border="none" fontSize="lg" + _focus={{ boxShadow: 'none' }} /> Date: Thu, 22 Jan 2026 14:58:34 +0000 Subject: [PATCH 14/54] Add .gitignore for docs site build artifacts --- docs/.gitignore | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/.gitignore diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..28f8b53 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,37 @@ +# Dependencies +node_modules/ + +# Next.js build output +.next/ +out/ + +# Next.js auto-generated files +next-env.d.ts + +# Pagefind search index (generated during build) +public/pagefind/ + +# Lock files (project uses yarn) +package-lock.json + +# Environment files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Debug logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db From b59a2cc16be40859a7007acb0896382cb6467f0e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 15:27:46 +0000 Subject: [PATCH 15/54] Configure static export for docs and Next.js app example - Add output: 'export' to docs site next.config.mjs - Add force-static export config to robots.ts and sitemap.ts - Remove opengraph-image.tsx (incompatible with static export + optional catch-all) - Add output: 'export' to next-app example - Remove package-lock.json files from examples --- docs/app/docs/[[...slug]]/opengraph-image.tsx | 92 -- docs/app/robots.ts | 2 + docs/app/sitemap.ts | 2 + docs/next.config.mjs | 1 + examples/next-app/next.config.js | 1 + examples/next-app/package-lock.json | 1055 ----------------- examples/next-pages/package-lock.json | 1055 ----------------- 7 files changed, 6 insertions(+), 2202 deletions(-) delete mode 100644 docs/app/docs/[[...slug]]/opengraph-image.tsx delete mode 100644 examples/next-app/package-lock.json delete mode 100644 examples/next-pages/package-lock.json diff --git a/docs/app/docs/[[...slug]]/opengraph-image.tsx b/docs/app/docs/[[...slug]]/opengraph-image.tsx deleted file mode 100644 index ab23215..0000000 --- a/docs/app/docs/[[...slug]]/opengraph-image.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { ImageResponse } from 'next/og' -import { getDocBySlug } from '@/lib/docs' - -export const runtime = 'nodejs' -export const alt = 'react-fathom documentation' -export const size = { width: 1200, height: 630 } -export const contentType = 'image/png' - -export default async function OGImage({ params }: { params: Promise<{ slug?: string[] }> }) { - const { slug = [] } = await params - const doc = getDocBySlug(slug) - - const title = doc?.frontmatter.title || 'react-fathom' - const description = doc?.frontmatter.description || 'Privacy-focused analytics for React, Next.js, and React Native' - - return new ImageResponse( - ( -
- {/* Logo/Brand */} -
-
- react-fathom -
-
- - {/* Title */} -
- {title} -
- - {/* Description */} -
- {description} -
- - {/* Footer gradient line */} -
-
- ), - { - ...size, - } - ) -} diff --git a/docs/app/robots.ts b/docs/app/robots.ts index 3681416..0f7b85d 100644 --- a/docs/app/robots.ts +++ b/docs/app/robots.ts @@ -1,5 +1,7 @@ import { MetadataRoute } from 'next' +export const dynamic = 'force-static' + const SITE_URL = process.env.SITE_URL || 'https://react-fathom.com' export default function robots(): MetadataRoute.Robots { diff --git a/docs/app/sitemap.ts b/docs/app/sitemap.ts index 4208dd4..e52aa5c 100644 --- a/docs/app/sitemap.ts +++ b/docs/app/sitemap.ts @@ -1,6 +1,8 @@ import { MetadataRoute } from 'next' import { getAllDocSlugs } from '@/lib/docs' +export const dynamic = 'force-static' + const SITE_URL = process.env.SITE_URL || 'https://react-fathom.com' export default function sitemap(): MetadataRoute.Sitemap { diff --git a/docs/next.config.mjs b/docs/next.config.mjs index 0e18a3b..7f2813d 100644 --- a/docs/next.config.mjs +++ b/docs/next.config.mjs @@ -1,5 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + output: 'export', reactStrictMode: true, pageExtensions: ['js', 'jsx', 'ts', 'tsx'], eslint: { diff --git a/examples/next-app/next.config.js b/examples/next-app/next.config.js index a843cbe..42c10fd 100644 --- a/examples/next-app/next.config.js +++ b/examples/next-app/next.config.js @@ -1,5 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + output: 'export', reactStrictMode: true, } diff --git a/examples/next-app/package-lock.json b/examples/next-app/package-lock.json deleted file mode 100644 index 6f3d121..0000000 --- a/examples/next-app/package-lock.json +++ /dev/null @@ -1,1055 +0,0 @@ -{ - "name": "next-app-example", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "next-app-example", - "version": "0.1.0", - "dependencies": { - "fathom-client": "^3.6.0", - "next": "^16.1.0", - "react": "^19.2.3", - "react-dom": "^19.2.3", - "react-fathom": "file:../.." - }, - "devDependencies": { - "@types/node": "^22.0.0", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.0", - "typescript": "^5.6.0" - } - }, - "../..": { - "version": "0.1.10", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ryanhefner" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/ryanhefner" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ryanhefner" - } - ], - "license": "MIT", - "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.7", - "@babel/plugin-syntax-import-assertions": "^7.18.6", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.23.3", - "@babel/preset-typescript": "^7.23.3", - "@eslint/js": "^9.0.0", - "@rollup/plugin-babel": "^6.0.2", - "@rollup/plugin-commonjs": "^29.0.0", - "@rollup/plugin-json": "^6.0.0", - "@rollup/plugin-node-resolve": "^16.0.3", - "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^16.3.1", - "@types/react": "^19.2.7", - "@typescript-eslint/eslint-plugin": "^8.50.0", - "@vitest/coverage-v8": "^4.0.16", - "babel-core": "^7.0.0-bridge.0", - "babel-plugin-dev-expression": "^0.2.2", - "coveralls": "^3.1.1", - "eslint": "^9.0.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-import": "^2.32.0", - "eslint-plugin-prettier": "^5.0.1", - "eslint-plugin-react": "^7.34.1", - "eslint-plugin-react-hooks": "^7.0.1", - "fathom-client": "^3.6.0", - "globals": "^16.5.0", - "jsdom": "^27.3.0", - "next": "^16.1.0", - "prettier": "^3.0.3", - "react": "^19.2.3", - "react-dom": "^19.2.3", - "regenerator-runtime": "^0.14.0", - "rimraf": "^6.1.2", - "rollup": "^4.6.0", - "rollup-plugin-terser": "^7.0.2", - "snyk": "^1.437.3", - "typescript": "*", - "typescript-eslint": "^8.50.0", - "vitest": "^4.0.16" - }, - "peerDependencies": { - "fathom-client": ">=3.0.0", - "next": ">=10.0.0", - "react": ">=16.8", - "react-dom": ">=16.8" - }, - "peerDependenciesMeta": { - "next": { - "optional": true - } - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@next/env": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.1.tgz", - "integrity": "sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==", - "license": "MIT" - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.1.tgz", - "integrity": "sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.1.tgz", - "integrity": "sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.1.tgz", - "integrity": "sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.1.tgz", - "integrity": "sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.1.tgz", - "integrity": "sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.1.tgz", - "integrity": "sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.1.tgz", - "integrity": "sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.1.tgz", - "integrity": "sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@types/node": { - "version": "22.19.3", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", - "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.11", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001762", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", - "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/fathom-client": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/fathom-client/-/fathom-client-3.7.2.tgz", - "integrity": "sha512-sWtaNivhg7uwp/q1bUuIiNj4LeQZMEZ5NXXFFpZ8le4uDedAfQG84gPOdYehtVXbl+1yX2s8lmXZ2+IQ9a/xxA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.1.tgz", - "integrity": "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==", - "license": "MIT", - "dependencies": { - "@next/env": "16.1.1", - "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.8.3", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=20.9.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.1", - "@next/swc-darwin-x64": "16.1.1", - "@next/swc-linux-arm64-gnu": "16.1.1", - "@next/swc-linux-arm64-musl": "16.1.1", - "@next/swc-linux-x64-gnu": "16.1.1", - "@next/swc-linux-x64-musl": "16.1.1", - "@next/swc-win32-arm64-msvc": "16.1.1", - "@next/swc-win32-x64-msvc": "16.1.1", - "sharp": "^0.34.4" - }, - "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" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/react": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", - "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.3" - } - }, - "node_modules/react-fathom": { - "resolved": "../..", - "link": true - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "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 - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "dev": true, - "license": "MIT" - } - } -} diff --git a/examples/next-pages/package-lock.json b/examples/next-pages/package-lock.json deleted file mode 100644 index d062f4e..0000000 --- a/examples/next-pages/package-lock.json +++ /dev/null @@ -1,1055 +0,0 @@ -{ - "name": "next-pages-example", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "next-pages-example", - "version": "0.1.0", - "dependencies": { - "fathom-client": "^3.6.0", - "next": "^16.1.0", - "react": "^19.2.3", - "react-dom": "^19.2.3", - "react-fathom": "file:../.." - }, - "devDependencies": { - "@types/node": "^22.0.0", - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.0", - "typescript": "^5.6.0" - } - }, - "../..": { - "version": "0.1.10", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ryanhefner" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/ryanhefner" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/ryanhefner" - } - ], - "license": "MIT", - "devDependencies": { - "@babel/core": "^7.12.10", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.7", - "@babel/plugin-syntax-import-assertions": "^7.18.6", - "@babel/plugin-transform-runtime": "^7.12.10", - "@babel/preset-env": "^7.12.11", - "@babel/preset-react": "^7.23.3", - "@babel/preset-typescript": "^7.23.3", - "@eslint/js": "^9.0.0", - "@rollup/plugin-babel": "^6.0.2", - "@rollup/plugin-commonjs": "^29.0.0", - "@rollup/plugin-json": "^6.0.0", - "@rollup/plugin-node-resolve": "^16.0.3", - "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "^6.9.1", - "@testing-library/react": "^16.3.1", - "@types/react": "^19.2.7", - "@typescript-eslint/eslint-plugin": "^8.50.0", - "@vitest/coverage-v8": "^4.0.16", - "babel-core": "^7.0.0-bridge.0", - "babel-plugin-dev-expression": "^0.2.2", - "coveralls": "^3.1.1", - "eslint": "^9.0.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-import": "^2.32.0", - "eslint-plugin-prettier": "^5.0.1", - "eslint-plugin-react": "^7.34.1", - "eslint-plugin-react-hooks": "^7.0.1", - "fathom-client": "^3.6.0", - "globals": "^16.5.0", - "jsdom": "^27.3.0", - "next": "^16.1.0", - "prettier": "^3.0.3", - "react": "^19.2.3", - "react-dom": "^19.2.3", - "regenerator-runtime": "^0.14.0", - "rimraf": "^6.1.2", - "rollup": "^4.6.0", - "rollup-plugin-terser": "^7.0.2", - "snyk": "^1.437.3", - "typescript": "*", - "typescript-eslint": "^8.50.0", - "vitest": "^4.0.16" - }, - "peerDependencies": { - "fathom-client": ">=3.0.0", - "next": ">=10.0.0", - "react": ">=16.8", - "react-dom": ">=16.8" - }, - "peerDependenciesMeta": { - "next": { - "optional": true - } - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@next/env": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.1.tgz", - "integrity": "sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==", - "license": "MIT" - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.1.tgz", - "integrity": "sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.1.tgz", - "integrity": "sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.1.tgz", - "integrity": "sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.1.tgz", - "integrity": "sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.1.tgz", - "integrity": "sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.1.tgz", - "integrity": "sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.1.tgz", - "integrity": "sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.1.tgz", - "integrity": "sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@types/node": { - "version": "22.19.3", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", - "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.11", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001762", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", - "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/fathom-client": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/fathom-client/-/fathom-client-3.7.2.tgz", - "integrity": "sha512-sWtaNivhg7uwp/q1bUuIiNj4LeQZMEZ5NXXFFpZ8le4uDedAfQG84gPOdYehtVXbl+1yX2s8lmXZ2+IQ9a/xxA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/next/-/next-16.1.1.tgz", - "integrity": "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==", - "license": "MIT", - "dependencies": { - "@next/env": "16.1.1", - "@swc/helpers": "0.5.15", - "baseline-browser-mapping": "^2.8.3", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=20.9.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "16.1.1", - "@next/swc-darwin-x64": "16.1.1", - "@next/swc-linux-arm64-gnu": "16.1.1", - "@next/swc-linux-arm64-musl": "16.1.1", - "@next/swc-linux-x64-gnu": "16.1.1", - "@next/swc-linux-x64-musl": "16.1.1", - "@next/swc-win32-arm64-msvc": "16.1.1", - "@next/swc-win32-x64-msvc": "16.1.1", - "sharp": "^0.34.4" - }, - "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" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/react": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", - "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.3" - } - }, - "node_modules/react-fathom": { - "resolved": "../..", - "link": true - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "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 - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "dev": true, - "license": "MIT" - } - } -} From 4a7070e0b0037846783f10c221f4fd52ba7855de Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 15:28:29 +0000 Subject: [PATCH 16/54] Update Next.js examples to use published package version Change react-fathom dependency from file:../.. to ^0.2.0 to avoid duplicate React issues during development builds. --- examples/next-app/package.json | 2 +- examples/next-pages/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/next-app/package.json b/examples/next-app/package.json index 7f40711..4565dc9 100644 --- a/examples/next-app/package.json +++ b/examples/next-app/package.json @@ -12,7 +12,7 @@ "next": "^16.1.0", "react": "^19.2.3", "react-dom": "^19.2.3", - "react-fathom": "file:../..", + "react-fathom": "^0.2.0", "fathom-client": "^3.6.0" }, "devDependencies": { diff --git a/examples/next-pages/package.json b/examples/next-pages/package.json index ae53057..cb86d3b 100644 --- a/examples/next-pages/package.json +++ b/examples/next-pages/package.json @@ -12,7 +12,7 @@ "next": "^16.1.0", "react": "^19.2.3", "react-dom": "^19.2.3", - "react-fathom": "file:../..", + "react-fathom": "^0.2.0", "fathom-client": "^3.6.0" }, "devDependencies": { From 53f751b76b623fabe661dfd1eb0c075c401af29b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 17:26:29 +0000 Subject: [PATCH 17/54] Add React Router integration for pageview tracking - Add ReactRouterFathomTrackView component for React Router v6+ and Remix - Export via react-fathom/react-router path - Support options: disableAutoTrack, includeSearchParams, includeHash, transformUrl - Add react-router-dom as optional peer dependency - Include comprehensive test coverage (9 tests) --- package.json | 18 +- rollup.config.js | 26 ++ .../ReactRouterFathomTrackView.test.tsx | 297 ++++++++++++++++++ .../ReactRouterFathomTrackView.tsx | 161 ++++++++++ src/react-router/index.ts | 2 + yarn.lock | 25 ++ 6 files changed, 528 insertions(+), 1 deletion(-) create mode 100644 src/react-router/ReactRouterFathomTrackView.test.tsx create mode 100644 src/react-router/ReactRouterFathomTrackView.tsx create mode 100644 src/react-router/index.ts diff --git a/package.json b/package.json index 37cd172..4aa6a08 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "react", "react-native", "react-hooks", + "react-router", + "remix", "nextjs", "next.js", "typescript", @@ -91,6 +93,9 @@ ], "native": [ "./types/native/index.d.ts" + ], + "react-router": [ + "./types/react-router/index.d.ts" ] } }, @@ -112,6 +117,12 @@ "import": "./dist/es/native/index.js", "require": "./dist/cjs/native/index.cjs", "default": "./dist/es/native/index.js" + }, + "./react-router": { + "types": "./types/react-router/index.d.ts", + "import": "./dist/es/react-router/index.js", + "require": "./dist/cjs/react-router/index.cjs", + "default": "./dist/es/react-router/index.js" } }, "scripts": { @@ -134,7 +145,8 @@ "react": ">=16.8", "react-dom": ">=16.8", "react-native": ">=0.60.0", - "react-native-webview": ">=11.0.0" + "react-native-webview": ">=11.0.0", + "react-router-dom": ">=6.0.0" }, "peerDependenciesMeta": { "next": { @@ -146,6 +158,9 @@ "react-native-webview": { "optional": true }, + "react-router-dom": { + "optional": true + }, "fathom-client": { "optional": true } @@ -186,6 +201,7 @@ "prettier": "^3.0.3", "react": "^19.2.3", "react-dom": "^19.2.3", + "react-router-dom": "^7.0.0", "regenerator-runtime": "^0.14.0", "rimraf": "^6.1.2", "rollup": "^4.6.0", diff --git a/rollup.config.js b/rollup.config.js index ab07518..6da1dae 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -138,10 +138,12 @@ const nextExternal = [ 'next/navigation', ] const nativeExternal = ['react', 'react-native'] +const reactRouterExternal = ['fathom-client', 'react', 'react-router-dom'] const input = 'src/index.ts' const nextInput = 'src/next/index.ts' const nativeInput = 'src/native/index.ts' +const reactRouterInput = 'src/react-router/index.ts' export default [ // UMD - Minified @@ -238,4 +240,28 @@ export default [ external: makeExternal(nativeExternal), plugins: defaultPlugins, }, + // ES - react-router + { + input: reactRouterInput, + output: { + ...defaultOutputOptions, + dir: 'dist/es/react-router', + format: 'esm', + entryFileNames: '[name].js', + }, + external: makeExternal(reactRouterExternal), + plugins: defaultPlugins, + }, + // CJS - react-router + { + input: reactRouterInput, + output: { + ...defaultOutputOptions, + dir: 'dist/cjs/react-router', + format: 'cjs', + entryFileNames: '[name].cjs', + }, + external: makeExternal(reactRouterExternal), + plugins: defaultPlugins, + }, ] diff --git a/src/react-router/ReactRouterFathomTrackView.test.tsx b/src/react-router/ReactRouterFathomTrackView.test.tsx new file mode 100644 index 0000000..61f0842 --- /dev/null +++ b/src/react-router/ReactRouterFathomTrackView.test.tsx @@ -0,0 +1,297 @@ +import React from 'react' + +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { render, waitFor } from '@testing-library/react' +import { MemoryRouter, useNavigate } from 'react-router-dom' + +import { FathomProvider } from '../FathomProvider' +import { ReactRouterFathomTrackView } from './ReactRouterFathomTrackView' + +// Mock fathom-client +vi.mock('fathom-client', () => { + const mockFathomDefault = { + trackEvent: vi.fn(), + trackPageview: vi.fn(), + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + return { + default: mockFathomDefault, + } +}) + +// Helper component to trigger navigation +function NavigateButton({ to }: { to: string }) { + const navigate = useNavigate() + return +} + +describe('ReactRouterFathomTrackView', () => { + beforeEach(() => { + vi.clearAllMocks() + delete (window as { location?: unknown }).location + window.location = { + href: 'https://example.com/test-page', + origin: 'https://example.com', + } as Location + }) + + it('should track initial pageview on mount', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalled() + }) + + expect(trackPageviewSpy).toHaveBeenCalledWith({ + url: 'https://example.com/test-page', + }) + }) + + it('should track pageviews on route changes', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const { getByText } = render( + + + + + + , + ) + + // Wait for initial pageview + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalledTimes(1) + }) + + // Navigate to new page + getByText('Navigate').click() + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalledTimes(2) + }) + + expect(trackPageviewSpy).toHaveBeenLastCalledWith({ + url: 'https://example.com/new-page', + }) + }) + + it('should not track when disableAutoTrack is true', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + + + , + ) + + // Give time for effects to run + await new Promise((resolve) => setTimeout(resolve, 100)) + + expect(trackPageviewSpy).not.toHaveBeenCalled() + }) + + it('should include search params by default', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalled() + }) + + expect(trackPageviewSpy).toHaveBeenCalledWith({ + url: 'https://example.com/test-page?foo=bar', + }) + }) + + it('should exclude search params when includeSearchParams is false', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalled() + }) + + expect(trackPageviewSpy).toHaveBeenCalledWith({ + url: 'https://example.com/test-page', + }) + }) + + it('should include hash when includeHash is true', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalled() + }) + + expect(trackPageviewSpy).toHaveBeenCalledWith({ + url: 'https://example.com/test-page#section', + }) + }) + + it('should transform URL when transformUrl is provided', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const transformUrl = (url: string) => url.replace('/test-page', '/transformed') + + render( + + + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalled() + }) + + expect(trackPageviewSpy).toHaveBeenCalledWith({ + url: 'https://example.com/transformed', + }) + }) + + it('should skip tracking when transformUrl returns null', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const transformUrl = () => null + + render( + + + + + , + ) + + // Give time for effects to run + await new Promise((resolve) => setTimeout(resolve, 100)) + + expect(trackPageviewSpy).not.toHaveBeenCalled() + }) + + it('should have displayName', () => { + expect(ReactRouterFathomTrackView.displayName).toBe( + 'ReactRouterFathomTrackView', + ) + }) +}) diff --git a/src/react-router/ReactRouterFathomTrackView.tsx b/src/react-router/ReactRouterFathomTrackView.tsx new file mode 100644 index 0000000..8f3a139 --- /dev/null +++ b/src/react-router/ReactRouterFathomTrackView.tsx @@ -0,0 +1,161 @@ +import React, { useCallback, useEffect, useRef } from 'react' + +import { useLocation } from 'react-router-dom' + +import { useFathom } from '../hooks/useFathom' + +export interface ReactRouterFathomTrackViewProps { + /** + * Disable automatic pageview tracking on route changes + * @default false + */ + disableAutoTrack?: boolean + + /** + * Include search/query parameters in the tracked URL + * @default true + */ + includeSearchParams?: boolean + + /** + * Include hash fragment in the tracked URL + * @default false + */ + includeHash?: boolean + + /** + * Custom function to transform the URL before tracking. + * Useful for removing sensitive data or normalizing URLs. + * @param url The URL that would be tracked + * @returns The transformed URL to track, or null/undefined to skip tracking + */ + transformUrl?: (url: string) => string | null | undefined +} + +/** + * Component that tracks pageviews for React Router applications. + * Compatible with React Router v6+ and Remix. + * Must be used within a FathomProvider and a React Router context. + * + * @example + * ```tsx + * // App.tsx (React Router) + * import { BrowserRouter } from 'react-router-dom' + * import { FathomProvider } from 'react-fathom' + * import { ReactRouterFathomTrackView } from 'react-fathom/react-router' + * + * function App() { + * return ( + * + * + * + * ... + * + * + * ) + * } + * ``` + * + * @example + * ```tsx + * // root.tsx (Remix) + * import { FathomProvider } from 'react-fathom' + * import { ReactRouterFathomTrackView } from 'react-fathom/react-router' + * + * export default function App() { + * return ( + * + * + * + * + * ) + * } + * ``` + * + * @example + * ```tsx + * // With URL transformation (strip sensitive params) + * { + * const urlObj = new URL(url) + * urlObj.searchParams.delete('token') + * return urlObj.toString() + * }} + * /> + * ``` + */ +export const ReactRouterFathomTrackView: React.FC< + ReactRouterFathomTrackViewProps +> = ({ + disableAutoTrack = false, + includeSearchParams = true, + includeHash = false, + transformUrl, +}) => { + const hasTrackedInitialPageview = useRef(false) + const { trackPageview, client } = useFathom() + const location = useLocation() + + // Build URL from location parts + const buildUrl = useCallback(() => { + let url = window.location.origin + location.pathname + + if (includeSearchParams && location.search) { + url += location.search + } + + if (includeHash && location.hash) { + url += location.hash + } + + if (transformUrl) { + const transformed = transformUrl(url) + if (transformed === null || transformed === undefined) { + return null + } + url = transformed + } + + return url + }, [location.pathname, location.search, location.hash, includeSearchParams, includeHash, transformUrl]) + + // Track pageviews on route changes + useEffect(() => { + if (!trackPageview || !client || disableAutoTrack) { + return + } + + // Skip initial render - handled separately + if (!hasTrackedInitialPageview.current) { + return + } + + const url = buildUrl() + if (url) { + trackPageview({ url }) + } + }, [location.pathname, location.search, location.hash, trackPageview, client, disableAutoTrack, buildUrl]) + + // Track initial pageview + useEffect(() => { + if ( + !trackPageview || + !client || + disableAutoTrack || + hasTrackedInitialPageview.current + ) { + return + } + + hasTrackedInitialPageview.current = true + const url = buildUrl() + if (url) { + trackPageview({ url }) + } + }, [trackPageview, client, disableAutoTrack, buildUrl]) + + // This component doesn't render anything + return null +} + +ReactRouterFathomTrackView.displayName = 'ReactRouterFathomTrackView' diff --git a/src/react-router/index.ts b/src/react-router/index.ts new file mode 100644 index 0000000..c651901 --- /dev/null +++ b/src/react-router/index.ts @@ -0,0 +1,2 @@ +// React Router tracking component +export * from './ReactRouterFathomTrackView' diff --git a/yarn.lock b/yarn.lock index e6beb43..cfbcc08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2365,6 +2365,11 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cookie@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c" + integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ== + core-js-compat@^3.43.0: version "3.47.0" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.47.0.tgz#698224bbdbb6f2e3f39decdda4147b161e3772a3" @@ -4198,6 +4203,21 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-router-dom@^7.0.0: + version "7.12.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.12.0.tgz#0f2a059c6b2c4ae04474fe4171c59fb48b9fb8cf" + integrity sha512-pfO9fiBcpEfX4Tx+iTYKDtPbrSLLCbwJ5EqP+SPYQu1VYCXdy79GSj0wttR0U4cikVdlImZuEZ/9ZNCgoaxwBA== + dependencies: + react-router "7.12.0" + +react-router@7.12.0: + version "7.12.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.12.0.tgz#459a86862abbedd02e76e686751fe71f9fd73a4f" + integrity sha512-kTPDYPFzDVGIIGNLS5VJykK0HfHLY5MF3b+xj0/tTyNYL1gF1qs7u67Z9jEhQk2sQ98SUaHxlG31g1JtF7IfVw== + dependencies: + cookie "^1.0.1" + set-cookie-parser "^2.6.0" + react@^19.2.3: version "19.2.3" resolved "https://registry.yarnpkg.com/react/-/react-19.2.3.tgz#d83e5e8e7a258cf6b4fe28640515f99b87cd19b8" @@ -4475,6 +4495,11 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" +set-cookie-parser@^2.6.0: + version "2.7.2" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz#ccd08673a9ae5d2e44ea2a2de25089e67c7edf68" + integrity sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw== + set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" From 855930ba7b7fc96414c35b8222b493b2b5ce27e6 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 17:36:55 +0000 Subject: [PATCH 18/54] Add React Router documentation and update example - Add comprehensive React Router / Remix documentation - Update docs navigation to include React Router section - Add ReactRouterFathomTrackView to React example - Update README with React Router usage and links - Update getting-started with React Router peer dependency --- README.md | 25 +++ docs/content/_meta.ts | 1 + docs/content/getting-started.mdx | 2 + docs/content/react-router/index.mdx | 231 ++++++++++++++++++++++++++++ examples/react/src/main.tsx | 2 + 5 files changed, 261 insertions(+) create mode 100644 docs/content/react-router/index.mdx diff --git a/README.md b/README.md index 9704a17..1dd6a0b 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ The official `fathom-client` works, but: - βš›οΈ Hooks API and declarative tracking components - πŸ“± React Native with offline queuing and navigation tracking - ⚑ Next.js App Router and Pages Router support +- πŸ›€οΈ React Router v6+ and Remix support - 🌳 Tree-shakeable, fully typed (TypeScript) ## Usage @@ -104,6 +105,28 @@ function MyApp({ Component, pageProps }) { πŸ“– [Full Next.js guide](https://react-fathom.com/nextjs) +### React Router / Remix + +```tsx +// App.tsx or root.tsx +import { BrowserRouter } from 'react-router-dom' +import { FathomProvider } from 'react-fathom' +import { ReactRouterFathomTrackView } from 'react-fathom/react-router' + +function App() { + return ( + + + + ... + + + ) +} +``` + +πŸ“– [Full React Router guide](https://react-fathom.com/react-router) + ### Hooks ```tsx @@ -190,6 +213,7 @@ function App() { | `FathomProvider` | Basic React apps | | `NextFathomProviderApp` | Next.js App Router | | `NextFathomTrackViewPages` | Next.js Pages Router (add inside `FathomProvider`) | +| `ReactRouterFathomTrackView` | React Router v6+ / Remix (add inside `FathomProvider`) | | `NativeFathomProvider` | React Native | ### Hooks @@ -230,6 +254,7 @@ function App() { - πŸ“– [Getting Started](https://react-fathom.com/getting-started) - βš›οΈ [React Guide](https://react-fathom.com/react) +- πŸ›€οΈ [React Router Guide](https://react-fathom.com/react-router) - ⚑ [Next.js Guide](https://react-fathom.com/nextjs) - πŸ“± [React Native Guide](https://react-fathom.com/react-native) - πŸ“š [API Reference](https://react-fathom.com/api/providers) diff --git a/docs/content/_meta.ts b/docs/content/_meta.ts index 4063dc2..a414804 100644 --- a/docs/content/_meta.ts +++ b/docs/content/_meta.ts @@ -2,6 +2,7 @@ export default { index: 'Introduction', 'getting-started': 'Getting Started', react: 'React', + 'react-router': 'React Router', nextjs: 'Next.js', 'react-native': 'React Native', api: 'API Reference', diff --git a/docs/content/getting-started.mdx b/docs/content/getting-started.mdx index c635912..e7e785e 100644 --- a/docs/content/getting-started.mdx +++ b/docs/content/getting-started.mdx @@ -21,6 +21,7 @@ npm install react-fathom fathom-client | `react-dom` | >= 16.8 | Web only | | `fathom-client` | >= 3.0.0 | Web only (not needed for React Native) | | `next` | >= 10.0.0 | Next.js providers only | +| `react-router-dom` | >= 6.0.0 | React Router / Remix only | | `react-native` | >= 0.60.0 | React Native only | | `react-native-webview` | >= 11.0.0 | React Native only | @@ -69,6 +70,7 @@ Pageviews are tracked automatically by default. ## Next Steps - [React usage guide](/docs/react) β€” Hooks and declarative components +- [React Router integration](/docs/react-router) β€” Automatic pageview tracking for React Router and Remix - [Next.js integration](/docs/nextjs) β€” App Router and Pages Router - [React Native setup](/docs/react-native) β€” Mobile apps with offline queuing - [API Reference](/docs/api/providers) β€” Full provider props and options diff --git a/docs/content/react-router/index.mdx b/docs/content/react-router/index.mdx new file mode 100644 index 0000000..6d440db --- /dev/null +++ b/docs/content/react-router/index.mdx @@ -0,0 +1,231 @@ +--- +title: "React Router" +description: "Using react-fathom with React Router and Remix for automatic pageview tracking." +--- + +# React Router + +This guide covers integrating react-fathom with React Router v6+ and Remix applications for automatic pageview tracking on route changes. + +## Installation + +Make sure you have both `react-fathom` and `react-router-dom` installed: + +```bash +npm install react-fathom react-router-dom fathom-client +``` + +## Quick Start + +Import `ReactRouterFathomTrackView` from `react-fathom/react-router` and add it inside your `FathomProvider`: + +```tsx +import { BrowserRouter } from 'react-router-dom' +import { FathomProvider } from 'react-fathom' +import { ReactRouterFathomTrackView } from 'react-fathom/react-router' + +function App() { + return ( + + + + + } /> + } /> + } /> + + + + ) +} +``` + +The component will automatically track: +- Initial pageview on app load +- Subsequent pageviews on every route change + +## Remix Integration + +For Remix applications, add the component to your root layout: + +```tsx +// app/root.tsx +import { Outlet } from '@remix-run/react' +import { FathomProvider } from 'react-fathom' +import { ReactRouterFathomTrackView } from 'react-fathom/react-router' + +export default function App() { + return ( + + + + + + + + + + + + + + ) +} +``` + +## Component Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `disableAutoTrack` | `boolean` | `false` | Disable automatic pageview tracking | +| `includeSearchParams` | `boolean` | `true` | Include query parameters in tracked URL | +| `includeHash` | `boolean` | `false` | Include hash fragment in tracked URL | +| `transformUrl` | `(url: string) => string \| null` | β€” | Transform URL before tracking | + +## Configuration Options + +### Including/Excluding URL Parts + +By default, search parameters are included but hash fragments are not: + +```tsx +// Track /products?category=shoes (includes search params) + + +// Track /products (excludes search params) + + +// Track /docs#installation (includes hash) + +``` + +### URL Transformation + +Use `transformUrl` to modify URLs before tracking. This is useful for: +- Removing sensitive parameters (tokens, session IDs) +- Normalizing URLs +- Skipping certain routes + +```tsx +// Remove sensitive parameters + { + const urlObj = new URL(url) + urlObj.searchParams.delete('token') + urlObj.searchParams.delete('session') + return urlObj.toString() + }} +/> + +// Normalize dynamic IDs to patterns + { + return url.replace(/\/users\/\d+/, '/users/:id') + }} +/> + +// Skip tracking certain routes (return null) + { + if (url.includes('/admin')) { + return null // Skip tracking admin pages + } + return url + }} +/> +``` + +### Disabling Auto-Tracking + +If you need manual control over pageview tracking: + +```tsx +import { useFathom } from 'react-fathom' +import { ReactRouterFathomTrackView } from 'react-fathom/react-router' + +function App() { + return ( + + {/* Disable auto-tracking, handle manually */} + + ... + + ) +} + +// Manual tracking in a specific component +function SpecialPage() { + const { trackPageview } = useFathom() + + useEffect(() => { + // Custom tracking logic + trackPageview({ url: '/special-page-custom' }) + }, []) + + return
Special Page
+} +``` + +## Combining with Event Tracking + +Use alongside other react-fathom features for comprehensive analytics: + +```tsx +import { useFathom, TrackClick, TrackVisible } from 'react-fathom' +import { ReactRouterFathomTrackView } from 'react-fathom/react-router' + +function App() { + return ( + + + {/* Automatic pageview tracking */} + + + + } /> + } /> + + + + ) +} + +function Pricing() { + const { trackEvent } = useFathom() + + return ( +
+ {/* Track when pricing section becomes visible */} + +

Pricing

+
+ + {/* Track CTA clicks */} + + + +
+ ) +} +``` + +## TypeScript + +The component is fully typed. Import types if needed: + +```tsx +import type { ReactRouterFathomTrackViewProps } from 'react-fathom/react-router' + +const config: ReactRouterFathomTrackViewProps = { + includeSearchParams: true, + includeHash: false, + transformUrl: (url) => url.toLowerCase(), +} +``` + +## Next Steps + +- [React Guide](/docs/react) β€” Hooks and components for React +- [API Reference: Hooks](/docs/api/hooks) β€” Full hook documentation +- [API Reference: Components](/docs/api/components) β€” Full component documentation +- [Testing Guide](/docs/guides/testing) β€” Mock clients for testing diff --git a/examples/react/src/main.tsx b/examples/react/src/main.tsx index a8bdd95..054248c 100644 --- a/examples/react/src/main.tsx +++ b/examples/react/src/main.tsx @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client' import { ChakraProvider, defaultSystem } from '@chakra-ui/react' import { BrowserRouter } from 'react-router-dom' import { FathomProvider } from 'react-fathom' +import { ReactRouterFathomTrackView } from 'react-fathom/react-router' import App from './App' const siteId = import.meta.env.VITE_FATHOM_SITE_ID @@ -13,6 +14,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render( {siteId ? ( + ) : ( From cced3c3c3735c7b79fffd35da731f262a559d3cf Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 18:49:25 +0000 Subject: [PATCH 19/54] Add Gatsby integration for pageview tracking - Add GatsbyFathomTrackView component using @reach/router's globalHistory - Add gatsby-browser.js helpers (createGatsbyFathomPlugins, trackGatsbyPageview) - Add comprehensive tests for Gatsby integration - Create Gatsby documentation page - Update README and getting-started with Gatsby information --- README.md | 20 ++ docs/content/_meta.ts | 1 + docs/content/gatsby/index.mdx | 321 ++++++++++++++++++++ docs/content/getting-started.mdx | 3 + package.json | 20 ++ rollup.config.js | 26 ++ src/gatsby/GatsbyFathomTrackView.test.tsx | 343 ++++++++++++++++++++++ src/gatsby/GatsbyFathomTrackView.tsx | 148 ++++++++++ src/gatsby/gatsbyBrowser.ts | 185 ++++++++++++ src/gatsby/index.ts | 5 + yarn.lock | 60 +++- 11 files changed, 1130 insertions(+), 2 deletions(-) create mode 100644 docs/content/gatsby/index.mdx create mode 100644 src/gatsby/GatsbyFathomTrackView.test.tsx create mode 100644 src/gatsby/GatsbyFathomTrackView.tsx create mode 100644 src/gatsby/gatsbyBrowser.ts create mode 100644 src/gatsby/index.ts diff --git a/README.md b/README.md index 1dd6a0b..f146b78 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ The official `fathom-client` works, but: - πŸ“± React Native with offline queuing and navigation tracking - ⚑ Next.js App Router and Pages Router support - πŸ›€οΈ React Router v6+ and Remix support +- 🏠 Gatsby support with @reach/router integration - 🌳 Tree-shakeable, fully typed (TypeScript) ## Usage @@ -127,6 +128,23 @@ function App() { πŸ“– [Full React Router guide](https://react-fathom.com/react-router) +### Gatsby + +```tsx +// gatsby-browser.js or Layout component +import { FathomProvider } from 'react-fathom' +import { GatsbyFathomTrackView } from 'react-fathom/gatsby' + +export const wrapRootElement = ({ element }) => ( + + + {element} + +) +``` + +πŸ“– [Full Gatsby guide](https://react-fathom.com/gatsby) + ### Hooks ```tsx @@ -214,6 +232,7 @@ function App() { | `NextFathomProviderApp` | Next.js App Router | | `NextFathomTrackViewPages` | Next.js Pages Router (add inside `FathomProvider`) | | `ReactRouterFathomTrackView` | React Router v6+ / Remix (add inside `FathomProvider`) | +| `GatsbyFathomTrackView` | Gatsby (add inside `FathomProvider`) | | `NativeFathomProvider` | React Native | ### Hooks @@ -255,6 +274,7 @@ function App() { - πŸ“– [Getting Started](https://react-fathom.com/getting-started) - βš›οΈ [React Guide](https://react-fathom.com/react) - πŸ›€οΈ [React Router Guide](https://react-fathom.com/react-router) +- 🏠 [Gatsby Guide](https://react-fathom.com/gatsby) - ⚑ [Next.js Guide](https://react-fathom.com/nextjs) - πŸ“± [React Native Guide](https://react-fathom.com/react-native) - πŸ“š [API Reference](https://react-fathom.com/api/providers) diff --git a/docs/content/_meta.ts b/docs/content/_meta.ts index a414804..aa7e341 100644 --- a/docs/content/_meta.ts +++ b/docs/content/_meta.ts @@ -3,6 +3,7 @@ export default { 'getting-started': 'Getting Started', react: 'React', 'react-router': 'React Router', + gatsby: 'Gatsby', nextjs: 'Next.js', 'react-native': 'React Native', api: 'API Reference', diff --git a/docs/content/gatsby/index.mdx b/docs/content/gatsby/index.mdx new file mode 100644 index 0000000..4ebea4d --- /dev/null +++ b/docs/content/gatsby/index.mdx @@ -0,0 +1,321 @@ +--- +title: "Gatsby" +description: "Integrate react-fathom with Gatsby for automatic pageview tracking." +--- + +# Gatsby Integration + +This guide covers integrating `react-fathom` with Gatsby applications for automatic pageview tracking. + +## Installation + +Install the required packages: + +```bash npm2yarn +npm install react-fathom fathom-client +``` + +## Setup Options + +There are two ways to integrate react-fathom with Gatsby: + +1. **Component-based** (recommended) β€” Use `GatsbyFathomTrackView` in your layout +2. **gatsby-browser.js** β€” Use helpers in your gatsby-browser.js file + +## Option 1: Component-Based (Recommended) + +This approach uses a React component in your layout file, consistent with other react-fathom integrations. + +### Layout Setup + +```tsx +// src/components/Layout.tsx +import React from 'react' +import { FathomProvider } from 'react-fathom' +import { GatsbyFathomTrackView } from 'react-fathom/gatsby' + +interface LayoutProps { + children: React.ReactNode +} + +export function Layout({ children }: LayoutProps) { + return ( + + + {children} + + ) +} +``` + +### Wrap Pages with Layout + +Use the layout in your pages: + +```tsx +// src/pages/index.tsx +import React from 'react' +import { Layout } from '../components/Layout' + +export default function HomePage() { + return ( + +

Welcome to my Gatsby site

+
+ ) +} +``` + +Or use `gatsby-browser.js` to wrap all pages: + +```js +// gatsby-browser.js +import React from 'react' +import { FathomProvider } from 'react-fathom' +import { GatsbyFathomTrackView } from 'react-fathom/gatsby' + +export const wrapRootElement = ({ element }) => ( + + + {element} + +) +``` + +## Option 2: gatsby-browser.js Helpers + +For users who prefer configuring analytics entirely in `gatsby-browser.js`, react-fathom provides helper functions. + +### Using createGatsbyFathomPlugins + +```js +// gatsby-browser.js +import { createGatsbyFathomPlugins } from 'react-fathom/gatsby' + +const fathomPlugins = createGatsbyFathomPlugins({ + siteId: 'YOUR_SITE_ID', + // Optional configuration + includedDomains: ['yourdomain.com'], + excludedDomains: ['staging.yourdomain.com'], + spa: 'auto', + honorDNT: false, +}) + +export const onClientEntry = fathomPlugins.onClientEntry +export const onRouteUpdate = fathomPlugins.onRouteUpdate +``` + +### Manual Configuration + +For more control, use `trackGatsbyPageview` directly: + +```js +// gatsby-browser.js +import Fathom from 'fathom-client' +import { trackGatsbyPageview } from 'react-fathom/gatsby' + +export const onClientEntry = () => { + Fathom.load('YOUR_SITE_ID', { + includedDomains: ['yourdomain.com'], + }) +} + +export const onRouteUpdate = ({ location }) => { + trackGatsbyPageview(Fathom, location, { + includeSearchParams: true, + includeHash: false, + }) +} +``` + +## GatsbyFathomTrackView Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `disableAutoTrack` | `boolean` | `false` | Disable automatic pageview tracking | +| `includeSearchParams` | `boolean` | `true` | Include query parameters in tracked URLs | +| `includeHash` | `boolean` | `false` | Include URL hash in tracked URLs | +| `transformUrl` | `(url: string) => string \| null` | β€” | Transform URL before tracking; return `null` to skip | + +## Tracking Custom Events + +Use the `useFathom` hook in any component: + +```tsx +import React from 'react' +import { useFathom } from 'react-fathom' + +function NewsletterForm() { + const { trackEvent } = useFathom() + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault() + trackEvent('newsletter-signup') + // Submit form... + } + + return ( +
+ + +
+ ) +} +``` + +## URL Transformation + +### Sanitizing Dynamic Routes + +Remove dynamic segments from URLs for cleaner analytics: + +```tsx + { + // /blog/post-123 β†’ /blog/[slug] + return url.replace(/\/blog\/[^/]+/, '/blog/[slug]') + }} +/> +``` + +### Excluding Routes + +Skip tracking for specific routes: + +```tsx + { + // Don't track admin pages + if (url.includes('/admin')) { + return null + } + return url + }} +/> +``` + +## Environment Variables + +Store your site ID in environment variables: + +```bash +# .env.development +GATSBY_FATHOM_SITE_ID=YOUR_SITE_ID +``` + +```tsx + +``` + +## Local Development + +Enable tracking on localhost: + +```tsx + +``` + +Or with gatsby-browser.js helpers: + +```js +const fathomPlugins = createGatsbyFathomPlugins({ + siteId: 'YOUR_SITE_ID', + includedDomains: ['localhost', 'yourdomain.com'], +}) +``` + +## TypeScript Support + +All Gatsby exports are fully typed. Import types as needed: + +```tsx +import type { GatsbyFathomTrackViewProps } from 'react-fathom/gatsby' +``` + +## How It Works + +Gatsby uses `@reach/router` internally. The `GatsbyFathomTrackView` component listens to route changes via `@reach/router`'s `globalHistory`: + +1. On mount, tracks the initial pageview +2. Listens for `PUSH` and `POP` history actions +3. Constructs the full URL from `window.location.origin` and the route path +4. Calls `trackPageview` with the constructed URL + +The `gatsby-browser.js` helpers use Gatsby's built-in `onRouteUpdate` API, which fires after each client-side navigation. + +## Complete Example + +Here's a full Gatsby setup with react-fathom: + +```tsx +// src/components/Layout.tsx +import React from 'react' +import { FathomProvider } from 'react-fathom' +import { GatsbyFathomTrackView } from 'react-fathom/gatsby' + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + { + // Normalize blog post URLs + return url.replace(/\/blog\/[^/]+$/, '/blog/[slug]') + }} + /> +
...
+
{children}
+
...
+
+ ) +} +``` + +```tsx +// src/pages/index.tsx +import React from 'react' +import { useFathom } from 'react-fathom' +import { Layout } from '../components/Layout' + +export default function HomePage() { + const { trackEvent } = useFathom() + + return ( + +

Welcome

+ +
+ ) +} +``` + +## Troubleshooting + +### Events not appearing in Fathom? + +1. Verify your site ID matches your [Fathom dashboard](https://app.usefathom.com) +2. Check for ad blockers (test in incognito mode) +3. Add `localhost` to `includedDomains` for local testing +4. Ensure `FathomProvider` wraps your entire app + +### Duplicate pageviews? + +If using both the component approach and gatsby-browser.js helpers, you may get duplicate tracking. Use only one approach. + +### Route changes not tracking? + +Ensure `GatsbyFathomTrackView` is rendered inside `FathomProvider` and that the provider persists across route changes (use gatsby-browser.js `wrapRootElement`). diff --git a/docs/content/getting-started.mdx b/docs/content/getting-started.mdx index e7e785e..2b15806 100644 --- a/docs/content/getting-started.mdx +++ b/docs/content/getting-started.mdx @@ -22,6 +22,8 @@ npm install react-fathom fathom-client | `fathom-client` | >= 3.0.0 | Web only (not needed for React Native) | | `next` | >= 10.0.0 | Next.js providers only | | `react-router-dom` | >= 6.0.0 | React Router / Remix only | +| `@reach/router` | >= 1.3.0 | Gatsby only | +| `gatsby` | >= 4.0.0 | Gatsby only | | `react-native` | >= 0.60.0 | React Native only | | `react-native-webview` | >= 11.0.0 | React Native only | @@ -71,6 +73,7 @@ Pageviews are tracked automatically by default. - [React usage guide](/docs/react) β€” Hooks and declarative components - [React Router integration](/docs/react-router) β€” Automatic pageview tracking for React Router and Remix +- [Gatsby integration](/docs/gatsby) β€” Automatic pageview tracking for Gatsby - [Next.js integration](/docs/nextjs) β€” App Router and Pages Router - [React Native setup](/docs/react-native) β€” Mobile apps with offline queuing - [API Reference](/docs/api/providers) β€” Full provider props and options diff --git a/package.json b/package.json index 4aa6a08..66fa462 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "react-hooks", "react-router", "remix", + "gatsby", "nextjs", "next.js", "typescript", @@ -96,6 +97,9 @@ ], "react-router": [ "./types/react-router/index.d.ts" + ], + "gatsby": [ + "./types/gatsby/index.d.ts" ] } }, @@ -123,6 +127,12 @@ "import": "./dist/es/react-router/index.js", "require": "./dist/cjs/react-router/index.cjs", "default": "./dist/es/react-router/index.js" + }, + "./gatsby": { + "types": "./types/gatsby/index.d.ts", + "import": "./dist/es/gatsby/index.js", + "require": "./dist/cjs/gatsby/index.cjs", + "default": "./dist/es/gatsby/index.js" } }, "scripts": { @@ -140,7 +150,9 @@ "test:ci": "vitest run --coverage" }, "peerDependencies": { + "@reach/router": ">=1.3.0", "fathom-client": ">=3.0.0", + "gatsby": ">=4.0.0", "next": ">=10.0.0", "react": ">=16.8", "react-dom": ">=16.8", @@ -149,6 +161,12 @@ "react-router-dom": ">=6.0.0" }, "peerDependenciesMeta": { + "@reach/router": { + "optional": true + }, + "gatsby": { + "optional": true + }, "next": { "optional": true }, @@ -166,6 +184,7 @@ } }, "devDependencies": { + "@reach/router": "^1.3.4", "@babel/core": "^7.12.10", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-object-rest-spread": "^7.20.7", @@ -182,6 +201,7 @@ "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.1", + "@types/reach__router": "^1.3.15", "@types/react": "^19.2.7", "@typescript-eslint/eslint-plugin": "^8.50.0", "@vitest/coverage-v8": "^4.0.16", diff --git a/rollup.config.js b/rollup.config.js index 6da1dae..85d6282 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -139,11 +139,13 @@ const nextExternal = [ ] const nativeExternal = ['react', 'react-native'] const reactRouterExternal = ['fathom-client', 'react', 'react-router-dom'] +const gatsbyExternal = ['fathom-client', 'react', '@reach/router'] const input = 'src/index.ts' const nextInput = 'src/next/index.ts' const nativeInput = 'src/native/index.ts' const reactRouterInput = 'src/react-router/index.ts' +const gatsbyInput = 'src/gatsby/index.ts' export default [ // UMD - Minified @@ -264,4 +266,28 @@ export default [ external: makeExternal(reactRouterExternal), plugins: defaultPlugins, }, + // ES - gatsby + { + input: gatsbyInput, + output: { + ...defaultOutputOptions, + dir: 'dist/es/gatsby', + format: 'esm', + entryFileNames: '[name].js', + }, + external: makeExternal(gatsbyExternal), + plugins: defaultPlugins, + }, + // CJS - gatsby + { + input: gatsbyInput, + output: { + ...defaultOutputOptions, + dir: 'dist/cjs/gatsby', + format: 'cjs', + entryFileNames: '[name].cjs', + }, + external: makeExternal(gatsbyExternal), + plugins: defaultPlugins, + }, ] diff --git a/src/gatsby/GatsbyFathomTrackView.test.tsx b/src/gatsby/GatsbyFathomTrackView.test.tsx new file mode 100644 index 0000000..be8ccfa --- /dev/null +++ b/src/gatsby/GatsbyFathomTrackView.test.tsx @@ -0,0 +1,343 @@ +import React from 'react' + +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { render, waitFor } from '@testing-library/react' + +import { FathomProvider } from '../FathomProvider' +import { GatsbyFathomTrackView } from './GatsbyFathomTrackView' + +// Mock @reach/router's globalHistory +const mockListeners: Array<(args: { location: { pathname: string; search: string; hash: string }; action: string }) => void> = [] + +vi.mock('@reach/router', () => ({ + globalHistory: { + listen: (callback: (args: { location: { pathname: string; search: string; hash: string }; action: string }) => void) => { + mockListeners.push(callback) + return () => { + const index = mockListeners.indexOf(callback) + if (index > -1) { + mockListeners.splice(index, 1) + } + } + }, + }, +})) + +// Mock fathom-client +vi.mock('fathom-client', () => { + const mockFathomDefault = { + trackEvent: vi.fn(), + trackPageview: vi.fn(), + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + return { + default: mockFathomDefault, + } +}) + +// Helper to simulate route changes +function simulateRouteChange(pathname: string, search = '', hash = '', action = 'PUSH') { + mockListeners.forEach((listener) => { + listener({ + location: { pathname, search, hash }, + action, + }) + }) +} + +describe('GatsbyFathomTrackView', () => { + beforeEach(() => { + vi.clearAllMocks() + mockListeners.length = 0 + delete (window as { location?: unknown }).location + window.location = { + href: 'https://example.com/test-page', + origin: 'https://example.com', + pathname: '/test-page', + search: '', + hash: '', + } as Location + }) + + it('should track initial pageview on mount', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalled() + }) + + expect(trackPageviewSpy).toHaveBeenCalledWith({ + url: 'https://example.com/test-page', + }) + }) + + it('should track pageviews on route changes', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + , + ) + + // Wait for initial pageview + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalledTimes(1) + }) + + // Simulate route change + simulateRouteChange('/new-page') + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalledTimes(2) + }) + + expect(trackPageviewSpy).toHaveBeenLastCalledWith({ + url: 'https://example.com/new-page', + }) + }) + + it('should track on POP action (back/forward navigation)', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalledTimes(1) + }) + + // Simulate back navigation + simulateRouteChange('/previous-page', '', '', 'POP') + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalledTimes(2) + }) + }) + + it('should not track when disableAutoTrack is true', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + render( + + + , + ) + + // Give time for effects to run + await new Promise((resolve) => setTimeout(resolve, 100)) + + expect(trackPageviewSpy).not.toHaveBeenCalled() + }) + + it('should include search params by default', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + window.location = { + ...window.location, + search: '?foo=bar', + } as Location + + render( + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalled() + }) + + expect(trackPageviewSpy).toHaveBeenCalledWith({ + url: 'https://example.com/test-page?foo=bar', + }) + }) + + it('should include hash when includeHash is true', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + window.location = { + ...window.location, + hash: '#section', + } as Location + + render( + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalled() + }) + + expect(trackPageviewSpy).toHaveBeenCalledWith({ + url: 'https://example.com/test-page#section', + }) + }) + + it('should transform URL when transformUrl is provided', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const transformUrl = (url: string) => url.replace('/test-page', '/transformed') + + render( + + + , + ) + + await waitFor(() => { + expect(trackPageviewSpy).toHaveBeenCalled() + }) + + expect(trackPageviewSpy).toHaveBeenCalledWith({ + url: 'https://example.com/transformed', + }) + }) + + it('should skip tracking when transformUrl returns null', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const transformUrl = () => null + + render( + + + , + ) + + // Give time for effects to run + await new Promise((resolve) => setTimeout(resolve, 100)) + + expect(trackPageviewSpy).not.toHaveBeenCalled() + }) + + it('should have displayName', () => { + expect(GatsbyFathomTrackView.displayName).toBe('GatsbyFathomTrackView') + }) + + it('should cleanup listener on unmount', async () => { + const trackPageviewSpy = vi.fn() + const client = { + trackEvent: vi.fn(), + trackPageview: trackPageviewSpy, + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const { unmount } = render( + + + , + ) + + await waitFor(() => { + expect(mockListeners.length).toBe(1) + }) + + unmount() + + expect(mockListeners.length).toBe(0) + }) +}) diff --git a/src/gatsby/GatsbyFathomTrackView.tsx b/src/gatsby/GatsbyFathomTrackView.tsx new file mode 100644 index 0000000..427cf79 --- /dev/null +++ b/src/gatsby/GatsbyFathomTrackView.tsx @@ -0,0 +1,148 @@ +import React, { useEffect, useRef } from 'react' + +import { globalHistory } from '@reach/router' + +import { useFathom } from '../hooks/useFathom' + +export interface GatsbyFathomTrackViewProps { + /** + * Disable automatic pageview tracking on route changes + * @default false + */ + disableAutoTrack?: boolean + + /** + * Include search/query parameters in the tracked URL + * @default true + */ + includeSearchParams?: boolean + + /** + * Include hash fragment in the tracked URL + * @default false + */ + includeHash?: boolean + + /** + * Custom function to transform the URL before tracking. + * Useful for removing sensitive data or normalizing URLs. + * @param url The URL that would be tracked + * @returns The transformed URL to track, or null/undefined to skip tracking + */ + transformUrl?: (url: string) => string | null | undefined +} + +/** + * Component that tracks pageviews for Gatsby applications. + * Uses @reach/router's globalHistory to listen for route changes. + * Must be used within a FathomProvider. + * + * @example + * ```tsx + * // src/components/Layout.tsx + * import { FathomProvider } from 'react-fathom' + * import { GatsbyFathomTrackView } from 'react-fathom/gatsby' + * + * export default function Layout({ children }) { + * return ( + * + * + * {children} + * + * ) + * } + * ``` + * + * @example + * ```tsx + * // With URL transformation + * { + * // Strip query params for cleaner analytics + * return url.split('?')[0] + * }} + * /> + * ``` + */ +export const GatsbyFathomTrackView: React.FC = ({ + disableAutoTrack = false, + includeSearchParams = true, + includeHash = false, + transformUrl, +}) => { + const hasTrackedInitialPageview = useRef(false) + const { trackPageview, client } = useFathom() + + // Build URL from location + const buildUrl = (location: { pathname: string; search: string; hash: string }) => { + if (typeof window === 'undefined') return null + + let url = window.location.origin + location.pathname + + if (includeSearchParams && location.search) { + url += location.search + } + + if (includeHash && location.hash) { + url += location.hash + } + + if (transformUrl) { + const transformed = transformUrl(url) + if (transformed === null || transformed === undefined) { + return null + } + url = transformed + } + + return url + } + + // Track initial pageview + useEffect(() => { + if ( + !trackPageview || + !client || + disableAutoTrack || + hasTrackedInitialPageview.current || + typeof window === 'undefined' + ) { + return + } + + hasTrackedInitialPageview.current = true + const url = buildUrl({ + pathname: window.location.pathname, + search: window.location.search, + hash: window.location.hash, + }) + if (url) { + trackPageview({ url }) + } + }, [trackPageview, client, disableAutoTrack]) + + // Listen to route changes via globalHistory + useEffect(() => { + if (!trackPageview || !client || disableAutoTrack) { + return + } + + const unsubscribe = globalHistory.listen(({ location, action }) => { + // Only track on PUSH and POP actions (actual navigation) + if (action === 'PUSH' || action === 'POP') { + const url = buildUrl(location) + if (url) { + trackPageview({ url }) + } + } + }) + + return () => { + unsubscribe() + } + }, [trackPageview, client, disableAutoTrack, includeSearchParams, includeHash, transformUrl]) + + return null +} + +GatsbyFathomTrackView.displayName = 'GatsbyFathomTrackView' diff --git a/src/gatsby/gatsbyBrowser.ts b/src/gatsby/gatsbyBrowser.ts new file mode 100644 index 0000000..af996a8 --- /dev/null +++ b/src/gatsby/gatsbyBrowser.ts @@ -0,0 +1,185 @@ +import type * as FathomType from 'fathom-client' + +export interface GatsbyFathomOptions { + /** + * Your Fathom site ID + */ + siteId: string + + /** + * Include search/query parameters in the tracked URL + * @default true + */ + includeSearchParams?: boolean + + /** + * Include hash fragment in the tracked URL + * @default false + */ + includeHash?: boolean + + /** + * Custom function to transform the URL before tracking + * @param url The URL that would be tracked + * @returns The transformed URL to track, or null/undefined to skip tracking + */ + transformUrl?: (url: string) => string | null | undefined + + /** + * Additional options to pass to Fathom's load function + */ + loadOptions?: Parameters[1] +} + +/** + * Creates Gatsby browser API handlers for Fathom Analytics. + * Use in gatsby-browser.js to automatically track pageviews. + * + * @example + * ```js + * // gatsby-browser.js + * import { createGatsbyFathomPlugins } from 'react-fathom/gatsby' + * + * const fathomPlugins = createGatsbyFathomPlugins({ + * siteId: 'YOUR_SITE_ID', + * loadOptions: { + * includedDomains: ['yourdomain.com'], + * }, + * }) + * + * export const onClientEntry = fathomPlugins.onClientEntry + * export const onRouteUpdate = fathomPlugins.onRouteUpdate + * ``` + * + * @example + * ```js + * // Or use spread syntax to export all handlers + * import { createGatsbyFathomPlugins } from 'react-fathom/gatsby' + * + * const { onClientEntry, onRouteUpdate } = createGatsbyFathomPlugins({ + * siteId: process.env.GATSBY_FATHOM_SITE_ID, + * }) + * + * export { onClientEntry, onRouteUpdate } + * ``` + */ +export function createGatsbyFathomPlugins(options: GatsbyFathomOptions) { + const { + siteId, + includeSearchParams = true, + includeHash = false, + transformUrl, + loadOptions, + } = options + + let fathomClient: typeof FathomType | null = null + + const buildUrl = (location: { pathname: string; search?: string; hash?: string }) => { + if (typeof window === 'undefined') return null + + let url = window.location.origin + location.pathname + + if (includeSearchParams && location.search) { + url += location.search + } + + if (includeHash && location.hash) { + url += location.hash + } + + if (transformUrl) { + const transformed = transformUrl(url) + if (transformed === null || transformed === undefined) { + return null + } + url = transformed + } + + return url + } + + return { + /** + * Called when the Gatsby browser runtime first starts. + * Loads the Fathom script. + */ + onClientEntry: async () => { + if (typeof window === 'undefined') return + + // Dynamically import fathom-client + const Fathom = await import('fathom-client') + fathomClient = Fathom + + fathomClient.load(siteId, { + auto: false, // We handle tracking manually + ...loadOptions, + }) + }, + + /** + * Called when the user changes routes. + * Tracks a pageview for the new route. + */ + onRouteUpdate: ({ location }: { location: { pathname: string; search?: string; hash?: string } }) => { + if (!fathomClient) return + + const url = buildUrl(location) + if (url) { + fathomClient.trackPageview({ url }) + } + }, + } +} + +/** + * Simplified helper to track a pageview in Gatsby. + * Use this if you want more control over when tracking happens. + * + * @example + * ```js + * // gatsby-browser.js + * import * as Fathom from 'fathom-client' + * import { trackGatsbyPageview } from 'react-fathom/gatsby' + * + * export const onClientEntry = () => { + * Fathom.load('YOUR_SITE_ID', { auto: false }) + * } + * + * export const onRouteUpdate = ({ location }) => { + * trackGatsbyPageview(Fathom, location) + * } + * ``` + */ +export function trackGatsbyPageview( + fathomClient: typeof FathomType, + location: { pathname: string; search?: string; hash?: string }, + options?: { + includeSearchParams?: boolean + includeHash?: boolean + transformUrl?: (url: string) => string | null | undefined + } +) { + if (typeof window === 'undefined') return + + const { includeSearchParams = true, includeHash = false, transformUrl } = options || {} + + let url = window.location.origin + location.pathname + + if (includeSearchParams && location.search) { + url += location.search + } + + if (includeHash && location.hash) { + url += location.hash + } + + if (transformUrl) { + const transformed = transformUrl(url) + if (transformed === null || transformed === undefined) { + return + } + url = transformed + } + + fathomClient.trackPageview({ url }) +} diff --git a/src/gatsby/index.ts b/src/gatsby/index.ts new file mode 100644 index 0000000..e9e6ba0 --- /dev/null +++ b/src/gatsby/index.ts @@ -0,0 +1,5 @@ +// Gatsby tracking component +export * from './GatsbyFathomTrackView' + +// Gatsby browser API helpers +export * from './gatsbyBrowser' diff --git a/yarn.lock b/yarn.lock index cfbcc08..0a34f36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1467,6 +1467,16 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== +"@reach/router@^1.3.4": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.3.4.tgz#d2574b19370a70c80480ed91f3da840136d10f8c" + integrity sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA== + dependencies: + create-react-context "0.3.0" + invariant "^2.2.3" + prop-types "^15.6.1" + react-lifecycles-compat "^3.0.4" + "@rollup/plugin-babel@^6.0.2": version "6.1.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-6.1.0.tgz#5766913722057f28a56365bb6c1ca61306c7e527" @@ -1780,6 +1790,20 @@ dependencies: undici-types "~7.16.0" +"@types/reach__router@^1.3.15": + version "1.3.15" + resolved "https://registry.yarnpkg.com/@types/reach__router/-/reach__router-1.3.15.tgz#be4e23ee57786a9a16db9af3cff4c085de9e0db0" + integrity sha512-5WEHKGglRjq/Ae3F8UQxg+GYUIhTUEiyBT9GKPoOLU/vPTn8iZrRbdzxqvarOaGludIejJykHLMdOCdhgWqaxA== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "19.2.9" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.9.tgz#84ec7669742bb3e7e2e8d6a5258d95ead7764200" + integrity sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA== + dependencies: + csstype "^3.2.2" + "@types/react@^19.2.7": version "19.2.7" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.7.tgz#84e62c0f23e8e4e5ac2cadcea1ffeacccae7f62f" @@ -2393,6 +2417,14 @@ coveralls@^3.1.1: minimist "^1.2.5" request "^2.88.2" +create-react-context@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c" + integrity sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw== + dependencies: + gud "^1.0.0" + warning "^4.0.3" + cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -3184,6 +3216,11 @@ gopd@^1.0.1, gopd@^1.2.0: resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -3331,6 +3368,13 @@ internal-slot@^1.1.0: hasown "^2.0.2" side-channel "^1.1.0" +invariant@^2.2.3: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" @@ -3772,7 +3816,7 @@ log-driver@^1.2.7: resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== -loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -4153,7 +4197,7 @@ pretty-format@^27.0.2: ansi-styles "^5.0.0" react-is "^17.0.1" -prop-types@^15.8.1: +prop-types@^15.6.1, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -4203,6 +4247,11 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + react-router-dom@^7.0.0: version "7.12.0" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.12.0.tgz#0f2a059c6b2c4ae04474fe4171c59fb48b9fb8cf" @@ -5083,6 +5132,13 @@ w3c-xmlserializer@^5.0.0: dependencies: xml-name-validator "^5.0.0" +warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + webidl-conversions@^8.0.0: version "8.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-8.0.1.tgz#0657e571fe6f06fcb15ca50ed1fdbcb495cd1686" From 2bb14e0ef7790e285f5ab2a57fed9cf7c0123e31 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 19:07:27 +0000 Subject: [PATCH 20/54] Add TanStack Router integration for pageview tracking - Add TanStackRouterFathomTrackView component using useRouterState hook - Add comprehensive tests for TanStack Router integration - Create TanStack Router documentation page - Update README and getting-started with TanStack Router information - Configure rollup and package.json for tanstack-router export path --- README.md | 23 ++ docs/content/_meta.ts | 1 + docs/content/getting-started.mdx | 2 + docs/content/tanstack-router/index.mdx | 266 ++++++++++++++++++ package.json | 15 + rollup.config.js | 26 ++ .../TanStackRouterFathomTrackView.test.tsx | 238 ++++++++++++++++ .../TanStackRouterFathomTrackView.tsx | 159 +++++++++++ src/tanstack-router/index.ts | 1 + yarn.lock | 78 +++++ 10 files changed, 809 insertions(+) create mode 100644 docs/content/tanstack-router/index.mdx create mode 100644 src/tanstack-router/TanStackRouterFathomTrackView.test.tsx create mode 100644 src/tanstack-router/TanStackRouterFathomTrackView.tsx create mode 100644 src/tanstack-router/index.ts diff --git a/README.md b/README.md index f146b78..a3210db 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ The official `fathom-client` works, but: - ⚑ Next.js App Router and Pages Router support - πŸ›€οΈ React Router v6+ and Remix support - 🏠 Gatsby support with @reach/router integration +- 🧭 TanStack Router support with type-safe routing - 🌳 Tree-shakeable, fully typed (TypeScript) ## Usage @@ -145,6 +146,26 @@ export const wrapRootElement = ({ element }) => ( πŸ“– [Full Gatsby guide](https://react-fathom.com/gatsby) +### TanStack Router + +```tsx +// src/routes/__root.tsx +import { createRootRoute, Outlet } from '@tanstack/react-router' +import { FathomProvider } from 'react-fathom' +import { TanStackRouterFathomTrackView } from 'react-fathom/tanstack-router' + +export const Route = createRootRoute({ + component: () => ( + + + + + ), +}) +``` + +πŸ“– [Full TanStack Router guide](https://react-fathom.com/tanstack-router) + ### Hooks ```tsx @@ -233,6 +254,7 @@ function App() { | `NextFathomTrackViewPages` | Next.js Pages Router (add inside `FathomProvider`) | | `ReactRouterFathomTrackView` | React Router v6+ / Remix (add inside `FathomProvider`) | | `GatsbyFathomTrackView` | Gatsby (add inside `FathomProvider`) | +| `TanStackRouterFathomTrackView` | TanStack Router (add inside `FathomProvider`) | | `NativeFathomProvider` | React Native | ### Hooks @@ -275,6 +297,7 @@ function App() { - βš›οΈ [React Guide](https://react-fathom.com/react) - πŸ›€οΈ [React Router Guide](https://react-fathom.com/react-router) - 🏠 [Gatsby Guide](https://react-fathom.com/gatsby) +- 🧭 [TanStack Router Guide](https://react-fathom.com/tanstack-router) - ⚑ [Next.js Guide](https://react-fathom.com/nextjs) - πŸ“± [React Native Guide](https://react-fathom.com/react-native) - πŸ“š [API Reference](https://react-fathom.com/api/providers) diff --git a/docs/content/_meta.ts b/docs/content/_meta.ts index aa7e341..87c53f9 100644 --- a/docs/content/_meta.ts +++ b/docs/content/_meta.ts @@ -4,6 +4,7 @@ export default { react: 'React', 'react-router': 'React Router', gatsby: 'Gatsby', + 'tanstack-router': 'TanStack Router', nextjs: 'Next.js', 'react-native': 'React Native', api: 'API Reference', diff --git a/docs/content/getting-started.mdx b/docs/content/getting-started.mdx index 2b15806..2119675 100644 --- a/docs/content/getting-started.mdx +++ b/docs/content/getting-started.mdx @@ -24,6 +24,7 @@ npm install react-fathom fathom-client | `react-router-dom` | >= 6.0.0 | React Router / Remix only | | `@reach/router` | >= 1.3.0 | Gatsby only | | `gatsby` | >= 4.0.0 | Gatsby only | +| `@tanstack/react-router` | >= 1.0.0 | TanStack Router only | | `react-native` | >= 0.60.0 | React Native only | | `react-native-webview` | >= 11.0.0 | React Native only | @@ -74,6 +75,7 @@ Pageviews are tracked automatically by default. - [React usage guide](/docs/react) β€” Hooks and declarative components - [React Router integration](/docs/react-router) β€” Automatic pageview tracking for React Router and Remix - [Gatsby integration](/docs/gatsby) β€” Automatic pageview tracking for Gatsby +- [TanStack Router integration](/docs/tanstack-router) β€” Type-safe routing with automatic tracking - [Next.js integration](/docs/nextjs) β€” App Router and Pages Router - [React Native setup](/docs/react-native) β€” Mobile apps with offline queuing - [API Reference](/docs/api/providers) β€” Full provider props and options diff --git a/docs/content/tanstack-router/index.mdx b/docs/content/tanstack-router/index.mdx new file mode 100644 index 0000000..4c503d6 --- /dev/null +++ b/docs/content/tanstack-router/index.mdx @@ -0,0 +1,266 @@ +--- +title: "TanStack Router" +description: "Integrate react-fathom with TanStack Router for automatic pageview tracking." +--- + +# TanStack Router Integration + +This guide covers integrating `react-fathom` with TanStack Router applications for automatic pageview tracking. + +## Installation + +Install the required packages: + +```bash npm2yarn +npm install react-fathom fathom-client @tanstack/react-router +``` + +## Setup + +Add `TanStackRouterFathomTrackView` inside your `FathomProvider`, typically in your root route component. + +### Root Route Setup + +```tsx +// src/routes/__root.tsx +import { createRootRoute, Outlet } from '@tanstack/react-router' +import { FathomProvider } from 'react-fathom' +import { TanStackRouterFathomTrackView } from 'react-fathom/tanstack-router' + +export const Route = createRootRoute({ + component: () => ( + + + + + ), +}) +``` + +### Alternative: App-Level Setup + +If you prefer to keep analytics configuration separate from routes: + +```tsx +// src/App.tsx +import { RouterProvider, createRouter } from '@tanstack/react-router' +import { FathomProvider } from 'react-fathom' +import { TanStackRouterFathomTrackView } from 'react-fathom/tanstack-router' +import { routeTree } from './routeTree.gen' + +const router = createRouter({ routeTree }) + +function InnerApp() { + return ( + <> + + {/* Your app content */} + + ) +} + +export function App() { + return ( + + + + ) +} +``` + +> Note: When using this approach, ensure `TanStackRouterFathomTrackView` is rendered within the router context (inside a route component). + +## TanStackRouterFathomTrackView Props + +| Prop | Type | Default | Description | +|------|------|---------|-------------| +| `disableAutoTrack` | `boolean` | `false` | Disable automatic pageview tracking | +| `includeSearchParams` | `boolean` | `true` | Include query parameters in tracked URLs | +| `includeHash` | `boolean` | `false` | Include URL hash in tracked URLs | +| `transformUrl` | `(url: string) => string \| null` | β€” | Transform URL before tracking; return `null` to skip | + +## Tracking Custom Events + +Use the `useFathom` hook in any component: + +```tsx +import { useFathom } from 'react-fathom' + +function CheckoutButton() { + const { trackEvent } = useFathom() + + const handleClick = () => { + trackEvent('checkout-started') + // Navigate to checkout... + } + + return +} +``` + +## URL Transformation + +### Sanitizing Dynamic Routes + +Remove dynamic segments from URLs for cleaner analytics: + +```tsx + { + // /users/123 β†’ /users/$userId + return url.replace(/\/users\/\d+/, '/users/$userId') + }} +/> +``` + +### Excluding Routes + +Skip tracking for specific routes: + +```tsx + { + // Don't track admin pages + if (url.includes('/admin')) { + return null + } + return url + }} +/> +``` + +### Stripping Sensitive Parameters + +Remove sensitive data from tracked URLs: + +```tsx + { + const urlObj = new URL(url) + urlObj.searchParams.delete('token') + urlObj.searchParams.delete('session') + return urlObj.toString() + }} +/> +``` + +## Environment Variables + +Store your site ID in environment variables: + +```bash +# .env +VITE_FATHOM_SITE_ID=YOUR_SITE_ID +``` + +```tsx + +``` + +## Local Development + +Enable tracking on localhost: + +```tsx + +``` + +## TypeScript Support + +All TanStack Router exports are fully typed. Import types as needed: + +```tsx +import type { TanStackRouterFathomTrackViewProps } from 'react-fathom/tanstack-router' +``` + +## How It Works + +TanStack Router provides location state through the `useRouterState` hook. The `TanStackRouterFathomTrackView` component: + +1. Uses `useRouterState` to watch for location changes +2. On mount, tracks the initial pageview +3. When `pathname`, `searchStr`, or `hash` change, tracks a new pageview +4. Constructs the full URL from `window.location.origin` and the route path + +## File-Based Routing Example + +If you're using TanStack Router's file-based routing: + +```tsx +// src/routes/__root.tsx +import { createRootRoute, Outlet } from '@tanstack/react-router' +import { FathomProvider } from 'react-fathom' +import { TanStackRouterFathomTrackView } from 'react-fathom/tanstack-router' + +export const Route = createRootRoute({ + component: RootComponent, +}) + +function RootComponent() { + return ( + + { + // Normalize user profile URLs + return url.replace(/\/users\/[^/]+$/, '/users/$userId') + }} + /> +
...
+
+ +
+
...
+
+ ) +} +``` + +```tsx +// src/routes/index.tsx +import { createFileRoute } from '@tanstack/react-router' +import { useFathom } from 'react-fathom' + +export const Route = createFileRoute('/')({ + component: HomePage, +}) + +function HomePage() { + const { trackEvent } = useFathom() + + return ( +
+

Welcome

+ +
+ ) +} +``` + +## Troubleshooting + +### Events not appearing in Fathom? + +1. Verify your site ID matches your [Fathom dashboard](https://app.usefathom.com) +2. Check for ad blockers (test in incognito mode) +3. Add `localhost` to `includedDomains` for local testing +4. Ensure `TanStackRouterFathomTrackView` is within both `FathomProvider` and router context + +### Route changes not tracking? + +Ensure `TanStackRouterFathomTrackView` is rendered inside `FathomProvider` and within the TanStack Router context. The component must be inside a route component to access the router state. + +### Search params not being tracked? + +TanStack Router uses `searchStr` for the serialized search string. This is handled automatically by the component. If you have custom search param serialization, make sure it's producing the expected format. diff --git a/package.json b/package.json index 66fa462..a7c8629 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "react-router", "remix", "gatsby", + "tanstack-router", "nextjs", "next.js", "typescript", @@ -100,6 +101,9 @@ ], "gatsby": [ "./types/gatsby/index.d.ts" + ], + "tanstack-router": [ + "./types/tanstack-router/index.d.ts" ] } }, @@ -133,6 +137,12 @@ "import": "./dist/es/gatsby/index.js", "require": "./dist/cjs/gatsby/index.cjs", "default": "./dist/es/gatsby/index.js" + }, + "./tanstack-router": { + "types": "./types/tanstack-router/index.d.ts", + "import": "./dist/es/tanstack-router/index.js", + "require": "./dist/cjs/tanstack-router/index.cjs", + "default": "./dist/es/tanstack-router/index.js" } }, "scripts": { @@ -151,6 +161,7 @@ }, "peerDependencies": { "@reach/router": ">=1.3.0", + "@tanstack/react-router": ">=1.0.0", "fathom-client": ">=3.0.0", "gatsby": ">=4.0.0", "next": ">=10.0.0", @@ -164,6 +175,9 @@ "@reach/router": { "optional": true }, + "@tanstack/react-router": { + "optional": true + }, "gatsby": { "optional": true }, @@ -185,6 +199,7 @@ }, "devDependencies": { "@reach/router": "^1.3.4", + "@tanstack/react-router": "^1.120.5", "@babel/core": "^7.12.10", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-object-rest-spread": "^7.20.7", diff --git a/rollup.config.js b/rollup.config.js index 85d6282..9f77db6 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -140,12 +140,14 @@ const nextExternal = [ const nativeExternal = ['react', 'react-native'] const reactRouterExternal = ['fathom-client', 'react', 'react-router-dom'] const gatsbyExternal = ['fathom-client', 'react', '@reach/router'] +const tanstackRouterExternal = ['fathom-client', 'react', '@tanstack/react-router'] const input = 'src/index.ts' const nextInput = 'src/next/index.ts' const nativeInput = 'src/native/index.ts' const reactRouterInput = 'src/react-router/index.ts' const gatsbyInput = 'src/gatsby/index.ts' +const tanstackRouterInput = 'src/tanstack-router/index.ts' export default [ // UMD - Minified @@ -290,4 +292,28 @@ export default [ external: makeExternal(gatsbyExternal), plugins: defaultPlugins, }, + // ES - tanstack-router + { + input: tanstackRouterInput, + output: { + ...defaultOutputOptions, + dir: 'dist/es/tanstack-router', + format: 'esm', + entryFileNames: '[name].js', + }, + external: makeExternal(tanstackRouterExternal), + plugins: defaultPlugins, + }, + // CJS - tanstack-router + { + input: tanstackRouterInput, + output: { + ...defaultOutputOptions, + dir: 'dist/cjs/tanstack-router', + format: 'cjs', + entryFileNames: '[name].cjs', + }, + external: makeExternal(tanstackRouterExternal), + plugins: defaultPlugins, + }, ] diff --git a/src/tanstack-router/TanStackRouterFathomTrackView.test.tsx b/src/tanstack-router/TanStackRouterFathomTrackView.test.tsx new file mode 100644 index 0000000..b7cbb66 --- /dev/null +++ b/src/tanstack-router/TanStackRouterFathomTrackView.test.tsx @@ -0,0 +1,238 @@ +import React from 'react' + +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { act, render, waitFor } from '@testing-library/react' +import { + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, + Outlet, + RouterProvider, +} from '@tanstack/react-router' + +import { FathomProvider } from '../FathomProvider' +import { TanStackRouterFathomTrackView } from './TanStackRouterFathomTrackView' + +// Mock fathom-client +vi.mock('fathom-client', () => { + const mockFathomDefault = { + trackEvent: vi.fn(), + trackPageview: vi.fn(), + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + return { + default: mockFathomDefault, + } +}) + +// Create a mock client factory +const createMockClient = () => ({ + trackEvent: vi.fn(), + trackPageview: vi.fn(), + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), +}) + +// Create a test router setup helper that includes tracking in root component +function createTestRouter( + initialPath: string = '/', + client: ReturnType, + trackViewProps: React.ComponentProps = {}, +) { + const rootRoute = createRootRoute({ + component: () => ( + + + + + ), + }) + + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () =>
Home
, + }) + + const testRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/test-page', + component: () =>
Test Page
, + }) + + const newRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/new-page', + component: () =>
New Page
, + }) + + const routeTree = rootRoute.addChildren([indexRoute, testRoute, newRoute]) + + const router = createRouter({ + routeTree, + history: createMemoryHistory({ + initialEntries: [initialPath], + }), + }) + + return router +} + +describe('TanStackRouterFathomTrackView', () => { + beforeEach(() => { + vi.clearAllMocks() + delete (window as { location?: unknown }).location + window.location = { + href: 'https://example.com/test-page', + origin: 'https://example.com', + } as Location + }) + + it('should track initial pageview on mount', async () => { + const client = createMockClient() + const router = createTestRouter('/test-page', client) + + render() + + await waitFor(() => { + expect(client.trackPageview).toHaveBeenCalled() + }) + + expect(client.trackPageview).toHaveBeenCalledWith({ + url: 'https://example.com/test-page', + }) + }) + + it('should track pageviews on route changes', async () => { + const client = createMockClient() + const router = createTestRouter('/', client) + + render() + + // Wait for initial pageview + await waitFor(() => { + expect(client.trackPageview).toHaveBeenCalledTimes(1) + }) + + // Navigate to new page using router + await act(async () => { + await router.navigate({ to: '/new-page' }) + }) + + await waitFor(() => { + expect(client.trackPageview).toHaveBeenCalledTimes(2) + }) + + expect(client.trackPageview).toHaveBeenLastCalledWith({ + url: 'https://example.com/new-page', + }) + }) + + it('should not track when disableAutoTrack is true', async () => { + const client = createMockClient() + const router = createTestRouter('/test-page', client, { disableAutoTrack: true }) + + render() + + // Give time for effects to run + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 100)) + }) + + expect(client.trackPageview).not.toHaveBeenCalled() + }) + + it('should include search params by default', async () => { + const client = createMockClient() + const router = createTestRouter('/test-page?foo=bar', client) + + render() + + await waitFor(() => { + expect(client.trackPageview).toHaveBeenCalled() + }) + + expect(client.trackPageview).toHaveBeenCalledWith({ + url: 'https://example.com/test-page?foo=bar', + }) + }) + + it('should exclude search params when includeSearchParams is false', async () => { + const client = createMockClient() + const router = createTestRouter('/test-page?foo=bar', client, { includeSearchParams: false }) + + render() + + await waitFor(() => { + expect(client.trackPageview).toHaveBeenCalled() + }) + + expect(client.trackPageview).toHaveBeenCalledWith({ + url: 'https://example.com/test-page', + }) + }) + + it('should include hash when includeHash is true', async () => { + const client = createMockClient() + const router = createTestRouter('/test-page#section', client, { includeHash: true }) + + render() + + await waitFor(() => { + expect(client.trackPageview).toHaveBeenCalled() + }) + + expect(client.trackPageview).toHaveBeenCalledWith({ + url: 'https://example.com/test-page#section', + }) + }) + + it('should transform URL when transformUrl is provided', async () => { + const client = createMockClient() + const transformUrl = (url: string) => url.replace('/test-page', '/transformed') + const router = createTestRouter('/test-page', client, { transformUrl }) + + render() + + await waitFor(() => { + expect(client.trackPageview).toHaveBeenCalled() + }) + + expect(client.trackPageview).toHaveBeenCalledWith({ + url: 'https://example.com/transformed', + }) + }) + + it('should skip tracking when transformUrl returns null', async () => { + const client = createMockClient() + const transformUrl = () => null + const router = createTestRouter('/test-page', client, { transformUrl }) + + render() + + // Give time for effects to run + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 100)) + }) + + expect(client.trackPageview).not.toHaveBeenCalled() + }) + + it('should have displayName', () => { + expect(TanStackRouterFathomTrackView.displayName).toBe( + 'TanStackRouterFathomTrackView', + ) + }) +}) diff --git a/src/tanstack-router/TanStackRouterFathomTrackView.tsx b/src/tanstack-router/TanStackRouterFathomTrackView.tsx new file mode 100644 index 0000000..f601cab --- /dev/null +++ b/src/tanstack-router/TanStackRouterFathomTrackView.tsx @@ -0,0 +1,159 @@ +import React, { useCallback, useEffect, useRef } from 'react' + +import { useRouterState } from '@tanstack/react-router' + +import { useFathom } from '../hooks/useFathom' + +export interface TanStackRouterFathomTrackViewProps { + /** + * Disable automatic pageview tracking on route changes + * @default false + */ + disableAutoTrack?: boolean + + /** + * Include search/query parameters in the tracked URL + * @default true + */ + includeSearchParams?: boolean + + /** + * Include hash fragment in the tracked URL + * @default false + */ + includeHash?: boolean + + /** + * Custom function to transform the URL before tracking. + * Useful for removing sensitive data or normalizing URLs. + * @param url The URL that would be tracked + * @returns The transformed URL to track, or null/undefined to skip tracking + */ + transformUrl?: (url: string) => string | null | undefined +} + +/** + * Component that tracks pageviews for TanStack Router applications. + * Must be used within a FathomProvider and a TanStack Router context. + * + * @example + * ```tsx + * import { RouterProvider, createRouter } from '@tanstack/react-router' + * import { FathomProvider } from 'react-fathom' + * import { TanStackRouterFathomTrackView } from 'react-fathom/tanstack-router' + * + * // In your root route component + * function RootComponent() { + * return ( + * + * + * + * + * ) + * } + * ``` + * + * @example + * ```tsx + * // With URL transformation (strip sensitive params) + * { + * const urlObj = new URL(url) + * urlObj.searchParams.delete('token') + * return urlObj.toString() + * }} + * /> + * ``` + * + * @example + * ```tsx + * // Normalize dynamic route segments + * { + * // /users/123 β†’ /users/[id] + * return url.replace(/\/users\/\d+/, '/users/[id]') + * }} + * /> + * ``` + */ +export const TanStackRouterFathomTrackView: React.FC< + TanStackRouterFathomTrackViewProps +> = ({ + disableAutoTrack = false, + includeSearchParams = true, + includeHash = false, + transformUrl, +}) => { + const hasTrackedInitialPageview = useRef(false) + const { trackPageview, client } = useFathom() + + // Get location from TanStack Router state + const location = useRouterState({ select: (s) => s.location }) + + // Build URL from location parts + const buildUrl = useCallback(() => { + if (typeof window === 'undefined') return null + + let url = window.location.origin + location.pathname + + // TanStack Router provides search as an object, use the serialized searchStr + if (includeSearchParams && location.searchStr) { + url += location.searchStr + } + + if (includeHash && location.hash) { + // TanStack Router's hash doesn't include the # prefix + url += location.hash.startsWith('#') ? location.hash : `#${location.hash}` + } + + if (transformUrl) { + const transformed = transformUrl(url) + if (transformed === null || transformed === undefined) { + return null + } + url = transformed + } + + return url + }, [location.pathname, location.searchStr, location.hash, includeSearchParams, includeHash, transformUrl]) + + // Track pageviews on route changes + useEffect(() => { + if (!trackPageview || !client || disableAutoTrack) { + return + } + + // Skip initial render - handled separately + if (!hasTrackedInitialPageview.current) { + return + } + + const url = buildUrl() + if (url) { + trackPageview({ url }) + } + }, [location.pathname, location.searchStr, location.hash, trackPageview, client, disableAutoTrack, buildUrl]) + + // Track initial pageview + useEffect(() => { + if ( + !trackPageview || + !client || + disableAutoTrack || + hasTrackedInitialPageview.current + ) { + return + } + + hasTrackedInitialPageview.current = true + const url = buildUrl() + if (url) { + trackPageview({ url }) + } + }, [trackPageview, client, disableAutoTrack, buildUrl]) + + // This component doesn't render anything + return null +} + +TanStackRouterFathomTrackView.displayName = 'TanStackRouterFathomTrackView' diff --git a/src/tanstack-router/index.ts b/src/tanstack-router/index.ts new file mode 100644 index 0000000..0049b82 --- /dev/null +++ b/src/tanstack-router/index.ts @@ -0,0 +1 @@ +export * from './TanStackRouterFathomTrackView' diff --git a/yarn.lock b/yarn.lock index 0a34f36..bb1095c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1717,6 +1717,49 @@ dependencies: tslib "^2.8.0" +"@tanstack/history@1.154.7": + version "1.154.7" + resolved "https://registry.yarnpkg.com/@tanstack/history/-/history-1.154.7.tgz#2e3c45ba5619078af0a288af0abf7cc096685dc4" + integrity sha512-YBgwS9qG4rs1ZY/ZrhQtjOH8BG9Qa2wf2AsxT/SnZ4HZJ1DcCEqkoiHH0yH6CYvdDit31X5HokOqQrRSsZEwGA== + +"@tanstack/react-router@^1.120.5": + version "1.154.8" + resolved "https://registry.yarnpkg.com/@tanstack/react-router/-/react-router-1.154.8.tgz#48c28ae298d35fae412c24e2d7a9ab1a4f6fe878" + integrity sha512-FD7VjAaO1kjg2v8jL2jXMgSLga2fD6kBkBN+50voeJ5jHLasHcZpRcPZl7hkdy0gXiR/d6JepgnoeHQYiC2vRA== + dependencies: + "@tanstack/history" "1.154.7" + "@tanstack/react-store" "^0.8.0" + "@tanstack/router-core" "1.154.8" + isbot "^5.1.22" + tiny-invariant "^1.3.3" + tiny-warning "^1.0.3" + +"@tanstack/react-store@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-store/-/react-store-0.8.0.tgz#af2e73f1a466e08897349829bae1073f6172e7da" + integrity sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow== + dependencies: + "@tanstack/store" "0.8.0" + use-sync-external-store "^1.6.0" + +"@tanstack/router-core@1.154.8": + version "1.154.8" + resolved "https://registry.yarnpkg.com/@tanstack/router-core/-/router-core-1.154.8.tgz#46f781ec01c380067d800ef40a90e57550b800a7" + integrity sha512-bJvc4scMXktX2ZrO6xE8R3SC6SM9kCLu34KMlS5pFbK6C/Ry4yGgW4UWDY+Nwznbhc59Ehcw+HeBoE9WDWOzDA== + dependencies: + "@tanstack/history" "1.154.7" + "@tanstack/store" "^0.8.0" + cookie-es "^2.0.0" + seroval "^1.4.2" + seroval-plugins "^1.4.2" + tiny-invariant "^1.3.3" + tiny-warning "^1.0.3" + +"@tanstack/store@0.8.0", "@tanstack/store@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@tanstack/store/-/store-0.8.0.tgz#f1c533b9cff000fc792ed77edda178000abc9442" + integrity sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ== + "@testing-library/dom@^10.4.1": version "10.4.1" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-10.4.1.tgz#d444f8a889e9a46e9a3b4f3b88e0fcb3efb6cf95" @@ -2389,6 +2432,11 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cookie-es@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cookie-es/-/cookie-es-2.0.0.tgz#ca6163d7ef8686ea6bbdd551f1de575569c1ed69" + integrity sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg== + cookie@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c" @@ -3580,6 +3628,11 @@ isarray@^2.0.5: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== +isbot@^5.1.22: + version "5.1.33" + resolved "https://registry.yarnpkg.com/isbot/-/isbot-5.1.33.tgz#99afd99be96390452dcf794d5ff97400339f40d7" + integrity sha512-P4Hgb5NqswjkI0J1CM6XKXon/sxKY1SuowE7Qx2hrBhIwICFyXy54mfgB5eMHXsbe/eStzzpbIGNOvGmz+dlKg== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -4544,6 +4597,16 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" +seroval-plugins@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/seroval-plugins/-/seroval-plugins-1.4.2.tgz#e903e9a9f4f77f82bf80714213fa4bcae3b395cf" + integrity sha512-X7p4MEDTi+60o2sXZ4bnDBhgsUYDSkQEvzYZuJyFqWg9jcoPsHts5nrg5O956py2wyt28lUrBxk0M0/wU8URpA== + +seroval@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/seroval/-/seroval-1.4.2.tgz#0718ce33f0363407eee7c78272c24bc48ec940c8" + integrity sha512-N3HEHRCZYn3cQbsC4B5ldj9j+tHdf4JZoYPlcI4rRYu0Xy4qN8MQf1Z08EibzB0WpgRG5BGK08FTrmM66eSzKQ== + set-cookie-parser@^2.6.0: version "2.7.2" resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz#ccd08673a9ae5d2e44ea2a2de25089e67c7edf68" @@ -4857,6 +4920,16 @@ terser@^5.0.0: commander "^2.20.0" source-map-support "~0.5.20" +tiny-invariant@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + +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== + tinybench@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b" @@ -5071,6 +5144,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +use-sync-external-store@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" + integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== + uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" From 10ad99cf184722ffbd9ffb37ad5c6f745f2b2d69 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 19:19:25 +0000 Subject: [PATCH 21/54] Add deep merge for nested FathomProvider default options Previously, nested providers would completely replace parent default options. Now child providers deep merge their options with parent options, allowing children to override specific values while inheriting others. Example: - Parent: { _site_id: 'abc', _value: 100 } - Child: { _value: 200 } - Result: { _site_id: 'abc', _value: 200 } --- src/FathomProvider.test.tsx | 104 ++++++++++++++++++++++++++++++++++++ src/FathomProvider.tsx | 15 ++++-- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/src/FathomProvider.test.tsx b/src/FathomProvider.test.tsx index a3937df..9c498b9 100644 --- a/src/FathomProvider.test.tsx +++ b/src/FathomProvider.test.tsx @@ -527,6 +527,110 @@ describe('FathomProvider', () => { }) }) + it('should deep merge nested providers - child overrides specific defaultEventOptions while inheriting others', () => { + const mockClient = { + trackEvent: vi.fn(), + trackPageview: vi.fn(), + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + + {children} + + + ) + + const { result } = renderHook(() => useFathom(), { wrapper }) + + result.current.trackEvent?.('test-event') + + // Child overrides _value but inherits _site_id from parent + expect(mockClient.trackEvent).toHaveBeenCalledWith('test-event', { + _site_id: 'parent-id', + _value: 200, + }) + }) + + it('should deep merge nested providers - child overrides specific defaultPageviewOptions while inheriting others', () => { + const mockClient = { + trackEvent: vi.fn(), + trackPageview: vi.fn(), + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + + {children} + + + ) + + const { result } = renderHook(() => useFathom(), { wrapper }) + + result.current.trackPageview?.() + + // Child overrides url but inherits referrer from parent + expect(mockClient.trackPageview).toHaveBeenCalledWith({ + url: '/child', + referrer: 'https://parent.com', + }) + }) + + it('should deep merge three levels of nested providers', () => { + const mockClient = { + trackEvent: vi.fn(), + trackPageview: vi.fn(), + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), + } + + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + + + {children} + + + + ) + + const { result } = renderHook(() => useFathom(), { wrapper }) + + result.current.trackEvent?.('test-event') + + // Deepest child overrides _value, inherits _site_id from root + expect(mockClient.trackEvent).toHaveBeenCalledWith('test-event', { + _site_id: 'root-id', + _value: 200, + }) + }) + it('should have displayName', () => { expect(FathomProvider.displayName).toBe('FathomProvider') }) diff --git a/src/FathomProvider.tsx b/src/FathomProvider.tsx index 4d5e1d8..9226c97 100644 --- a/src/FathomProvider.tsx +++ b/src/FathomProvider.tsx @@ -26,16 +26,21 @@ const FathomProvider: React.FC = ({ [providedClient, parentContext.client], ) - // Merge defaultPageviewOptions: provided > parent > undefined + // Merge defaultPageviewOptions: parent + provided (provided overrides parent) const defaultPageviewOptions = useMemo( - () => - providedDefaultPageviewOptions ?? parentContext.defaultPageviewOptions, + () => ({ + ...parentContext.defaultPageviewOptions, + ...providedDefaultPageviewOptions, + }), [providedDefaultPageviewOptions, parentContext.defaultPageviewOptions], ) - // Merge defaultEventOptions: provided > parent > undefined + // Merge defaultEventOptions: parent + provided (provided overrides parent) const defaultEventOptions = useMemo( - () => providedDefaultEventOptions ?? parentContext.defaultEventOptions, + () => ({ + ...parentContext.defaultEventOptions, + ...providedDefaultEventOptions, + }), [providedDefaultEventOptions, parentContext.defaultEventOptions], ) From 7e6b32d938faa9fa07dbaa7021bedc19373735e9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 19:42:52 +0000 Subject: [PATCH 22/54] Add debug mode to FathomProvider for development and demos - Add `debug` prop to FathomProvider (boolean or options object) - When enabled, emits debug events for all tracking calls - Console logging with colored output and emojis - `onTrack` callback for custom UI integration (toast notifications) - `subscribeToDebug` context method for reactive subscriptions - New `useDebugSubscription` hook for consuming debug events - Export DebugEvent, DebugOptions, and DebugEventCallback types - Works without siteId for demo/development scenarios - Does not block actual Fathom tracking Example usage: ```tsx // Simple console logging ``` --- src/FathomContext.tsx | 2 + src/FathomProvider.tsx | 133 +++++++++++- src/hooks/index.ts | 1 + src/hooks/useDebugSubscription.test.tsx | 257 ++++++++++++++++++++++++ src/hooks/useDebugSubscription.ts | 89 ++++++++ src/types.ts | 78 +++++++ 6 files changed, 553 insertions(+), 7 deletions(-) create mode 100644 src/hooks/useDebugSubscription.test.tsx create mode 100644 src/hooks/useDebugSubscription.ts diff --git a/src/FathomContext.tsx b/src/FathomContext.tsx index d2f2096..359564c 100644 --- a/src/FathomContext.tsx +++ b/src/FathomContext.tsx @@ -28,6 +28,8 @@ const defaultContextValue: FathomContextInterface = { trackPageview: () => warnMissingProvider('trackPageview'), trackEvent: () => warnMissingProvider('trackEvent'), trackGoal: () => warnMissingProvider('trackGoal'), + subscribeToDebug: undefined, + debugEnabled: false, } export const FathomContext = diff --git a/src/FathomProvider.tsx b/src/FathomProvider.tsx index 9226c97..043ca34 100644 --- a/src/FathomProvider.tsx +++ b/src/FathomProvider.tsx @@ -1,12 +1,16 @@ 'use client' -import React, { useCallback, useContext, useEffect, useMemo } from 'react' +import React, { useCallback, useContext, useEffect, useMemo, useRef } from 'react' import * as Fathom from 'fathom-client' import type { EventOptions, LoadOptions, PageViewOptions } from 'fathom-client' import { FathomContext } from './FathomContext' -import type { FathomProviderProps } from './types' +import type { DebugEvent, DebugEventCallback, DebugOptions, FathomProviderProps } from './types' + +// Generate unique IDs for debug events +let debugEventCounter = 0 +const generateDebugEventId = () => `debug-${Date.now()}-${++debugEventCounter}` const FathomProvider: React.FC = ({ children, @@ -16,10 +20,89 @@ const FathomProvider: React.FC = ({ siteId, defaultPageviewOptions: providedDefaultPageviewOptions, defaultEventOptions: providedDefaultEventOptions, + debug: debugProp, }) => { // Read parent context if it exists const parentContext = useContext(FathomContext) + // Parse debug options + const debugOptions: DebugOptions = useMemo(() => { + if (debugProp === true) { + return { enabled: true, console: true } + } + if (debugProp && typeof debugProp === 'object') { + return { + enabled: debugProp.enabled ?? false, + console: debugProp.console ?? debugProp.enabled ?? false, + onTrack: debugProp.onTrack, + } + } + // Inherit from parent if not specified + return { + enabled: parentContext.debugEnabled ?? false, + console: false, + } + }, [debugProp, parentContext.debugEnabled]) + + const debugEnabled = debugOptions.enabled + + // Store debug subscribers + const debugSubscribersRef = useRef>(new Set()) + + // Inherit parent's subscribers if we're a nested provider without our own debug config + useEffect(() => { + if (!debugProp && parentContext.subscribeToDebug) { + // We don't have our own debug config, so we don't need our own subscribers + // Events will flow through parent + } + }, [debugProp, parentContext.subscribeToDebug]) + + // Subscribe to debug events + const subscribeToDebug = useCallback((callback: DebugEventCallback) => { + debugSubscribersRef.current.add(callback) + return () => { + debugSubscribersRef.current.delete(callback) + } + }, []) + + // Emit debug event to all subscribers and optionally log to console + const emitDebugEvent = useCallback( + (event: DebugEvent) => { + if (!debugEnabled) return + + // Log to console if enabled + if (debugOptions.console) { + const emoji = event.type === 'pageview' ? 'πŸ“„' : event.type === 'event' ? '🎯' : 'πŸ†' + const label = event.type === 'pageview' + ? `Pageview: ${event.url || '(current page)'}` + : event.type === 'event' + ? `Event: ${event.eventName}` + : `Goal: ${event.goalCode} ($${((event.goalCents || 0) / 100).toFixed(2)})` + + console.log( + `%c[react-fathom] ${emoji} ${label}`, + 'color: #8b5cf6; font-weight: bold;', + event.options || '' + ) + } + + // Call the onTrack callback if provided + if (debugOptions.onTrack) { + debugOptions.onTrack(event) + } + + // Notify all subscribers + debugSubscribersRef.current.forEach((callback) => { + try { + callback(event) + } catch (err) { + console.error('[react-fathom] Debug subscriber error:', err) + } + }) + }, + [debugEnabled, debugOptions] + ) + // Use provided client or fall back to parent client or default Fathom const client = useMemo( () => providedClient ?? parentContext.client ?? Fathom, @@ -72,29 +155,63 @@ const FathomProvider: React.FC = ({ const trackEvent = useCallback( (eventName: string, options?: EventOptions) => { - client.trackEvent(eventName, { + const mergedOptions = { ...defaultEventOptions, ...options, + } + + // Emit debug event + emitDebugEvent({ + id: generateDebugEventId(), + timestamp: Date.now(), + type: 'event', + eventName, + options: mergedOptions, }) + + // Track to Fathom + client.trackEvent(eventName, mergedOptions) }, - [client, defaultEventOptions], + [client, defaultEventOptions, emitDebugEvent], ) const trackPageview = useCallback( (options?: PageViewOptions) => { - client.trackPageview({ + const mergedOptions = { ...defaultPageviewOptions, ...options, + } + + // Emit debug event + emitDebugEvent({ + id: generateDebugEventId(), + timestamp: Date.now(), + type: 'pageview', + url: mergedOptions.url, + options: mergedOptions, }) + + // Track to Fathom + client.trackPageview(mergedOptions) }, - [client, defaultPageviewOptions], + [client, defaultPageviewOptions, emitDebugEvent], ) const trackGoal = useCallback( (code: string, cents: number) => { + // Emit debug event + emitDebugEvent({ + id: generateDebugEventId(), + timestamp: Date.now(), + type: 'goal', + goalCode: code, + goalCents: cents, + }) + + // Track to Fathom client.trackGoal(code, cents) }, - [client], + [client, emitDebugEvent], ) useEffect(() => { @@ -124,6 +241,8 @@ const FathomProvider: React.FC = ({ client, defaultPageviewOptions, defaultEventOptions, + subscribeToDebug: debugEnabled ? subscribeToDebug : undefined, + debugEnabled, }} > {children} diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 5d1b702..93ce451 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -2,3 +2,4 @@ export * from './useFathom' export * from './useTrackOnMount' export * from './useTrackOnClick' export * from './useTrackOnVisible' +export * from './useDebugSubscription' diff --git a/src/hooks/useDebugSubscription.test.tsx b/src/hooks/useDebugSubscription.test.tsx new file mode 100644 index 0000000..c676705 --- /dev/null +++ b/src/hooks/useDebugSubscription.test.tsx @@ -0,0 +1,257 @@ +import React from 'react' + +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { act, renderHook, waitFor } from '@testing-library/react' + +import { FathomProvider } from '../FathomProvider' +import { useFathom } from './useFathom' +import { useDebugSubscription } from './useDebugSubscription' + +// Mock fathom-client +vi.mock('fathom-client', () => ({ + trackEvent: vi.fn(), + trackPageview: vi.fn(), + trackGoal: vi.fn(), + load: vi.fn(), + setSite: vi.fn(), + blockTrackingForMe: vi.fn(), + enableTrackingForMe: vi.fn(), + isTrackingEnabled: vi.fn(() => true), +})) + +describe('useDebugSubscription', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + it('should return debugEnabled as false when debug is not enabled', () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ) + + const { result } = renderHook(() => useDebugSubscription(), { wrapper }) + + expect(result.current.debugEnabled).toBe(false) + expect(result.current.events).toEqual([]) + }) + + it('should return debugEnabled as true when debug is enabled', () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ) + + const { result } = renderHook(() => useDebugSubscription(), { wrapper }) + + expect(result.current.debugEnabled).toBe(true) + }) + + it('should receive events when trackEvent is called', async () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook( + () => { + const fathom = useFathom() + const debug = useDebugSubscription() + return { fathom, debug } + }, + { wrapper } + ) + + act(() => { + result.current.fathom.trackEvent?.('test-event', { _value: 100 }) + }) + + await waitFor(() => { + expect(result.current.debug.events.length).toBe(1) + }) + + expect(result.current.debug.events[0].type).toBe('event') + expect(result.current.debug.events[0].eventName).toBe('test-event') + }) + + it('should receive events when trackPageview is called', async () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook( + () => { + const fathom = useFathom() + const debug = useDebugSubscription() + return { fathom, debug } + }, + { wrapper } + ) + + act(() => { + result.current.fathom.trackPageview?.({ url: '/test-page' }) + }) + + await waitFor(() => { + expect(result.current.debug.events.length).toBe(1) + }) + + expect(result.current.debug.events[0].type).toBe('pageview') + expect(result.current.debug.events[0].url).toBe('/test-page') + }) + + it('should receive events when trackGoal is called', async () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook( + () => { + const fathom = useFathom() + const debug = useDebugSubscription() + return { fathom, debug } + }, + { wrapper } + ) + + act(() => { + result.current.fathom.trackGoal?.('GOAL123', 2999) + }) + + await waitFor(() => { + expect(result.current.debug.events.length).toBe(1) + }) + + expect(result.current.debug.events[0].type).toBe('goal') + expect(result.current.debug.events[0].goalCode).toBe('GOAL123') + expect(result.current.debug.events[0].goalCents).toBe(2999) + }) + + it('should limit events to maxEvents', async () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook( + () => { + const fathom = useFathom() + const debug = useDebugSubscription({ maxEvents: 3 }) + return { fathom, debug } + }, + { wrapper } + ) + + act(() => { + result.current.fathom.trackEvent?.('event-1') + result.current.fathom.trackEvent?.('event-2') + result.current.fathom.trackEvent?.('event-3') + result.current.fathom.trackEvent?.('event-4') + result.current.fathom.trackEvent?.('event-5') + }) + + await waitFor(() => { + expect(result.current.debug.events.length).toBe(3) + }) + + // Most recent events should be first + expect(result.current.debug.events[0].eventName).toBe('event-5') + expect(result.current.debug.events[1].eventName).toBe('event-4') + expect(result.current.debug.events[2].eventName).toBe('event-3') + }) + + it('should call onEvent callback when events are received', async () => { + const onEventSpy = vi.fn() + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook( + () => { + const fathom = useFathom() + const debug = useDebugSubscription({ onEvent: onEventSpy }) + return { fathom, debug } + }, + { wrapper } + ) + + act(() => { + result.current.fathom.trackEvent?.('test-event') + }) + + await waitFor(() => { + expect(onEventSpy).toHaveBeenCalled() + }) + + expect(onEventSpy).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'event', + eventName: 'test-event', + }) + ) + }) + + it('should clear events when clearEvents is called', async () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ) + + const { result } = renderHook( + () => { + const fathom = useFathom() + const debug = useDebugSubscription() + return { fathom, debug } + }, + { wrapper } + ) + + act(() => { + result.current.fathom.trackEvent?.('event-1') + result.current.fathom.trackEvent?.('event-2') + }) + + await waitFor(() => { + expect(result.current.debug.events.length).toBe(2) + }) + + act(() => { + result.current.debug.clearEvents() + }) + + expect(result.current.debug.events.length).toBe(0) + }) + + it('should work with debug={true} shorthand', async () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ) + + const { result } = renderHook( + () => { + const fathom = useFathom() + const debug = useDebugSubscription() + return { fathom, debug } + }, + { wrapper } + ) + + expect(result.current.debug.debugEnabled).toBe(true) + + act(() => { + result.current.fathom.trackEvent?.('test-event') + }) + + await waitFor(() => { + expect(result.current.debug.events.length).toBe(1) + }) + }) +}) diff --git a/src/hooks/useDebugSubscription.ts b/src/hooks/useDebugSubscription.ts new file mode 100644 index 0000000..85e61ea --- /dev/null +++ b/src/hooks/useDebugSubscription.ts @@ -0,0 +1,89 @@ +import { useContext, useEffect, useState } from 'react' + +import { FathomContext } from '../FathomContext' +import type { DebugEvent, DebugEventCallback } from '../types' + +export interface UseDebugSubscriptionOptions { + /** + * Maximum number of events to keep in history. + * @default 50 + */ + maxEvents?: number + /** + * Callback fired when a new event is received. + */ + onEvent?: DebugEventCallback +} + +export interface UseDebugSubscriptionResult { + /** + * Array of debug events, most recent first. + */ + events: DebugEvent[] + /** + * Whether debug mode is enabled in the provider. + */ + debugEnabled: boolean + /** + * Clear all events from history. + */ + clearEvents: () => void +} + +/** + * Hook to subscribe to debug events from FathomProvider. + * Returns an array of events that can be displayed in a UI. + * + * @example + * ```tsx + * function DebugPanel() { + * const { events, debugEnabled, clearEvents } = useDebugSubscription({ + * maxEvents: 20, + * onEvent: (event) => console.log('New event:', event) + * }) + * + * if (!debugEnabled) return null + * + * return ( + *
+ * + * {events.map(event => ( + *
{event.type}: {event.eventName || event.url}
+ * ))} + *
+ * ) + * } + * ``` + */ +export function useDebugSubscription( + options: UseDebugSubscriptionOptions = {} +): UseDebugSubscriptionResult { + const { maxEvents = 50, onEvent } = options + const { subscribeToDebug, debugEnabled = false } = useContext(FathomContext) + const [events, setEvents] = useState([]) + + useEffect(() => { + if (!subscribeToDebug) return + + const unsubscribe = subscribeToDebug((event) => { + setEvents((prev) => { + const updated = [event, ...prev] + return updated.slice(0, maxEvents) + }) + + if (onEvent) { + onEvent(event) + } + }) + + return unsubscribe + }, [subscribeToDebug, maxEvents, onEvent]) + + const clearEvents = () => setEvents([]) + + return { + events, + debugEnabled, + clearEvents, + } +} diff --git a/src/types.ts b/src/types.ts index 1dc0ce0..7834c1b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,6 +5,54 @@ import type { EventOptions, LoadOptions, PageViewOptions } from 'fathom-client' // Re-export fathom-client types for convenience export type { EventOptions, LoadOptions, PageViewOptions } +/** + * Represents a debug event emitted by FathomProvider when debug mode is enabled. + */ +export interface DebugEvent { + /** Unique identifier for this event */ + id: string + /** Timestamp when the event occurred */ + timestamp: number + /** Type of tracking call */ + type: 'pageview' | 'event' | 'goal' + /** Event name (for 'event' type) */ + eventName?: string + /** Goal code (for 'goal' type) */ + goalCode?: string + /** Goal value in cents (for 'goal' type) */ + goalCents?: number + /** URL being tracked (for 'pageview' type) */ + url?: string + /** Additional options passed to the tracking call */ + options?: PageViewOptions | EventOptions +} + +/** + * Callback function for debug events. + */ +export type DebugEventCallback = (event: DebugEvent) => void + +/** + * Options for debug mode in FathomProvider. + */ +export interface DebugOptions { + /** + * Enable debug mode. + * @default false + */ + enabled?: boolean + /** + * Log tracking calls to the console. + * @default true when debug is enabled + */ + console?: boolean + /** + * Callback fired when any tracking call is made. + * Use this to integrate with custom UI (e.g., toast notifications). + */ + onTrack?: DebugEventCallback +} + export interface FathomClient { blockTrackingForMe: () => void enableTrackingForMe: () => void @@ -28,6 +76,15 @@ export interface FathomContextInterface { client?: FathomClient defaultPageviewOptions?: PageViewOptions defaultEventOptions?: EventOptions + /** + * Subscribe to debug events. Returns an unsubscribe function. + * Only available when debug mode is enabled. + */ + subscribeToDebug?: (callback: DebugEventCallback) => () => void + /** + * Whether debug mode is enabled. + */ + debugEnabled?: boolean } export interface FathomProviderProps extends PropsWithChildren { @@ -60,4 +117,25 @@ export interface FathomProviderProps extends PropsWithChildren { siteId?: string defaultPageviewOptions?: PageViewOptions defaultEventOptions?: EventOptions + /** + * Enable debug mode to log and/or receive callbacks for all tracking calls. + * Useful for development, demos, and debugging. + * Does not block actual Fathom tracking. + * + * @example + * ```tsx + * // Simple console logging + * + * + * // Custom callback for toast notifications + * showToast(event) + * }} + * /> + * ``` + */ + debug?: DebugOptions | boolean } From f6ca3f48a876908b8a73e5864342cbd6e60e2e4a Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 19:52:27 +0000 Subject: [PATCH 23/54] Add EventStream component and event tracking for docs site - Create EventStream component to display debug events as a toggleable toast stream - Enable debug mode in docs site FathomProvider for event visualization - Add event tracking to CodeBlock (copy code) and Search (open, result click) components - EventStream persists visibility preference in localStorage --- docs/app/provider.tsx | 15 +- docs/components/docs/CodeBlock.tsx | 3 + docs/components/docs/EventStream.tsx | 234 +++++++++++++++++++++++++++ docs/components/docs/Search.tsx | 22 ++- 4 files changed, 262 insertions(+), 12 deletions(-) create mode 100644 docs/components/docs/EventStream.tsx diff --git a/docs/app/provider.tsx b/docs/app/provider.tsx index a97b6d5..3d75244 100644 --- a/docs/app/provider.tsx +++ b/docs/app/provider.tsx @@ -3,6 +3,7 @@ import { ChakraProvider, defaultSystem } from '@chakra-ui/react' import { NextFathomProviderApp } from 'react-fathom/next' import { ColorModeProvider } from './color-mode' +import { EventStream } from '../components/docs/EventStream' export function Provider({ children }: { children: React.ReactNode }) { const siteId = process.env.NEXT_PUBLIC_FATHOM_SITE_ID @@ -10,13 +11,13 @@ export function Provider({ children }: { children: React.ReactNode }) { return ( - {siteId ? ( - - {children} - - ) : ( - children - )} + + {children} + + ) diff --git a/docs/components/docs/CodeBlock.tsx b/docs/components/docs/CodeBlock.tsx index 277c382..a336a02 100644 --- a/docs/components/docs/CodeBlock.tsx +++ b/docs/components/docs/CodeBlock.tsx @@ -2,16 +2,19 @@ import { Box, IconButton } from '@chakra-ui/react' import { useState, useRef, type ReactNode, type ComponentProps } from 'react' +import { useFathom } from 'react-fathom' // Pre component for rehype-pretty-code export function Pre({ children, ...props }: ComponentProps<'pre'>) { const [copied, setCopied] = useState(false) const preRef = useRef(null) + const { trackEvent } = useFathom() const handleCopy = async () => { const text = preRef.current?.textContent || '' await navigator.clipboard.writeText(text) setCopied(true) + trackEvent?.('code-copy') setTimeout(() => setCopied(false), 2000) } diff --git a/docs/components/docs/EventStream.tsx b/docs/components/docs/EventStream.tsx new file mode 100644 index 0000000..198ab60 --- /dev/null +++ b/docs/components/docs/EventStream.tsx @@ -0,0 +1,234 @@ +'use client' + +import { useState, useEffect } from 'react' +import { Box, Button, Flex, IconButton, Text, VStack } from '@chakra-ui/react' +import { useDebugSubscription, type DebugEvent } from 'react-fathom' + +const STORAGE_KEY = 'react-fathom-event-stream-visible' + +function EventIcon({ type }: { type: DebugEvent['type'] }) { + const icons = { + pageview: 'πŸ“„', + event: '🎯', + goal: 'πŸ†', + } + return {icons[type]} +} + +function formatTime(timestamp: number): string { + return new Date(timestamp).toLocaleTimeString('en-US', { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }) +} + +function EventCard({ event }: { event: DebugEvent }) { + const bgColor = { + pageview: { light: 'blue.50', dark: 'blue.950' }, + event: { light: 'purple.50', dark: 'purple.950' }, + goal: { light: 'green.50', dark: 'green.950' }, + } + const borderColor = { + pageview: 'blue.400', + event: 'purple.400', + goal: 'green.400', + } + + let title = '' + let subtitle = '' + + switch (event.type) { + case 'pageview': + title = 'Pageview' + subtitle = event.url || window.location.pathname + break + case 'event': + title = 'Event' + subtitle = event.eventName || '' + break + case 'goal': + title = 'Goal' + subtitle = `${event.goalCode} ($${((event.goalCents || 0) / 100).toFixed(2)})` + break + } + + return ( + + + + + + {title} + + + + {formatTime(event.timestamp)} + + + + {subtitle} + + + ) +} + +export function EventStream() { + const [isVisible, setIsVisible] = useState(false) + const [isHydrated, setIsHydrated] = useState(false) + const { events, debugEnabled, clearEvents } = useDebugSubscription({ + maxEvents: 20, + }) + + // Load visibility state from localStorage on mount + useEffect(() => { + setIsHydrated(true) + const stored = localStorage.getItem(STORAGE_KEY) + if (stored !== null) { + setIsVisible(stored === 'true') + } + }, []) + + // Save visibility state to localStorage + useEffect(() => { + if (isHydrated) { + localStorage.setItem(STORAGE_KEY, String(isVisible)) + } + }, [isVisible, isHydrated]) + + // Don't render if debug mode is not enabled + if (!debugEnabled) { + return null + } + + // Don't render until hydrated to avoid SSR mismatch + if (!isHydrated) { + return null + } + + return ( + <> + {/* Toggle button */} + setIsVisible(!isVisible)} + boxShadow="lg" + > + {isVisible ? 'βœ•' : 'πŸ“Š'} + + + {/* Event stream panel */} + {isVisible && ( + + {/* Header */} + + + πŸ“Š + Event Stream + + + + + {/* Events list */} + + {events.length === 0 ? ( + + πŸ” + + No events yet. +
+ Navigate or interact to see tracking events. +
+
+ ) : ( + + {events.map((event) => ( + + ))} + + )} +
+ + {/* Footer */} + + + Debug mode enabled β€’ {events.length} event{events.length !== 1 ? 's' : ''} + + +
+ )} + + ) +} diff --git a/docs/components/docs/Search.tsx b/docs/components/docs/Search.tsx index 8d42217..6d040dc 100644 --- a/docs/components/docs/Search.tsx +++ b/docs/components/docs/Search.tsx @@ -10,6 +10,7 @@ import { } from '@chakra-ui/react' import NextLink from 'next/link' import { useCallback, useEffect, useRef, useState } from 'react' +import { useFathom } from 'react-fathom' interface SearchResult { url: string @@ -34,6 +35,7 @@ export function Search() { const [isLoading, setIsLoading] = useState(false) const pagefindRef = useRef(null) const inputRef = useRef(null) + const { trackEvent } = useFathom() // Load Pagefind on first open useEffect(() => { @@ -65,13 +67,17 @@ export function Search() { const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault() + trackEvent?.('search-open') setIsOpen(true) } if (e.key === 'Escape') { setIsOpen(false) } } - const handleOpenSearch = () => setIsOpen(true) + const handleOpenSearch = () => { + trackEvent?.('search-open') + setIsOpen(true) + } document.addEventListener('keydown', handleKeyDown) window.addEventListener('open-search', handleOpenSearch) @@ -79,7 +85,7 @@ export function Search() { document.removeEventListener('keydown', handleKeyDown) window.removeEventListener('open-search', handleOpenSearch) } - }, []) + }, [trackEvent]) // Search handler const handleSearch = useCallback(async (searchQuery: string) => { @@ -112,12 +118,18 @@ export function Search() { } }, []) - const handleResultClick = () => { + const handleResultClick = (result: SearchResult) => { + trackEvent?.('search-result-click') setIsOpen(false) setQuery('') setResults([]) } + const handleOpen = () => { + trackEvent?.('search-open') + setIsOpen(true) + } + return ( <> {/* Search trigger button */} @@ -132,7 +144,7 @@ export function Search() { color="fg.muted" fontSize="sm" _hover={{ borderColor: 'fg.muted' }} - onClick={() => setIsOpen(true)} + onClick={handleOpen} > πŸ” Search... @@ -221,7 +233,7 @@ export function Search() { key={i} asChild _hover={{ textDecoration: 'none' }} - onClick={handleResultClick} + onClick={() => handleResultClick(result)} > Date: Thu, 22 Jan 2026 19:53:13 +0000 Subject: [PATCH 24/54] Add debug mode documentation to README - Add debug mode to features list - Document debug prop and useDebugSubscription hook - Add useDebugSubscription to hooks table --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index a3210db..3524986 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ The official `fathom-client` works, but: - πŸ›€οΈ React Router v6+ and Remix support - 🏠 Gatsby support with @reach/router integration - 🧭 TanStack Router support with type-safe routing +- πŸ› Debug mode for development with event subscription hooks - 🌳 Tree-shakeable, fully typed (TypeScript) ## Usage @@ -243,6 +244,35 @@ function App() { πŸ“– [Full React Native guide](https://react-fathom.com/react-native) +### Debug Mode + +Enable debug mode to log tracking calls and subscribe to events for custom UI: + +```tsx +// Console logging during development + + +// Subscribe to events programmatically +import { useDebugSubscription } from 'react-fathom' + +function DebugPanel() { + const { events, debugEnabled, clearEvents } = useDebugSubscription({ + maxEvents: 20, + onEvent: (event) => console.log('Tracked:', event) + }) + + if (!debugEnabled) return null + + return ( +
+ {events.map(e =>
{e.type}: {e.eventName || e.url}
)} +
+ ) +} +``` + +Debug mode does not block actual trackingβ€”events are still sent to Fathom. + ## API Overview ### Providers @@ -265,6 +295,7 @@ function App() { | `useTrackOnMount(opts?)` | Track pageview when component mounts | | `useTrackOnClick(opts)` | Returns click handler that tracks event | | `useTrackOnVisible(opts)` | Returns ref; tracks when element becomes visible | +| `useDebugSubscription(opts?)` | Subscribe to debug events for custom UI | ### Components From 2422d2ae5b3fe25539f9a50cc395d08fb97015cd Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 19:56:12 +0000 Subject: [PATCH 25/54] Remove unnecessary optional chaining on tracking methods The FathomContext provides default stub implementations for all tracking methods, so optional chaining is not needed when calling them. --- docs/components/docs/CodeBlock.tsx | 2 +- docs/components/docs/Search.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/components/docs/CodeBlock.tsx b/docs/components/docs/CodeBlock.tsx index a336a02..35827dc 100644 --- a/docs/components/docs/CodeBlock.tsx +++ b/docs/components/docs/CodeBlock.tsx @@ -14,7 +14,7 @@ export function Pre({ children, ...props }: ComponentProps<'pre'>) { const text = preRef.current?.textContent || '' await navigator.clipboard.writeText(text) setCopied(true) - trackEvent?.('code-copy') + trackEvent('code-copy') setTimeout(() => setCopied(false), 2000) } diff --git a/docs/components/docs/Search.tsx b/docs/components/docs/Search.tsx index 6d040dc..592d200 100644 --- a/docs/components/docs/Search.tsx +++ b/docs/components/docs/Search.tsx @@ -67,7 +67,7 @@ export function Search() { const handleKeyDown = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault() - trackEvent?.('search-open') + trackEvent('search-open') setIsOpen(true) } if (e.key === 'Escape') { @@ -119,7 +119,7 @@ export function Search() { }, []) const handleResultClick = (result: SearchResult) => { - trackEvent?.('search-result-click') + trackEvent('search-result-click') setIsOpen(false) setQuery('') setResults([]) From e0a6981326f4d41059a4e6f05e273d1e7e38324f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 20:17:59 +0000 Subject: [PATCH 26/54] Add keyboard shortcut to toggle EventStream panel - Add Cmd/Ctrl + . keyboard shortcut to toggle visibility - Show shortcut hint in the panel footer --- docs/components/docs/EventStream.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/components/docs/EventStream.tsx b/docs/components/docs/EventStream.tsx index 198ab60..51ed8e6 100644 --- a/docs/components/docs/EventStream.tsx +++ b/docs/components/docs/EventStream.tsx @@ -109,6 +109,18 @@ export function EventStream() { } }, []) + // Keyboard shortcut (Cmd/Ctrl + .) to toggle + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === '.') { + e.preventDefault() + setIsVisible((prev) => !prev) + } + } + document.addEventListener('keydown', handleKeyDown) + return () => document.removeEventListener('keydown', handleKeyDown) + }, []) + // Save visibility state to localStorage useEffect(() => { if (isHydrated) { @@ -224,7 +236,11 @@ export function EventStream() { _dark={{ bg: 'gray.800', borderTopColor: 'gray.700' }} > - Debug mode enabled β€’ {events.length} event{events.length !== 1 ? 's' : ''} + {events.length} event{events.length !== 1 ? 's' : ''} β€’ Press{' '} + + ⌘. + {' '} + to toggle
From f3e469ae608b8200d8e165af9ab49fcfb1a20aca Mon Sep 17 00:00:00 2001 From: Ryan Hefner Date: Thu, 22 Jan 2026 15:44:46 -0500 Subject: [PATCH 27/54] Upgrade dependencies --- package.json | 4 +- yarn.lock | 1484 +++++++++++++++++++++++++------------------------- 2 files changed, 738 insertions(+), 750 deletions(-) diff --git a/package.json b/package.json index a7c8629..001c12a 100644 --- a/package.json +++ b/package.json @@ -198,8 +198,6 @@ } }, "devDependencies": { - "@reach/router": "^1.3.4", - "@tanstack/react-router": "^1.120.5", "@babel/core": "^7.12.10", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-object-rest-spread": "^7.20.7", @@ -209,10 +207,12 @@ "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", "@eslint/js": "^9.0.0", + "@reach/router": "^1.3.4", "@rollup/plugin-babel": "^6.0.2", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^16.0.3", + "@tanstack/react-router": "^1.120.5", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.1", diff --git a/yarn.lock b/yarn.lock index bb1095c..8c4a517 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,9 +3,9 @@ "@acemir/cssom@^0.9.28": - version "0.9.30" - resolved "https://registry.yarnpkg.com/@acemir/cssom/-/cssom-0.9.30.tgz#78e73afd5284d2655f0a83458afefb2920d7bfba" - integrity sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg== + version "0.9.31" + resolved "https://registry.yarnpkg.com/@acemir/cssom/-/cssom-0.9.31.tgz#bd5337d290fb8be2ac18391f37386bc53778b0bc" + integrity sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA== "@adobe/css-tools@^4.4.0": version "4.4.4" @@ -39,34 +39,34 @@ resolved "https://registry.yarnpkg.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz#ad5549322dfe9d153d4b4dd6f7ff2ae234b06e24" integrity sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q== -"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== +"@babel/code-frame@^7.10.4", "@babel/code-frame@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.28.6.tgz#72499312ec58b1e2245ba4a4f550c132be4982f7" + integrity sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q== dependencies: - "@babel/helper-validator-identifier" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.27.2", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" - integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.27.7", "@babel/compat-data@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.6.tgz#103f466803fa0f059e82ccac271475470570d74c" + integrity sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg== "@babel/core@^7.12.10", "@babel/core@^7.24.4": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" - integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.5" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-module-transforms" "^7.28.3" - "@babel/helpers" "^7.28.4" - "@babel/parser" "^7.28.5" - "@babel/template" "^7.27.2" - "@babel/traverse" "^7.28.5" - "@babel/types" "^7.28.5" + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.6.tgz#531bf883a1126e53501ba46eb3bb414047af507f" + integrity sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw== + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/generator" "^7.28.6" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helpers" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" @@ -74,13 +74,13 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" - integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== +"@babel/generator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.6.tgz#48dcc65d98fcc8626a48f72b62e263d25fc3c3f1" + integrity sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw== dependencies: - "@babel/parser" "^7.28.5" - "@babel/types" "^7.28.5" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" "@jridgewell/gen-mapping" "^0.3.12" "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" @@ -92,31 +92,31 @@ dependencies: "@babel/types" "^7.27.3" -"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2", "@babel/helper-compilation-targets@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" + integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== dependencies: - "@babel/compat-data" "^7.27.2" + "@babel/compat-data" "^7.28.6" "@babel/helper-validator-option" "^7.27.1" browserslist "^4.24.0" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.27.1", "@babel/helper-create-class-features-plugin@^7.28.3", "@babel/helper-create-class-features-plugin@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz#472d0c28028850968979ad89f173594a6995da46" - integrity sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz#611ff5482da9ef0db6291bcd24303400bca170fb" + integrity sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" "@babel/helper-member-expression-to-functions" "^7.28.5" "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/helper-replace-supers" "^7.27.1" + "@babel/helper-replace-supers" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - "@babel/traverse" "^7.28.5" + "@babel/traverse" "^7.28.6" semver "^6.3.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.27.1": +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.27.1", "@babel/helper-create-regexp-features-plugin@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz#7c1ddd64b2065c7f78034b25b43346a7e19ed997" integrity sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw== @@ -141,7 +141,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== -"@babel/helper-member-expression-to-functions@^7.27.1", "@babel/helper-member-expression-to-functions@^7.28.5": +"@babel/helper-member-expression-to-functions@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150" integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== @@ -149,22 +149,22 @@ "@babel/traverse" "^7.28.5" "@babel/types" "^7.28.5" -"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.27.1", "@babel/helper-module-imports@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" - integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== +"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3", "@babel/helper-module-transforms@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" + integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.28.3" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.6" "@babel/helper-optimise-call-expression@^7.27.1": version "7.27.1" @@ -173,10 +173,10 @@ dependencies: "@babel/types" "^7.27.1" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" - integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.28.6", "@babel/helper-plugin-utils@^7.8.0": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8" + integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== "@babel/helper-remap-async-to-generator@^7.27.1": version "7.27.1" @@ -187,14 +187,14 @@ "@babel/helper-wrap-function" "^7.27.1" "@babel/traverse" "^7.27.1" -"@babel/helper-replace-supers@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" - integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== +"@babel/helper-replace-supers@^7.27.1", "@babel/helper-replace-supers@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz#94aa9a1d7423a00aead3f204f78834ce7d53fe44" + integrity sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg== dependencies: - "@babel/helper-member-expression-to-functions" "^7.27.1" + "@babel/helper-member-expression-to-functions" "^7.28.5" "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/traverse" "^7.27.1" + "@babel/traverse" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers@^7.27.1": version "7.27.1" @@ -209,7 +209,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": +"@babel/helper-validator-identifier@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== @@ -220,28 +220,28 @@ integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== "@babel/helper-wrap-function@^7.27.1": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz#fe4872092bc1438ffd0ce579e6f699609f9d0a7a" - integrity sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz#4e349ff9222dab69a93a019cc296cdd8442e279a" + integrity sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ== dependencies: - "@babel/template" "^7.27.2" - "@babel/traverse" "^7.28.3" - "@babel/types" "^7.28.2" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/helpers@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" - integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== +"@babel/helpers@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7" + integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw== dependencies: - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.4" + "@babel/template" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/parser@^7.24.4", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" - integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== +"@babel/parser@^7.24.4", "@babel/parser@^7.28.5", "@babel/parser@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.6.tgz#f01a8885b7fa1e56dd8a155130226cd698ef13fd" + integrity sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ== dependencies: - "@babel/types" "^7.28.5" + "@babel/types" "^7.28.6" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.28.5": version "7.28.5" @@ -274,13 +274,13 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" "@babel/plugin-transform-optional-chaining" "^7.27.1" -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz#373f6e2de0016f73caf8f27004f61d167743742a" - integrity sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw== +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz#0e8289cec28baaf05d54fd08d81ae3676065f69f" + integrity sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/traverse" "^7.28.3" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/traverse" "^7.28.6" "@babel/plugin-proposal-class-properties@^7.18.6": version "7.18.6" @@ -306,26 +306,26 @@ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== -"@babel/plugin-syntax-import-assertions@^7.18.6", "@babel/plugin-syntax-import-assertions@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" - integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== +"@babel/plugin-syntax-import-assertions@^7.18.6", "@babel/plugin-syntax-import-assertions@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz#ae9bc1923a6ba527b70104dd2191b0cd872c8507" + integrity sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-syntax-import-attributes@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" - integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== +"@babel/plugin-syntax-import-attributes@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz#b71d5914665f60124e133696f17cd7669062c503" + integrity sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-syntax-jsx@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" - integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== +"@babel/plugin-syntax-jsx@^7.27.1", "@babel/plugin-syntax-jsx@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz#f8ca28bbd84883b5fea0e447c635b81ba73997ee" + integrity sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" @@ -334,12 +334,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-typescript@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" - integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== +"@babel/plugin-syntax-typescript@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz#c7b2ddf1d0a811145b1de800d1abd146af92e3a2" + integrity sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" @@ -356,22 +356,22 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-async-generator-functions@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz#1276e6c7285ab2cd1eccb0bc7356b7a69ff842c2" - integrity sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q== +"@babel/plugin-transform-async-generator-functions@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.6.tgz#80cb86d3eaa2102e18ae90dd05ab87bdcad3877d" + integrity sha512-9knsChgsMzBV5Yh3kkhrZNxH3oCYAfMBkNNaVN4cP2RVlFPe8wYdwwcnOsAbkdDoV9UjFtOXWrWB52M8W4jNeA== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-remap-async-to-generator" "^7.27.1" - "@babel/traverse" "^7.28.0" + "@babel/traverse" "^7.28.6" -"@babel/plugin-transform-async-to-generator@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz#9a93893b9379b39466c74474f55af03de78c66e7" - integrity sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA== +"@babel/plugin-transform-async-to-generator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz#bd97b42237b2d1bc90d74bcb486c39be5b4d7e77" + integrity sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-remap-async-to-generator" "^7.27.1" "@babel/plugin-transform-block-scoped-functions@^7.27.1": @@ -381,50 +381,50 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-block-scoping@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz#e0d3af63bd8c80de2e567e690a54e84d85eb16f6" - integrity sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g== +"@babel/plugin-transform-block-scoping@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz#e1ef5633448c24e76346125c2534eeb359699a99" + integrity sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-class-properties@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz#dd40a6a370dfd49d32362ae206ddaf2bb082a925" - integrity sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA== +"@babel/plugin-transform-class-properties@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz#d274a4478b6e782d9ea987fda09bdb6d28d66b72" + integrity sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw== dependencies: - "@babel/helper-create-class-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-class-static-block@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz#d1b8e69b54c9993bc558203e1f49bfc979bfd852" - integrity sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg== +"@babel/plugin-transform-class-static-block@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz#1257491e8259c6d125ac4d9a6f39f9d2bf3dba70" + integrity sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.28.3" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-classes@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz#75d66175486788c56728a73424d67cbc7473495c" - integrity sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA== +"@babel/plugin-transform-classes@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz#8f6fb79ba3703978e701ce2a97e373aae7dda4b7" + integrity sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" - "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-compilation-targets" "^7.28.6" "@babel/helper-globals" "^7.28.0" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/helper-replace-supers" "^7.27.1" - "@babel/traverse" "^7.28.4" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/helper-replace-supers" "^7.28.6" + "@babel/traverse" "^7.28.6" -"@babel/plugin-transform-computed-properties@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz#81662e78bf5e734a97982c2b7f0a793288ef3caa" - integrity sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw== +"@babel/plugin-transform-computed-properties@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz#936824fc71c26cb5c433485776d79c8e7b0202d2" + integrity sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/template" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/template" "^7.28.6" -"@babel/plugin-transform-destructuring@^7.28.0", "@babel/plugin-transform-destructuring@^7.28.5": +"@babel/plugin-transform-destructuring@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz#b8402764df96179a2070bb7b501a1586cf8ad7a7" integrity sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw== @@ -432,13 +432,13 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/traverse" "^7.28.5" -"@babel/plugin-transform-dotall-regex@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz#aa6821de864c528b1fecf286f0a174e38e826f4d" - integrity sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw== +"@babel/plugin-transform-dotall-regex@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz#def31ed84e0fb6e25c71e53c124e7b76a4ab8e61" + integrity sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-duplicate-keys@^7.27.1": version "7.27.1" @@ -447,13 +447,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz#5043854ca620a94149372e69030ff8cb6a9eb0ec" - integrity sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ== +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.28.6.tgz#e0c59ba54f1655dd682f2edf5f101b5910a8f6f3" + integrity sha512-5suVoXjC14lUN6ZL9OLKIHCNVWCrqGqlmEp/ixdXjvgnEl/kauLvvMO/Xw9NyMc95Joj1AeLVPVMvibBgSoFlA== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-dynamic-import@^7.27.1": version "7.27.1" @@ -462,20 +462,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-explicit-resource-management@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz#45be6211b778dbf4b9d54c4e8a2b42fa72e09a1a" - integrity sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ== +"@babel/plugin-transform-explicit-resource-management@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz#dd6788f982c8b77e86779d1d029591e39d9d8be7" + integrity sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-transform-destructuring" "^7.28.0" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/plugin-transform-destructuring" "^7.28.5" -"@babel/plugin-transform-exponentiation-operator@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz#7cc90a8170e83532676cfa505278e147056e94fe" - integrity sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw== +"@babel/plugin-transform-exponentiation-operator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz#5e477eb7eafaf2ab5537a04aaafcf37e2d7f1091" + integrity sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-export-namespace-from@^7.27.1": version "7.27.1" @@ -501,12 +501,12 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/traverse" "^7.27.1" -"@babel/plugin-transform-json-strings@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz#a2e0ce6ef256376bd527f290da023983527a4f4c" - integrity sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q== +"@babel/plugin-transform-json-strings@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz#4c8c15b2dc49e285d110a4cf3dac52fd2dfc3038" + integrity sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-literals@^7.27.1": version "7.27.1" @@ -515,12 +515,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-logical-assignment-operators@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz#d028fd6db8c081dee4abebc812c2325e24a85b0e" - integrity sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA== +"@babel/plugin-transform-logical-assignment-operators@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz#53028a3d77e33c50ef30a8fce5ca17065936e605" + integrity sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-member-expression-literals@^7.27.1": version "7.27.1" @@ -537,13 +537,13 @@ "@babel/helper-module-transforms" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-modules-commonjs@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" - integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== +"@babel/plugin-transform-modules-commonjs@^7.27.1", "@babel/plugin-transform-modules-commonjs@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz#c0232e0dfe66a734cc4ad0d5e75fc3321b6fdef1" + integrity sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA== dependencies: - "@babel/helper-module-transforms" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-modules-systemjs@^7.28.5": version "7.28.5" @@ -578,30 +578,30 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-nullish-coalescing-operator@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz#4f9d3153bf6782d73dd42785a9d22d03197bc91d" - integrity sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA== +"@babel/plugin-transform-nullish-coalescing-operator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz#9bc62096e90ab7a887f3ca9c469f6adec5679757" + integrity sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-numeric-separator@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz#614e0b15cc800e5997dadd9bd6ea524ed6c819c6" - integrity sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw== +"@babel/plugin-transform-numeric-separator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz#1310b0292762e7a4a335df5f580c3320ee7d9e9f" + integrity sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-object-rest-spread@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz#9ee1ceca80b3e6c4bac9247b2149e36958f7f98d" - integrity sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew== +"@babel/plugin-transform-object-rest-spread@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz#fdd4bc2d72480db6ca42aed5c051f148d7b067f7" + integrity sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA== dependencies: - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-transform-destructuring" "^7.28.0" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/plugin-transform-destructuring" "^7.28.5" "@babel/plugin-transform-parameters" "^7.27.7" - "@babel/traverse" "^7.28.4" + "@babel/traverse" "^7.28.6" "@babel/plugin-transform-object-super@^7.27.1": version "7.27.1" @@ -611,19 +611,19 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/helper-replace-supers" "^7.27.1" -"@babel/plugin-transform-optional-catch-binding@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz#84c7341ebde35ccd36b137e9e45866825072a30c" - integrity sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q== +"@babel/plugin-transform-optional-catch-binding@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz#75107be14c78385978201a49c86414a150a20b4c" + integrity sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-optional-chaining@^7.27.1", "@babel/plugin-transform-optional-chaining@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz#8238c785f9d5c1c515a90bf196efb50d075a4b26" - integrity sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ== +"@babel/plugin-transform-optional-chaining@^7.27.1", "@babel/plugin-transform-optional-chaining@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz#926cf150bd421fc8362753e911b4a1b1ce4356cd" + integrity sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.27.7": @@ -633,22 +633,22 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-private-methods@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz#fdacbab1c5ed81ec70dfdbb8b213d65da148b6af" - integrity sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA== +"@babel/plugin-transform-private-methods@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz#c76fbfef3b86c775db7f7c106fff544610bdb411" + integrity sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg== dependencies: - "@babel/helper-create-class-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-private-property-in-object@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz#4dbbef283b5b2f01a21e81e299f76e35f900fb11" - integrity sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ== +"@babel/plugin-transform-private-property-in-object@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz#4fafef1e13129d79f1d75ac180c52aafefdb2811" + integrity sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA== dependencies: - "@babel/helper-annotate-as-pure" "^7.27.1" - "@babel/helper-create-class-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-property-literals@^7.27.1": version "7.27.1" @@ -672,15 +672,15 @@ "@babel/plugin-transform-react-jsx" "^7.27.1" "@babel/plugin-transform-react-jsx@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz#1023bc94b78b0a2d68c82b5e96aed573bcfb9db0" - integrity sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz#f51cb70a90b9529fbb71ee1f75ea27b7078eed62" + integrity sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow== dependencies: - "@babel/helper-annotate-as-pure" "^7.27.1" - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-syntax-jsx" "^7.27.1" - "@babel/types" "^7.27.1" + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" + "@babel/plugin-syntax-jsx" "^7.28.6" + "@babel/types" "^7.28.6" "@babel/plugin-transform-react-pure-annotations@^7.27.1": version "7.27.1" @@ -690,20 +690,20 @@ "@babel/helper-annotate-as-pure" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-regenerator@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz#9d3fa3bebb48ddd0091ce5729139cd99c67cea51" - integrity sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA== +"@babel/plugin-transform-regenerator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.6.tgz#6ca2ed5b76cff87980f96eaacfc2ce833e8e7a1b" + integrity sha512-eZhoEZHYQLL5uc1gS5e9/oTknS0sSSAtd5TkKMUp3J+S/CaUjagc0kOUPsEbDmMeva0nC3WWl4SxVY6+OBuxfw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/plugin-transform-regexp-modifiers@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz#df9ba5577c974e3f1449888b70b76169998a6d09" - integrity sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA== +"@babel/plugin-transform-regexp-modifiers@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz#7ef0163bd8b4a610481b2509c58cf217f065290b" + integrity sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-reserved-words@^7.27.1": version "7.27.1" @@ -731,12 +731,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-spread@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz#1a264d5fc12750918f50e3fe3e24e437178abb08" - integrity sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q== +"@babel/plugin-transform-spread@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz#40a2b423f6db7b70f043ad027a58bcb44a9757b6" + integrity sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" "@babel/plugin-transform-sticky-regex@^7.27.1": @@ -761,15 +761,15 @@ "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-typescript@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.5.tgz#441c5f9a4a1315039516c6c612fc66d5f4594e72" - integrity sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz#1e93d96da8adbefdfdade1d4956f73afa201a158" + integrity sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" - "@babel/helper-create-class-features-plugin" "^7.28.5" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-class-features-plugin" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - "@babel/plugin-syntax-typescript" "^7.27.1" + "@babel/plugin-syntax-typescript" "^7.28.6" "@babel/plugin-transform-unicode-escapes@^7.27.1": version "7.27.1" @@ -778,13 +778,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-unicode-property-regex@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz#bdfe2d3170c78c5691a3c3be934c8c0087525956" - integrity sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q== +"@babel/plugin-transform-unicode-property-regex@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz#63a7a6c21a0e75dae9b1861454111ea5caa22821" + integrity sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-transform-unicode-regex@^7.27.1": version "7.27.1" @@ -794,83 +794,83 @@ "@babel/helper-create-regexp-features-plugin" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-unicode-sets-regex@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz#6ab706d10f801b5c72da8bb2548561fa04193cd1" - integrity sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw== +"@babel/plugin-transform-unicode-sets-regex@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz#924912914e5df9fe615ec472f88ff4788ce04d4e" + integrity sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q== dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-create-regexp-features-plugin" "^7.28.5" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/preset-env@^7.12.11": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.28.5.tgz#82dd159d1563f219a1ce94324b3071eb89e280b0" - integrity sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.28.6.tgz#b4586bb59d8c61be6c58997f4912e7ea6bd17178" + integrity sha512-GaTI4nXDrs7l0qaJ6Rg06dtOXTBCG6TMDB44zbqofCIC4PqC7SEvmFFtpxzCDw9W5aJ7RKVshgXTLvLdBFV/qw== dependencies: - "@babel/compat-data" "^7.28.5" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/compat-data" "^7.28.6" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/helper-validator-option" "^7.27.1" "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.28.5" "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.27.1" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.27.1" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.27.1" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.28.3" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.28.6" "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-import-assertions" "^7.27.1" - "@babel/plugin-syntax-import-attributes" "^7.27.1" + "@babel/plugin-syntax-import-assertions" "^7.28.6" + "@babel/plugin-syntax-import-attributes" "^7.28.6" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.27.1" - "@babel/plugin-transform-async-generator-functions" "^7.28.0" - "@babel/plugin-transform-async-to-generator" "^7.27.1" + "@babel/plugin-transform-async-generator-functions" "^7.28.6" + "@babel/plugin-transform-async-to-generator" "^7.28.6" "@babel/plugin-transform-block-scoped-functions" "^7.27.1" - "@babel/plugin-transform-block-scoping" "^7.28.5" - "@babel/plugin-transform-class-properties" "^7.27.1" - "@babel/plugin-transform-class-static-block" "^7.28.3" - "@babel/plugin-transform-classes" "^7.28.4" - "@babel/plugin-transform-computed-properties" "^7.27.1" + "@babel/plugin-transform-block-scoping" "^7.28.6" + "@babel/plugin-transform-class-properties" "^7.28.6" + "@babel/plugin-transform-class-static-block" "^7.28.6" + "@babel/plugin-transform-classes" "^7.28.6" + "@babel/plugin-transform-computed-properties" "^7.28.6" "@babel/plugin-transform-destructuring" "^7.28.5" - "@babel/plugin-transform-dotall-regex" "^7.27.1" + "@babel/plugin-transform-dotall-regex" "^7.28.6" "@babel/plugin-transform-duplicate-keys" "^7.27.1" - "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.27.1" + "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.28.6" "@babel/plugin-transform-dynamic-import" "^7.27.1" - "@babel/plugin-transform-explicit-resource-management" "^7.28.0" - "@babel/plugin-transform-exponentiation-operator" "^7.28.5" + "@babel/plugin-transform-explicit-resource-management" "^7.28.6" + "@babel/plugin-transform-exponentiation-operator" "^7.28.6" "@babel/plugin-transform-export-namespace-from" "^7.27.1" "@babel/plugin-transform-for-of" "^7.27.1" "@babel/plugin-transform-function-name" "^7.27.1" - "@babel/plugin-transform-json-strings" "^7.27.1" + "@babel/plugin-transform-json-strings" "^7.28.6" "@babel/plugin-transform-literals" "^7.27.1" - "@babel/plugin-transform-logical-assignment-operators" "^7.28.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.28.6" "@babel/plugin-transform-member-expression-literals" "^7.27.1" "@babel/plugin-transform-modules-amd" "^7.27.1" - "@babel/plugin-transform-modules-commonjs" "^7.27.1" + "@babel/plugin-transform-modules-commonjs" "^7.28.6" "@babel/plugin-transform-modules-systemjs" "^7.28.5" "@babel/plugin-transform-modules-umd" "^7.27.1" "@babel/plugin-transform-named-capturing-groups-regex" "^7.27.1" "@babel/plugin-transform-new-target" "^7.27.1" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.27.1" - "@babel/plugin-transform-numeric-separator" "^7.27.1" - "@babel/plugin-transform-object-rest-spread" "^7.28.4" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.28.6" + "@babel/plugin-transform-numeric-separator" "^7.28.6" + "@babel/plugin-transform-object-rest-spread" "^7.28.6" "@babel/plugin-transform-object-super" "^7.27.1" - "@babel/plugin-transform-optional-catch-binding" "^7.27.1" - "@babel/plugin-transform-optional-chaining" "^7.28.5" + "@babel/plugin-transform-optional-catch-binding" "^7.28.6" + "@babel/plugin-transform-optional-chaining" "^7.28.6" "@babel/plugin-transform-parameters" "^7.27.7" - "@babel/plugin-transform-private-methods" "^7.27.1" - "@babel/plugin-transform-private-property-in-object" "^7.27.1" + "@babel/plugin-transform-private-methods" "^7.28.6" + "@babel/plugin-transform-private-property-in-object" "^7.28.6" "@babel/plugin-transform-property-literals" "^7.27.1" - "@babel/plugin-transform-regenerator" "^7.28.4" - "@babel/plugin-transform-regexp-modifiers" "^7.27.1" + "@babel/plugin-transform-regenerator" "^7.28.6" + "@babel/plugin-transform-regexp-modifiers" "^7.28.6" "@babel/plugin-transform-reserved-words" "^7.27.1" "@babel/plugin-transform-shorthand-properties" "^7.27.1" - "@babel/plugin-transform-spread" "^7.27.1" + "@babel/plugin-transform-spread" "^7.28.6" "@babel/plugin-transform-sticky-regex" "^7.27.1" "@babel/plugin-transform-template-literals" "^7.27.1" "@babel/plugin-transform-typeof-symbol" "^7.27.1" "@babel/plugin-transform-unicode-escapes" "^7.27.1" - "@babel/plugin-transform-unicode-property-regex" "^7.27.1" + "@babel/plugin-transform-unicode-property-regex" "^7.28.6" "@babel/plugin-transform-unicode-regex" "^7.27.1" - "@babel/plugin-transform-unicode-sets-regex" "^7.27.1" + "@babel/plugin-transform-unicode-sets-regex" "^7.28.6" "@babel/preset-modules" "0.1.6-no-external-plugins" babel-plugin-polyfill-corejs2 "^0.4.14" babel-plugin-polyfill-corejs3 "^0.13.0" @@ -911,36 +911,36 @@ "@babel/plugin-transform-typescript" "^7.28.5" "@babel/runtime@^7.12.5": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" - integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== - -"@babel/template@^7.27.1", "@babel/template@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" - -"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.0", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" - integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.5" + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b" + integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== + +"@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" + +"@babel/traverse@^7.27.1", "@babel/traverse@^7.28.5", "@babel/traverse@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.6.tgz#871ddc79a80599a5030c53b1cc48cbe3a5583c2e" + integrity sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg== + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/generator" "^7.28.6" "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.5" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.5" + "@babel/parser" "^7.28.6" + "@babel/template" "^7.28.6" + "@babel/types" "^7.28.6" debug "^4.3.1" -"@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5", "@babel/types@^7.4.4": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" - integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== +"@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.5", "@babel/types@^7.28.6", "@babel/types@^7.4.4": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.6.tgz#c3e9377f1b155005bcc4c46020e7e394e13089df" + integrity sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg== dependencies: "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.28.5" @@ -974,9 +974,9 @@ integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== "@csstools/css-syntax-patches-for-csstree@^1.0.21": - version "1.0.23" - resolved "https://registry.yarnpkg.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.23.tgz#0642a7627f10eb7f8c95c27fffbe9f726c1ff655" - integrity sha512-YEmgyklR6l/oKUltidNVYdjSmLSW88vMsKx0pmiS3r71s8ZZRpd8A0Yf0U+6p/RzElmMnPBv27hNWjDQMSZRtQ== + version "1.0.25" + resolved "https://registry.yarnpkg.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.25.tgz#200b4680988f33b07c2dfea70e6fddebaa578470" + integrity sha512-g0Kw9W3vjx5BEBAF8c5Fm2NcB/Fs8jJXh85aXqwEXiL+tqtOut07TWgyaGzAAfTM+gKckrrncyeGEZPcaRgm2Q== "@csstools/css-tokenizer@^3.0.4": version "3.0.4" @@ -1189,9 +1189,9 @@ levn "^0.4.1" "@exodus/bytes@^1.6.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@exodus/bytes/-/bytes-1.8.0.tgz#8382835f71db8377cf634a4ef5a71806e86ba9c7" - integrity sha512-8JPn18Bcp8Uo1T82gR8lh2guEOa5KKU/IEKvvdp0sgmi7coPBWf1Doi1EXsGZb2ehc8ym/StJCjffYV+ne7sXQ== + version "1.9.0" + resolved "https://registry.yarnpkg.com/@exodus/bytes/-/bytes-1.9.0.tgz#1644554c4103d956bf90196ae961ced85f99c972" + integrity sha512-lagqsvnk09NKogQaN/XrtlWeUF8SRhT12odMvbTIIaVObqzwAogL6jhR4DAp0gPuKoM1AOVrKUshJpRdpMFrww== "@humanfs/core@^0.19.1": version "0.19.1" @@ -1409,7 +1409,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28", "@jridgewell/trace-mapping@^0.3.31": +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28", "@jridgewell/trace-mapping@^0.3.31": version "0.3.31" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== @@ -1417,50 +1417,50 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@next/env@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@next/env/-/env-16.1.1.tgz#3d06a470efff135746ef609cc02a4996512bd9ab" - integrity sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA== - -"@next/swc-darwin-arm64@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.1.tgz#1e7e87fd21fcabce546dfb04fb946ecd9f866917" - integrity sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA== - -"@next/swc-darwin-x64@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.1.tgz#6617d03b96bdffad7bf4df50d4a699faea0d04c3" - integrity sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw== - -"@next/swc-linux-arm64-gnu@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.1.tgz#d8337e2f4881a80221a1f56ac3f979b665b7e574" - integrity sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ== - -"@next/swc-linux-arm64-musl@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.1.tgz#6c24392824406a50f27fb9cf4e49362d4666db1c" - integrity sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg== - -"@next/swc-linux-x64-gnu@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.1.tgz#f1bd19fc7d119f27c4cf7f51915aef0f119c943f" - integrity sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ== - -"@next/swc-linux-x64-musl@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.1.tgz#20c1ea0c98988c337614ce6fda01b82300ff00fd" - integrity sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA== - -"@next/swc-win32-arm64-msvc@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.1.tgz#9d66be9dc4ebc458d445a7f6ee804f416d5c2daf" - integrity sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA== - -"@next/swc-win32-x64-msvc@16.1.1": - version "16.1.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.1.tgz#96e9e335e2577481dab6fc7a2af48f3e4a28fbdb" - integrity sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw== +"@next/env@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/env/-/env-16.1.4.tgz#1f5155b16bad9825432b5e398b83df687b7b86f9" + integrity sha512-gkrXnZyxPUy0Gg6SrPQPccbNVLSP3vmW8LU5dwEttEEC1RwDivk8w4O+sZIjFvPrSICXyhQDCG+y3VmjlJf+9A== + +"@next/swc-darwin-arm64@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.4.tgz#2d5ee68da80c9b822edd06caa360aef1917d0f37" + integrity sha512-T8atLKuvk13XQUdVLCv1ZzMPgLPW0+DWWbHSQXs0/3TjPrKNxTmUIhOEaoEyl3Z82k8h/gEtqyuoZGv6+Ugawg== + +"@next/swc-darwin-x64@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.4.tgz#2f8d4462f48d4cb3c927de1962ca7a7b2f8a5b03" + integrity sha512-AKC/qVjUGUQDSPI6gESTx0xOnOPQ5gttogNS3o6bA83yiaSZJek0Am5yXy82F1KcZCx3DdOwdGPZpQCluonuxg== + +"@next/swc-linux-arm64-gnu@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.4.tgz#79fecac25ad4a0ee1081110f4c8863b87e754943" + integrity sha512-POQ65+pnYOkZNdngWfMEt7r53bzWiKkVNbjpmCt1Zb3V6lxJNXSsjwRuTQ8P/kguxDC8LRkqaL3vvsFrce4dMQ== + +"@next/swc-linux-arm64-musl@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.4.tgz#e9a99b1ea9a68908c3d36a847a6fe367b4fc3855" + integrity sha512-3Wm0zGYVCs6qDFAiSSDL+Z+r46EdtCv/2l+UlIdMbAq9hPJBvGu/rZOeuvCaIUjbArkmXac8HnTyQPJFzFWA0Q== + +"@next/swc-linux-x64-gnu@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.4.tgz#4804de5f42ac8333e0049ab538473cbd996507f6" + integrity sha512-lWAYAezFinaJiD5Gv8HDidtsZdT3CDaCeqoPoJjeB57OqzvMajpIhlZFce5sCAH6VuX4mdkxCRqecCJFwfm2nQ== + +"@next/swc-linux-x64-musl@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.4.tgz#4aa01e59b0e0fd19ab493ee239e3904c42419ca6" + integrity sha512-fHaIpT7x4gA6VQbdEpYUXRGyge/YbRrkG6DXM60XiBqDM2g2NcrsQaIuj375egnGFkJow4RHacgBOEsHfGbiUw== + +"@next/swc-win32-arm64-msvc@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.4.tgz#67652a5c57889f44c11e145d49f777ac2e6cde58" + integrity sha512-MCrXxrTSE7jPN1NyXJr39E+aNFBrQZtO154LoCz7n99FuKqJDekgxipoodLNWdQP7/DZ5tKMc/efybx1l159hw== + +"@next/swc-win32-x64-msvc@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.4.tgz#3c51597eb64a96b8fcade74ab3f21ef3ad278a33" + integrity sha512-JSVlm9MDhmTXw/sO2PE/MRj+G6XOSMZB+BcZ0a7d6KwVFZVpkHcb2okyoYFBaco6LeiL53BBklRlOrDDbOeE5w== "@pkgr/core@^0.2.9": version "0.2.9" @@ -1525,130 +1525,130 @@ estree-walker "^2.0.2" picomatch "^4.0.2" -"@rollup/rollup-android-arm-eabi@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz#76e0fef6533b3ce313f969879e61e8f21f0eeb28" - integrity sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg== - -"@rollup/rollup-android-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz#d3cfc675a40bbdec97bda6d7fe3b3b05f0e1cd93" - integrity sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg== - -"@rollup/rollup-darwin-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz#eb912b8f59dd47c77b3c50a78489013b1d6772b4" - integrity sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg== - -"@rollup/rollup-darwin-x64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz#e7d0839fdfd1276a1d34bc5ebbbd0dfd7d0b81a0" - integrity sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ== - -"@rollup/rollup-freebsd-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz#7ff8118760f7351e48fd0cd3717ff80543d6aac8" - integrity sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg== - -"@rollup/rollup-freebsd-x64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz#49d330dadbda1d4e9b86b4a3951b59928a9489a9" - integrity sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw== - -"@rollup/rollup-linux-arm-gnueabihf@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz#98c5f1f8b9776b4a36e466e2a1c9ed1ba52ef1b6" - integrity sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ== - -"@rollup/rollup-linux-arm-musleabihf@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz#b9acecd3672e742f70b0c8a94075c816a91ff040" - integrity sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg== - -"@rollup/rollup-linux-arm64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz#7a6ab06651bc29e18b09a50ed1a02bc972977c9b" - integrity sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ== - -"@rollup/rollup-linux-arm64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz#3c8c9072ba4a4d4ef1156b85ab9a2cbb57c1fad0" - integrity sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA== - -"@rollup/rollup-linux-loong64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz#17a7af13530f4e4a7b12cd26276c54307a84a8b0" - integrity sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g== - -"@rollup/rollup-linux-loong64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz#5cd7a900fd7b077ecd753e34a9b7ff1157fe70c1" - integrity sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw== - -"@rollup/rollup-linux-ppc64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz#03a097e70243ddf1c07b59d3c20f38e6f6800539" - integrity sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw== - -"@rollup/rollup-linux-ppc64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz#a5389873039d4650f35b4fa060d286392eb21a94" - integrity sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw== - -"@rollup/rollup-linux-riscv64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz#789e60e7d6e2b76132d001ffb24ba80007fb17d0" - integrity sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw== - -"@rollup/rollup-linux-riscv64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz#3556fa88d139282e9a73c337c9a170f3c5fe7aa4" - integrity sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg== - -"@rollup/rollup-linux-s390x-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz#c085995b10143c16747a67f1a5487512b2ff04b2" - integrity sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg== - -"@rollup/rollup-linux-x64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz#9563a5419dd2604841bad31a39ccfdd2891690fb" - integrity sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg== - -"@rollup/rollup-linux-x64-musl@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz#691bb06e6269a8959c13476b0cd2aa7458facb31" - integrity sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w== - -"@rollup/rollup-openbsd-x64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz#223e71224746a59ce6d955bbc403577bb5a8be9d" - integrity sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg== - -"@rollup/rollup-openharmony-arm64@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz#0817e5d8ecbfeb8b7939bf58f8ce3c9dd67fce77" - integrity sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw== - -"@rollup/rollup-win32-arm64-msvc@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz#de56d8f2013c84570ef5fb917aae034abda93e4a" - integrity sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g== - -"@rollup/rollup-win32-ia32-msvc@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz#659aff5244312475aeea2c9479a6c7d397b517bf" - integrity sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA== - -"@rollup/rollup-win32-x64-gnu@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz#2cb09549cbb66c1b979f9238db6dd454cac14a88" - integrity sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg== - -"@rollup/rollup-win32-x64-msvc@4.55.1": - version "4.55.1" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz#f79437939020b83057faf07e98365b1fa51c458b" - integrity sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw== +"@rollup/rollup-android-arm-eabi@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz#067cfcd81f1c1bfd92aefe3ad5ef1523549d5052" + integrity sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw== + +"@rollup/rollup-android-arm64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.56.0.tgz#85e39a44034d7d4e4fee2a1616f0bddb85a80517" + integrity sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q== + +"@rollup/rollup-darwin-arm64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.56.0.tgz#17d92fe98f2cc277b91101eb1528b7c0b6c00c54" + integrity sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w== + +"@rollup/rollup-darwin-x64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.56.0.tgz#89ae6c66b1451609bd1f297da9384463f628437d" + integrity sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g== + +"@rollup/rollup-freebsd-arm64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.56.0.tgz#cdbdb9947b26e76c188a31238c10639347413628" + integrity sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ== + +"@rollup/rollup-freebsd-x64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.56.0.tgz#9b1458d07b6e040be16ee36d308a2c9520f7f7cc" + integrity sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg== + +"@rollup/rollup-linux-arm-gnueabihf@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.56.0.tgz#1d50ded7c965d5f125f5832c971ad5b287befef7" + integrity sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A== + +"@rollup/rollup-linux-arm-musleabihf@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.56.0.tgz#53597e319b7e65990d3bc2a5048097384814c179" + integrity sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw== + +"@rollup/rollup-linux-arm64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.56.0.tgz#597002909dec198ca4bdccb25f043d32db3d6283" + integrity sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ== + +"@rollup/rollup-linux-arm64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.56.0.tgz#286f0e0f799545ce288bdc5a7c777261fcba3d54" + integrity sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA== + +"@rollup/rollup-linux-loong64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.56.0.tgz#1fab07fa1a4f8d3697735b996517f1bae0ba101b" + integrity sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg== + +"@rollup/rollup-linux-loong64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.56.0.tgz#efc2cb143d6c067f95205482afb177f78ed9ea3d" + integrity sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA== + +"@rollup/rollup-linux-ppc64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.56.0.tgz#e8de8bd3463f96b92b7dfb7f151fd80ffe8a937c" + integrity sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw== + +"@rollup/rollup-linux-ppc64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.56.0.tgz#8c508fe28a239da83b3a9da75bcf093186e064b4" + integrity sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg== + +"@rollup/rollup-linux-riscv64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.56.0.tgz#ff6d51976e0830732880770a9e18553136b8d92b" + integrity sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew== + +"@rollup/rollup-linux-riscv64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.56.0.tgz#325fb35eefc7e81d75478318f0deee1e4a111493" + integrity sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ== + +"@rollup/rollup-linux-s390x-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.56.0.tgz#37410fabb5d3ba4ad34abcfbe9ba9b6288413f30" + integrity sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ== + +"@rollup/rollup-linux-x64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz#8ef907a53b2042068fc03fcc6a641e2b02276eca" + integrity sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw== + +"@rollup/rollup-linux-x64-musl@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz#61b9ba09ea219e0174b3f35a6ad2afc94bdd5662" + integrity sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA== + +"@rollup/rollup-openbsd-x64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.56.0.tgz#fc4e54133134c1787d0b016ffdd5aeb22a5effd3" + integrity sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA== + +"@rollup/rollup-openharmony-arm64@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.56.0.tgz#959ae225b1eeea0cc5b7c9f88e4834330fb6cd09" + integrity sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ== + +"@rollup/rollup-win32-arm64-msvc@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.56.0.tgz#842acd38869fa1cbdbc240c76c67a86f93444c27" + integrity sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing== + +"@rollup/rollup-win32-ia32-msvc@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.56.0.tgz#7ab654def4042df44cb29f8ed9d5044e850c66d5" + integrity sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg== + +"@rollup/rollup-win32-x64-gnu@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.56.0.tgz#7426cdec1b01d2382ffd5cda83cbdd1c8efb3ca6" + integrity sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ== + +"@rollup/rollup-win32-x64-msvc@4.56.0": + version "4.56.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.56.0.tgz#9eec0212732a432c71bde0350bc40b673d15b2db" + integrity sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g== "@rtsao/scc@^1.1.0": version "1.1.0" @@ -1723,9 +1723,9 @@ integrity sha512-YBgwS9qG4rs1ZY/ZrhQtjOH8BG9Qa2wf2AsxT/SnZ4HZJ1DcCEqkoiHH0yH6CYvdDit31X5HokOqQrRSsZEwGA== "@tanstack/react-router@^1.120.5": - version "1.154.8" - resolved "https://registry.yarnpkg.com/@tanstack/react-router/-/react-router-1.154.8.tgz#48c28ae298d35fae412c24e2d7a9ab1a4f6fe878" - integrity sha512-FD7VjAaO1kjg2v8jL2jXMgSLga2fD6kBkBN+50voeJ5jHLasHcZpRcPZl7hkdy0gXiR/d6JepgnoeHQYiC2vRA== + version "1.154.10" + resolved "https://registry.yarnpkg.com/@tanstack/react-router/-/react-router-1.154.10.tgz#4ce9ceda0a02168347755fcc65f0f7a42a7dc5b6" + integrity sha512-vDYLiNdR9ASDjh6/v7P4aKbBRgnAfi97sGNNFzcE6sdHOsNhVfVaI+ZFc6OqLu1f0NllXc3c06MtU3XBQ6lRGw== dependencies: "@tanstack/history" "1.154.7" "@tanstack/react-store" "^0.8.0" @@ -1787,9 +1787,9 @@ redent "^3.0.0" "@testing-library/react@^16.3.1": - version "16.3.1" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.3.1.tgz#60a9f1f6a930399d9e41b506a8bf68dbf4831fe8" - integrity sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw== + version "16.3.2" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-16.3.2.tgz#672883b7acb8e775fc0492d9e9d25e06e89786d0" + integrity sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g== dependencies: "@babel/runtime" "^7.12.5" @@ -1827,9 +1827,9 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/node@*": - version "25.0.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.3.tgz#79b9ac8318f373fbfaaf6e2784893efa9701f269" - integrity sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA== + version "25.0.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.10.tgz#4864459c3c9459376b8b75fd051315071c8213e7" + integrity sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg== dependencies: undici-types "~7.16.0" @@ -1840,194 +1840,186 @@ dependencies: "@types/react" "*" -"@types/react@*": +"@types/react@*", "@types/react@^19.2.7": version "19.2.9" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.9.tgz#84ec7669742bb3e7e2e8d6a5258d95ead7764200" integrity sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA== dependencies: csstype "^3.2.2" -"@types/react@^19.2.7": - version "19.2.7" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.7.tgz#84e62c0f23e8e4e5ac2cadcea1ffeacccae7f62f" - integrity sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg== - dependencies: - csstype "^3.2.2" - "@types/resolve@1.20.2": version "1.20.2" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.20.2.tgz#97d26e00cd4a0423b4af620abecf3e6f442b7975" integrity sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q== -"@typescript-eslint/eslint-plugin@8.52.0", "@typescript-eslint/eslint-plugin@^8.50.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz#9a9f1d2ee974ed77a8b1bda94e77123f697ee8b4" - integrity sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q== +"@typescript-eslint/eslint-plugin@8.53.1", "@typescript-eslint/eslint-plugin@^8.50.0": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz#f6640f6f8749b71d9ab457263939e8932a3c6b46" + integrity sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.52.0" - "@typescript-eslint/type-utils" "8.52.0" - "@typescript-eslint/utils" "8.52.0" - "@typescript-eslint/visitor-keys" "8.52.0" + "@typescript-eslint/scope-manager" "8.53.1" + "@typescript-eslint/type-utils" "8.53.1" + "@typescript-eslint/utils" "8.53.1" + "@typescript-eslint/visitor-keys" "8.53.1" ignore "^7.0.5" natural-compare "^1.4.0" ts-api-utils "^2.4.0" -"@typescript-eslint/parser@8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.52.0.tgz#9fae9f5f13ebb1c8f31a50c34381bfd6bf96a05f" - integrity sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg== +"@typescript-eslint/parser@8.53.1": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.53.1.tgz#58d4a70cc2daee2becf7d4521d65ea1782d6ec68" + integrity sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg== dependencies: - "@typescript-eslint/scope-manager" "8.52.0" - "@typescript-eslint/types" "8.52.0" - "@typescript-eslint/typescript-estree" "8.52.0" - "@typescript-eslint/visitor-keys" "8.52.0" + "@typescript-eslint/scope-manager" "8.53.1" + "@typescript-eslint/types" "8.53.1" + "@typescript-eslint/typescript-estree" "8.53.1" + "@typescript-eslint/visitor-keys" "8.53.1" debug "^4.4.3" -"@typescript-eslint/project-service@8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.52.0.tgz#5fb4c16af4eda6d74c70cbc62f5d3f77b96e4cbe" - integrity sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw== +"@typescript-eslint/project-service@8.53.1": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.53.1.tgz#4e47856a0b14a1ceb28b0294b4badef3be1e9734" + integrity sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.52.0" - "@typescript-eslint/types" "^8.52.0" + "@typescript-eslint/tsconfig-utils" "^8.53.1" + "@typescript-eslint/types" "^8.53.1" debug "^4.4.3" -"@typescript-eslint/scope-manager@8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.52.0.tgz#9884ff690fad30380ccabfb08af1ac200af6b4e5" - integrity sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA== +"@typescript-eslint/scope-manager@8.53.1": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz#6c4b8c82cd45ae3b365afc2373636e166743a8fa" + integrity sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ== dependencies: - "@typescript-eslint/types" "8.52.0" - "@typescript-eslint/visitor-keys" "8.52.0" + "@typescript-eslint/types" "8.53.1" + "@typescript-eslint/visitor-keys" "8.53.1" -"@typescript-eslint/tsconfig-utils@8.52.0", "@typescript-eslint/tsconfig-utils@^8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.52.0.tgz#0296751c22ed05c83787a6eaec65ae221bd8b8ed" - integrity sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg== +"@typescript-eslint/tsconfig-utils@8.53.1", "@typescript-eslint/tsconfig-utils@^8.53.1": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz#efe80b8d019cd49e5a1cf46c2eb0cd2733076424" + integrity sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA== -"@typescript-eslint/type-utils@8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.52.0.tgz#6e554113f8a074cf9b2faa818d2ebfccb867d6c5" - integrity sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ== +"@typescript-eslint/type-utils@8.53.1": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz#95de2651a96d580bf5c6c6089ddd694284d558ad" + integrity sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w== dependencies: - "@typescript-eslint/types" "8.52.0" - "@typescript-eslint/typescript-estree" "8.52.0" - "@typescript-eslint/utils" "8.52.0" + "@typescript-eslint/types" "8.53.1" + "@typescript-eslint/typescript-estree" "8.53.1" + "@typescript-eslint/utils" "8.53.1" debug "^4.4.3" ts-api-utils "^2.4.0" -"@typescript-eslint/types@8.52.0", "@typescript-eslint/types@^8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.52.0.tgz#1eb0a16b324824bc23b89d109a267c38c9213c4a" - integrity sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg== +"@typescript-eslint/types@8.53.1", "@typescript-eslint/types@^8.53.1": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.53.1.tgz#101f203f0807a63216cceceedb815fabe21d5793" + integrity sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A== -"@typescript-eslint/typescript-estree@8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.52.0.tgz#2ad7721c671be2127951286cb7f44c4ce55b0591" - integrity sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ== +"@typescript-eslint/typescript-estree@8.53.1": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz#b6dce2303c9e27e95b8dcd8c325868fff53e488f" + integrity sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg== dependencies: - "@typescript-eslint/project-service" "8.52.0" - "@typescript-eslint/tsconfig-utils" "8.52.0" - "@typescript-eslint/types" "8.52.0" - "@typescript-eslint/visitor-keys" "8.52.0" + "@typescript-eslint/project-service" "8.53.1" + "@typescript-eslint/tsconfig-utils" "8.53.1" + "@typescript-eslint/types" "8.53.1" + "@typescript-eslint/visitor-keys" "8.53.1" debug "^4.4.3" minimatch "^9.0.5" semver "^7.7.3" tinyglobby "^0.2.15" ts-api-utils "^2.4.0" -"@typescript-eslint/utils@8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.52.0.tgz#b249be8264899b80d996fa353b4b84da4662f962" - integrity sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ== +"@typescript-eslint/utils@8.53.1": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.53.1.tgz#81fe6c343de288701b774f4d078382f567e6edaa" + integrity sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.52.0" - "@typescript-eslint/types" "8.52.0" - "@typescript-eslint/typescript-estree" "8.52.0" + "@typescript-eslint/scope-manager" "8.53.1" + "@typescript-eslint/types" "8.53.1" + "@typescript-eslint/typescript-estree" "8.53.1" -"@typescript-eslint/visitor-keys@8.52.0": - version "8.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.52.0.tgz#50361c48a6302676230fe498f80f6decce4bf673" - integrity sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ== +"@typescript-eslint/visitor-keys@8.53.1": + version "8.53.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz#405f04959be22b9be364939af8ac19c3649b6eb7" + integrity sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg== dependencies: - "@typescript-eslint/types" "8.52.0" + "@typescript-eslint/types" "8.53.1" eslint-visitor-keys "^4.2.1" "@vitest/coverage-v8@^4.0.16": - version "4.0.16" - resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-4.0.16.tgz#375fa0c1cbb357443628a1fcbf694154dfee9561" - integrity sha512-2rNdjEIsPRzsdu6/9Eq0AYAzYdpP6Bx9cje9tL3FE5XzXRQF1fNU9pe/1yE8fCrS0HD+fBtt6gLPh6LI57tX7A== + version "4.0.18" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz#b9c4db7479acd51d5f0ced91b2853c29c3d0cda7" + integrity sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg== dependencies: "@bcoe/v8-coverage" "^1.0.2" - "@vitest/utils" "4.0.16" - ast-v8-to-istanbul "^0.3.8" + "@vitest/utils" "4.0.18" + ast-v8-to-istanbul "^0.3.10" istanbul-lib-coverage "^3.2.2" istanbul-lib-report "^3.0.1" - istanbul-lib-source-maps "^5.0.6" istanbul-reports "^3.2.0" magicast "^0.5.1" obug "^2.1.1" std-env "^3.10.0" tinyrainbow "^3.0.3" -"@vitest/expect@4.0.16": - version "4.0.16" - resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.0.16.tgz#3cb324c35f59ae72a9e1fb3b4f7b92e596628151" - integrity sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA== +"@vitest/expect@4.0.18": + version "4.0.18" + resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-4.0.18.tgz#361510d99fbf20eb814222e4afcb8539d79dc94d" + integrity sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ== dependencies: "@standard-schema/spec" "^1.0.0" "@types/chai" "^5.2.2" - "@vitest/spy" "4.0.16" - "@vitest/utils" "4.0.16" + "@vitest/spy" "4.0.18" + "@vitest/utils" "4.0.18" chai "^6.2.1" tinyrainbow "^3.0.3" -"@vitest/mocker@4.0.16": - version "4.0.16" - resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-4.0.16.tgz#0351f17f5843b226f237f86cad7fc6dd7fd5b36d" - integrity sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg== +"@vitest/mocker@4.0.18": + version "4.0.18" + resolved "https://registry.yarnpkg.com/@vitest/mocker/-/mocker-4.0.18.tgz#b9735da114ef65ea95652c5bdf13159c6fab4865" + integrity sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ== dependencies: - "@vitest/spy" "4.0.16" + "@vitest/spy" "4.0.18" estree-walker "^3.0.3" magic-string "^0.30.21" -"@vitest/pretty-format@4.0.16": - version "4.0.16" - resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.0.16.tgz#91893e0337dbdd6f80a89bcc9710c0d03650f090" - integrity sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA== +"@vitest/pretty-format@4.0.18": + version "4.0.18" + resolved "https://registry.yarnpkg.com/@vitest/pretty-format/-/pretty-format-4.0.18.tgz#fbccd4d910774072ec15463553edb8ca5ce53218" + integrity sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw== dependencies: tinyrainbow "^3.0.3" -"@vitest/runner@4.0.16": - version "4.0.16" - resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.0.16.tgz#a9eb6786545727436e53eb51308abd6af8154323" - integrity sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q== +"@vitest/runner@4.0.18": + version "4.0.18" + resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-4.0.18.tgz#c2c0a3ed226ec85e9312f9cc8c43c5b3a893a8b1" + integrity sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw== dependencies: - "@vitest/utils" "4.0.16" + "@vitest/utils" "4.0.18" pathe "^2.0.3" -"@vitest/snapshot@4.0.16": - version "4.0.16" - resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-4.0.16.tgz#6a7e41bdd3a60206c167720042c836c30dc50f3a" - integrity sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA== +"@vitest/snapshot@4.0.18": + version "4.0.18" + resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-4.0.18.tgz#bcb40fd6d742679c2ac927ba295b66af1c6c34c5" + integrity sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA== dependencies: - "@vitest/pretty-format" "4.0.16" + "@vitest/pretty-format" "4.0.18" magic-string "^0.30.21" pathe "^2.0.3" -"@vitest/spy@4.0.16": - version "4.0.16" - resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.0.16.tgz#3ac2e63e3e0cf304f1a84ec086d8e36cd185fbbd" - integrity sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw== +"@vitest/spy@4.0.18": + version "4.0.18" + resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-4.0.18.tgz#ba0f20503fb6d08baf3309d690b3efabdfa88762" + integrity sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw== -"@vitest/utils@4.0.16": - version "4.0.16" - resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.0.16.tgz#f789a4ef5c5b2e8eef90a4c3304678dbc6c92599" - integrity sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA== +"@vitest/utils@4.0.18": + version "4.0.18" + resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-4.0.18.tgz#9636b16d86a4152ec68a8d6859cff702896433d4" + integrity sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA== dependencies: - "@vitest/pretty-format" "4.0.16" + "@vitest/pretty-format" "4.0.18" tinyrainbow "^3.0.3" acorn-jsx@^5.3.2: @@ -2204,7 +2196,7 @@ assertion-error@^2.0.1: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7" integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== -ast-v8-to-istanbul@^0.3.8: +ast-v8-to-istanbul@^0.3.10: version "0.3.10" resolved "https://registry.yarnpkg.com/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.10.tgz#ceff0094c8c64b9e04393c2377fd61857429ec04" integrity sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ== @@ -2280,9 +2272,9 @@ balanced-match@^1.0.0: integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== baseline-browser-mapping@^2.8.3, baseline-browser-mapping@^2.9.0: - version "2.9.12" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.12.tgz#fbed5f37edf24b708e6e0b1fb26c70982a577dfc" - integrity sha512-Mij6Lij93pTAIsSYy5cyBQ975Qh9uLEc5rwGTpomiZeXZL9yIS6uORJakb3ScHgfs0serMMfIbXzokPMuEiRyw== + version "2.9.17" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz#9d6019766cd7eba738cb5f32c84b9f937cc87780" + integrity sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ== bcrypt-pbkdf@^1.0.0: version "1.0.2" @@ -2318,7 +2310,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -browserslist@^4.24.0, browserslist@^4.28.0: +browserslist@^4.24.0, browserslist@^4.28.1: version "4.28.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== @@ -2366,9 +2358,9 @@ callsites@^3.0.0: integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001759: - version "1.0.30001762" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz#e4dbfeda63d33258cdde93e53af2023a13ba27d4" - integrity sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw== + version "1.0.30001765" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz#4a78d8a797fd4124ebaab2043df942eb091648ee" + integrity sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ== caseless@~0.12.0: version "0.12.0" @@ -2443,11 +2435,11 @@ cookie@^1.0.1: integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ== core-js-compat@^3.43.0: - version "3.47.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.47.0.tgz#698224bbdbb6f2e3f39decdda4147b161e3772a3" - integrity sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ== + version "3.48.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.48.0.tgz#7efbe1fc1cbad44008190462217cc5558adaeaa6" + integrity sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q== dependencies: - browserslist "^4.28.0" + browserslist "^4.28.1" core-util-is@1.0.2: version "1.0.2" @@ -2518,12 +2510,12 @@ dashdash@^1.12.0: assert-plus "^1.0.0" data-urls@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-6.0.0.tgz#95a7943c8ac14c1d563b771f2621cc50e8ec7744" - integrity sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA== + version "6.0.1" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-6.0.1.tgz#b448c8637997abe34978c9bfdb3d0a7778540184" + integrity sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ== dependencies: - whatwg-mimetype "^4.0.0" - whatwg-url "^15.0.0" + whatwg-mimetype "^5.0.0" + whatwg-url "^15.1.0" data-view-buffer@^1.0.2: version "1.0.2" @@ -2552,7 +2544,7 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.1, debug@^4.4.3: +debug@4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.1, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -2654,9 +2646,9 @@ ecc-jsbn@~0.1.1: safer-buffer "^2.1.0" electron-to-chromium@^1.5.263: - version "1.5.267" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" - integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== + version "1.5.277" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.277.tgz#7164191a07bf32a7e646e68334f402dd60629821" + integrity sha512-wKXFZw4erWmmOz5N/grBoJ2XrNJGDFMu2+W5ACHza5rHtvsqrK4gb6rnLC7XxKB9WlJ+RmyQatuEXmtm86xbnw== entities@^6.0.0: version "6.0.1" @@ -2887,12 +2879,12 @@ eslint-plugin-import@^2.32.0: tsconfig-paths "^3.15.0" eslint-plugin-prettier@^5.0.1: - version "5.5.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz#9d61c4ea11de5af704d4edf108c82ccfa7f2e61c" - integrity sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg== + version "5.5.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz#9eae11593faa108859c26f9a9c367d619a0769c0" + integrity sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw== dependencies: - prettier-linter-helpers "^1.0.0" - synckit "^0.11.7" + prettier-linter-helpers "^1.0.1" + synckit "^0.11.12" eslint-plugin-react-hooks@^7.0.1: version "7.0.1" @@ -3657,15 +3649,6 @@ istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: make-dir "^4.0.0" supports-color "^7.1.0" -istanbul-lib-source-maps@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" - integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== - dependencies: - "@jridgewell/trace-mapping" "^0.3.23" - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - istanbul-reports@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" @@ -4002,25 +3985,25 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@^16.1.0: - version "16.1.1" - resolved "https://registry.yarnpkg.com/next/-/next-16.1.1.tgz#4cc3477daa0fe22678532ff4778baee95842d866" - integrity sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w== + version "16.1.4" + resolved "https://registry.yarnpkg.com/next/-/next-16.1.4.tgz#d024bace2d52a2bea1dec33149b8bbd0852632c5" + integrity sha512-gKSecROqisnV7Buen5BfjmXAm7Xlpx9o2ueVQRo5DxQcjC8d330dOM1xiGWc2k3Dcnz0In3VybyRPOsudwgiqQ== dependencies: - "@next/env" "16.1.1" + "@next/env" "16.1.4" "@swc/helpers" "0.5.15" baseline-browser-mapping "^2.8.3" caniuse-lite "^1.0.30001579" postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "16.1.1" - "@next/swc-darwin-x64" "16.1.1" - "@next/swc-linux-arm64-gnu" "16.1.1" - "@next/swc-linux-arm64-musl" "16.1.1" - "@next/swc-linux-x64-gnu" "16.1.1" - "@next/swc-linux-x64-musl" "16.1.1" - "@next/swc-win32-arm64-msvc" "16.1.1" - "@next/swc-win32-x64-msvc" "16.1.1" + "@next/swc-darwin-arm64" "16.1.4" + "@next/swc-darwin-x64" "16.1.4" + "@next/swc-linux-arm64-gnu" "16.1.4" + "@next/swc-linux-arm64-musl" "16.1.4" + "@next/swc-linux-x64-gnu" "16.1.4" + "@next/swc-linux-x64-musl" "16.1.4" + "@next/swc-win32-arm64-msvc" "16.1.4" + "@next/swc-win32-x64-msvc" "16.1.4" sharp "^0.34.4" node-releases@^2.0.27: @@ -4229,7 +4212,7 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier-linter-helpers@^1.0.0: +prettier-linter-helpers@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz#6a31f88a4bad6c7adda253de12ba4edaea80ebcd" integrity sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg== @@ -4237,9 +4220,9 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier@^3.0.3: - version "3.7.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f" - integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA== + version "3.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== pretty-format@^27.0.2: version "27.5.1" @@ -4485,37 +4468,37 @@ rollup-plugin-terser@^7.0.2: terser "^5.0.0" rollup@^4.43.0, rollup@^4.6.0: - version "4.55.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.55.1.tgz#4ec182828be440648e7ee6520dc35e9f20e05144" - integrity sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A== + version "4.56.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.56.0.tgz#65959d13cfbd7e48b8868c05165b1738f0143862" + integrity sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg== dependencies: "@types/estree" "1.0.8" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.55.1" - "@rollup/rollup-android-arm64" "4.55.1" - "@rollup/rollup-darwin-arm64" "4.55.1" - "@rollup/rollup-darwin-x64" "4.55.1" - "@rollup/rollup-freebsd-arm64" "4.55.1" - "@rollup/rollup-freebsd-x64" "4.55.1" - "@rollup/rollup-linux-arm-gnueabihf" "4.55.1" - "@rollup/rollup-linux-arm-musleabihf" "4.55.1" - "@rollup/rollup-linux-arm64-gnu" "4.55.1" - "@rollup/rollup-linux-arm64-musl" "4.55.1" - "@rollup/rollup-linux-loong64-gnu" "4.55.1" - "@rollup/rollup-linux-loong64-musl" "4.55.1" - "@rollup/rollup-linux-ppc64-gnu" "4.55.1" - "@rollup/rollup-linux-ppc64-musl" "4.55.1" - "@rollup/rollup-linux-riscv64-gnu" "4.55.1" - "@rollup/rollup-linux-riscv64-musl" "4.55.1" - "@rollup/rollup-linux-s390x-gnu" "4.55.1" - "@rollup/rollup-linux-x64-gnu" "4.55.1" - "@rollup/rollup-linux-x64-musl" "4.55.1" - "@rollup/rollup-openbsd-x64" "4.55.1" - "@rollup/rollup-openharmony-arm64" "4.55.1" - "@rollup/rollup-win32-arm64-msvc" "4.55.1" - "@rollup/rollup-win32-ia32-msvc" "4.55.1" - "@rollup/rollup-win32-x64-gnu" "4.55.1" - "@rollup/rollup-win32-x64-msvc" "4.55.1" + "@rollup/rollup-android-arm-eabi" "4.56.0" + "@rollup/rollup-android-arm64" "4.56.0" + "@rollup/rollup-darwin-arm64" "4.56.0" + "@rollup/rollup-darwin-x64" "4.56.0" + "@rollup/rollup-freebsd-arm64" "4.56.0" + "@rollup/rollup-freebsd-x64" "4.56.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.56.0" + "@rollup/rollup-linux-arm-musleabihf" "4.56.0" + "@rollup/rollup-linux-arm64-gnu" "4.56.0" + "@rollup/rollup-linux-arm64-musl" "4.56.0" + "@rollup/rollup-linux-loong64-gnu" "4.56.0" + "@rollup/rollup-linux-loong64-musl" "4.56.0" + "@rollup/rollup-linux-ppc64-gnu" "4.56.0" + "@rollup/rollup-linux-ppc64-musl" "4.56.0" + "@rollup/rollup-linux-riscv64-gnu" "4.56.0" + "@rollup/rollup-linux-riscv64-musl" "4.56.0" + "@rollup/rollup-linux-s390x-gnu" "4.56.0" + "@rollup/rollup-linux-x64-gnu" "4.56.0" + "@rollup/rollup-linux-x64-musl" "4.56.0" + "@rollup/rollup-openbsd-x64" "4.56.0" + "@rollup/rollup-openharmony-arm64" "4.56.0" + "@rollup/rollup-win32-arm64-msvc" "4.56.0" + "@rollup/rollup-win32-ia32-msvc" "4.56.0" + "@rollup/rollup-win32-x64-gnu" "4.56.0" + "@rollup/rollup-win32-x64-msvc" "4.56.0" fsevents "~2.3.2" safe-array-concat@^1.1.3: @@ -4735,9 +4718,9 @@ siginfo@^2.0.0: integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== snyk@^1.437.3: - version "1.1301.2" - resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.1301.2.tgz#9e64882778cbd191393e4f85dc221c508d5f3a0b" - integrity sha512-FUTV/2WStY0GyvJROvxKPa2A3FzgVNIUB8rR2LMiR1to9onrnyO233HEIOS1HU+dJTXHFSh8z3fMze2TqPt58w== + version "1.1302.1" + resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.1302.1.tgz#bbd8522699291ff605f6450748ca9174e2f5b509" + integrity sha512-RT85Pz4N36xma7Mcob0Jno5TXu22VbVoT+7mWHk2TVkBHqsMZ8sKW55X+r+LMaT8GwlC5+cYjzw2iuv1VcPZOg== dependencies: "@sentry/node" "^7.36.0" global-agent "^3.0.0" @@ -4903,17 +4886,17 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -synckit@^0.11.7: - version "0.11.11" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.11.tgz#c0b619cf258a97faa209155d9cd1699b5c998cb0" - integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw== +synckit@^0.11.12: + version "0.11.12" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.12.tgz#abe74124264fbc00a48011b0d98bdc1cffb64a7b" + integrity sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ== dependencies: "@pkgr/core" "^0.2.9" terser@^5.0.0: - version "5.44.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.44.1.tgz#e391e92175c299b8c284ad6ded609e37303b0a9c" - integrity sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw== + version "5.46.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.0.tgz#1b81e560d584bbdd74a8ede87b4d9477b0ff9695" + integrity sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.15.0" @@ -5077,14 +5060,14 @@ typed-array-length@^1.0.7: reflect.getprototypeof "^1.0.6" typescript-eslint@^8.50.0: - version "8.52.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.52.0.tgz#b8c156b6f2b4dee202a85712ff6a37f614476413" - integrity sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA== + version "8.53.1" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.53.1.tgz#e8d2888083af4638d2952b938d69458f54865921" + integrity sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg== dependencies: - "@typescript-eslint/eslint-plugin" "8.52.0" - "@typescript-eslint/parser" "8.52.0" - "@typescript-eslint/typescript-estree" "8.52.0" - "@typescript-eslint/utils" "8.52.0" + "@typescript-eslint/eslint-plugin" "8.53.1" + "@typescript-eslint/parser" "8.53.1" + "@typescript-eslint/typescript-estree" "8.53.1" + "@typescript-eslint/utils" "8.53.1" typescript@*: version "5.9.3" @@ -5178,17 +5161,17 @@ verror@1.10.0: fsevents "~2.3.3" vitest@^4.0.16: - version "4.0.16" - resolved "https://registry.yarnpkg.com/vitest/-/vitest-4.0.16.tgz#7ceaecd4612fa6351923e842a0723c48cdfb6719" - integrity sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q== - dependencies: - "@vitest/expect" "4.0.16" - "@vitest/mocker" "4.0.16" - "@vitest/pretty-format" "4.0.16" - "@vitest/runner" "4.0.16" - "@vitest/snapshot" "4.0.16" - "@vitest/spy" "4.0.16" - "@vitest/utils" "4.0.16" + version "4.0.18" + resolved "https://registry.yarnpkg.com/vitest/-/vitest-4.0.18.tgz#56f966353eca0b50f4df7540cd4350ca6d454a05" + integrity sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ== + dependencies: + "@vitest/expect" "4.0.18" + "@vitest/mocker" "4.0.18" + "@vitest/pretty-format" "4.0.18" + "@vitest/runner" "4.0.18" + "@vitest/snapshot" "4.0.18" + "@vitest/spy" "4.0.18" + "@vitest/utils" "4.0.18" es-module-lexer "^1.7.0" expect-type "^1.2.2" magic-string "^0.30.21" @@ -5227,7 +5210,12 @@ whatwg-mimetype@^4.0.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== -whatwg-url@^15.0.0, whatwg-url@^15.1.0: +whatwg-mimetype@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz#d8232895dbd527ceaee74efd4162008fb8a8cf48" + integrity sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw== + +whatwg-url@^15.1.0: version "15.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-15.1.0.tgz#5c433439b9a5789eeb3806bbd0da89a8bd40b8d7" integrity sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g== @@ -5276,9 +5264,9 @@ which-collection@^1.0.2: is-weakset "^2.0.3" which-typed-array@^1.1.16, which-typed-array@^1.1.19: - version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" - integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + version "1.1.20" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.20.tgz#3fdb7adfafe0ea69157b1509f3a1cd892bd1d122" + integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.8" @@ -5339,6 +5327,6 @@ yocto-queue@^0.1.0: integrity sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ== "zod@^3.25.0 || ^4.0.0": - version "4.3.5" - resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.5.tgz#aeb269a6f9fc259b1212c348c7c5432aaa474d2a" - integrity sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g== + version "4.3.6" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a" + integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg== From 3263d3e373d6ae9082613ddb47d8eade80cf0676 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 21:31:21 +0000 Subject: [PATCH 28/54] Link docs and examples to local react-fathom package Update package.json files to use file: references instead of npm versions. This allows the docs site and examples to use the local package directly, making them useful as test cases during development without requiring a publish step. - docs: file:.. - examples/*: file:../.. --- docs/package.json | 2 +- docs/yarn.lock | 3024 ++++++++++++++++++++++++++++ examples/next-app/package.json | 2 +- examples/next-pages/package.json | 2 +- examples/react-native/package.json | 2 +- examples/react/package.json | 2 +- 6 files changed, 3029 insertions(+), 5 deletions(-) create mode 100644 docs/yarn.lock diff --git a/docs/package.json b/docs/package.json index 7a81e48..ebaf28d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -19,7 +19,7 @@ "pagefind": "^1.3.0", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-fathom": "^0.2.0", + "react-fathom": "file:..", "reading-time": "^1.5.0", "rehype-pretty-code": "^0.14.0", "shiki": "^1.24.0" diff --git a/docs/yarn.lock b/docs/yarn.lock new file mode 100644 index 0000000..606dec7 --- /dev/null +++ b/docs/yarn.lock @@ -0,0 +1,3024 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ark-ui/react@^5.29.1": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@ark-ui/react/-/react-5.30.0.tgz#57f82e5f91e7f3af6adc1b470b8deda322d135af" + integrity sha512-MIWgj6uWTuG42DGaXUQARObvuQJymm+/1wsdGEDrIHtSv0a2PFQO4svwMvMFwfFbL1jVkJzzBU6JDAH0xKbvyw== + dependencies: + "@internationalized/date" "3.10.0" + "@zag-js/accordion" "1.31.1" + "@zag-js/anatomy" "1.31.1" + "@zag-js/angle-slider" "1.31.1" + "@zag-js/async-list" "1.31.1" + "@zag-js/auto-resize" "1.31.1" + "@zag-js/avatar" "1.31.1" + "@zag-js/bottom-sheet" "1.31.1" + "@zag-js/carousel" "1.31.1" + "@zag-js/checkbox" "1.31.1" + "@zag-js/clipboard" "1.31.1" + "@zag-js/collapsible" "1.31.1" + "@zag-js/collection" "1.31.1" + "@zag-js/color-picker" "1.31.1" + "@zag-js/color-utils" "1.31.1" + "@zag-js/combobox" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/date-picker" "1.31.1" + "@zag-js/date-utils" "1.31.1" + "@zag-js/dialog" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/editable" "1.31.1" + "@zag-js/file-upload" "1.31.1" + "@zag-js/file-utils" "1.31.1" + "@zag-js/floating-panel" "1.31.1" + "@zag-js/focus-trap" "1.31.1" + "@zag-js/highlight-word" "1.31.1" + "@zag-js/hover-card" "1.31.1" + "@zag-js/i18n-utils" "1.31.1" + "@zag-js/image-cropper" "1.31.1" + "@zag-js/json-tree-utils" "1.31.1" + "@zag-js/listbox" "1.31.1" + "@zag-js/marquee" "1.31.1" + "@zag-js/menu" "1.31.1" + "@zag-js/navigation-menu" "1.31.1" + "@zag-js/number-input" "1.31.1" + "@zag-js/pagination" "1.31.1" + "@zag-js/password-input" "1.31.1" + "@zag-js/pin-input" "1.31.1" + "@zag-js/popover" "1.31.1" + "@zag-js/presence" "1.31.1" + "@zag-js/progress" "1.31.1" + "@zag-js/qr-code" "1.31.1" + "@zag-js/radio-group" "1.31.1" + "@zag-js/rating-group" "1.31.1" + "@zag-js/react" "1.31.1" + "@zag-js/scroll-area" "1.31.1" + "@zag-js/select" "1.31.1" + "@zag-js/signature-pad" "1.31.1" + "@zag-js/slider" "1.31.1" + "@zag-js/splitter" "1.31.1" + "@zag-js/steps" "1.31.1" + "@zag-js/switch" "1.31.1" + "@zag-js/tabs" "1.31.1" + "@zag-js/tags-input" "1.31.1" + "@zag-js/timer" "1.31.1" + "@zag-js/toast" "1.31.1" + "@zag-js/toggle" "1.31.1" + "@zag-js/toggle-group" "1.31.1" + "@zag-js/tooltip" "1.31.1" + "@zag-js/tour" "1.31.1" + "@zag-js/tree-view" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.28.6.tgz#72499312ec58b1e2245ba4a4f550c132be4982f7" + integrity sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.28.5" + js-tokens "^4.0.0" + picocolors "^1.1.1" + +"@babel/generator@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.6.tgz#48dcc65d98fcc8626a48f72b62e263d25fc3c3f1" + integrity sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw== + dependencies: + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + +"@babel/helper-globals@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" + integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== + +"@babel/helper-module-imports@^7.16.7": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== + dependencies: + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + +"@babel/parser@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.6.tgz#f01a8885b7fa1e56dd8a155130226cd698ef13fd" + integrity sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ== + dependencies: + "@babel/types" "^7.28.6" + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b" + integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== + +"@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" + +"@babel/traverse@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.6.tgz#871ddc79a80599a5030c53b1cc48cbe3a5583c2e" + integrity sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg== + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/generator" "^7.28.6" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.28.6" + "@babel/template" "^7.28.6" + "@babel/types" "^7.28.6" + debug "^4.3.1" + +"@babel/types@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.6.tgz#c3e9377f1b155005bcc4c46020e7e394e13089df" + integrity sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + +"@chakra-ui/react@^3.2.0": + version "3.31.0" + resolved "https://registry.yarnpkg.com/@chakra-ui/react/-/react-3.31.0.tgz#8328f6c57c22c2e8b5511927664a306f954b5ca3" + integrity sha512-puvrZOfnfMA+DckDcz0UxO20l7TVhwsdQ9ksCv4nIUB430yuWzon0yo9fM10lEr3hd7BhjZARpMCVw5u280clw== + dependencies: + "@ark-ui/react" "^5.29.1" + "@emotion/is-prop-valid" "^1.4.0" + "@emotion/serialize" "^1.3.3" + "@emotion/use-insertion-effect-with-fallbacks" "^1.2.0" + "@emotion/utils" "^1.4.2" + "@pandacss/is-valid-prop" "^1.4.2" + csstype "^3.2.3" + +"@emnapi/runtime@^1.7.0": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.8.1.tgz#550fa7e3c0d49c5fb175a116e8cd70614f9a22a5" + integrity sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg== + dependencies: + tslib "^2.4.0" + +"@emotion/babel-plugin@^11.13.5": + version "11.13.5" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz#eab8d65dbded74e0ecfd28dc218e75607c4e7bc0" + integrity sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.2" + "@emotion/memoize" "^0.9.0" + "@emotion/serialize" "^1.3.3" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + +"@emotion/cache@^11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.14.0.tgz#ee44b26986eeb93c8be82bb92f1f7a9b21b2ed76" + integrity sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA== + dependencies: + "@emotion/memoize" "^0.9.0" + "@emotion/sheet" "^1.4.0" + "@emotion/utils" "^1.4.2" + "@emotion/weak-memoize" "^0.4.0" + stylis "4.2.0" + +"@emotion/hash@^0.9.2": + version "0.9.2" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" + integrity sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g== + +"@emotion/is-prop-valid@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz#e9ad47adff0b5c94c72db3669ce46de33edf28c0" + integrity sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw== + dependencies: + "@emotion/memoize" "^0.9.0" + +"@emotion/memoize@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.9.0.tgz#745969d649977776b43fc7648c556aaa462b4102" + integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== + +"@emotion/react@^11.13.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.14.0.tgz#cfaae35ebc67dd9ef4ea2e9acc6cd29e157dd05d" + integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA== + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.13.5" + "@emotion/cache" "^11.14.0" + "@emotion/serialize" "^1.3.3" + "@emotion/use-insertion-effect-with-fallbacks" "^1.2.0" + "@emotion/utils" "^1.4.2" + "@emotion/weak-memoize" "^0.4.0" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.3.3.tgz#d291531005f17d704d0463a032fe679f376509e8" + integrity sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA== + dependencies: + "@emotion/hash" "^0.9.2" + "@emotion/memoize" "^0.9.0" + "@emotion/unitless" "^0.10.0" + "@emotion/utils" "^1.4.2" + csstype "^3.0.2" + +"@emotion/sheet@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.4.0.tgz#c9299c34d248bc26e82563735f78953d2efca83c" + integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== + +"@emotion/unitless@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" + integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== + +"@emotion/use-insertion-effect-with-fallbacks@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz#8a8cb77b590e09affb960f4ff1e9a89e532738bf" + integrity sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg== + +"@emotion/utils@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.4.2.tgz#6df6c45881fcb1c412d6688a311a98b7f59c1b52" + integrity sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA== + +"@emotion/weak-memoize@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz#5e13fac887f08c44f76b0ccaf3370eb00fec9bb6" + integrity sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg== + +"@floating-ui/core@^1.7.3": + version "1.7.3" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.7.3.tgz#462d722f001e23e46d86fd2bd0d21b7693ccb8b7" + integrity sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w== + dependencies: + "@floating-ui/utils" "^0.2.10" + +"@floating-ui/dom@1.7.4": + version "1.7.4" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.7.4.tgz#ee667549998745c9c3e3e84683b909c31d6c9a77" + integrity sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA== + dependencies: + "@floating-ui/core" "^1.7.3" + "@floating-ui/utils" "^0.2.10" + +"@floating-ui/utils@^0.2.10": + version "0.2.10" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.10.tgz#a2a1e3812d14525f725d011a73eceb41fef5bc1c" + integrity sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ== + +"@img/colour@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.0.0.tgz#d2fabb223455a793bf3bf9c70de3d28526aa8311" + integrity sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw== + +"@img/sharp-darwin-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz#6e0732dcade126b6670af7aa17060b926835ea86" + integrity sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w== + optionalDependencies: + "@img/sharp-libvips-darwin-arm64" "1.2.4" + +"@img/sharp-darwin-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz#19bc1dd6eba6d5a96283498b9c9f401180ee9c7b" + integrity sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw== + optionalDependencies: + "@img/sharp-libvips-darwin-x64" "1.2.4" + +"@img/sharp-libvips-darwin-arm64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz#2894c0cb87d42276c3889942e8e2db517a492c43" + integrity sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g== + +"@img/sharp-libvips-darwin-x64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz#e63681f4539a94af9cd17246ed8881734386f8cc" + integrity sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg== + +"@img/sharp-libvips-linux-arm64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz#b1b288b36864b3bce545ad91fa6dadcf1a4ad318" + integrity sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw== + +"@img/sharp-libvips-linux-arm@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz#b9260dd1ebe6f9e3bdbcbdcac9d2ac125f35852d" + integrity sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A== + +"@img/sharp-libvips-linux-ppc64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz#4b83ecf2a829057222b38848c7b022e7b4d07aa7" + integrity sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA== + +"@img/sharp-libvips-linux-riscv64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz#880b4678009e5a2080af192332b00b0aaf8a48de" + integrity sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA== + +"@img/sharp-libvips-linux-s390x@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz#74f343c8e10fad821b38f75ced30488939dc59ec" + integrity sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ== + +"@img/sharp-libvips-linux-x64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz#df4183e8bd8410f7d61b66859a35edeab0a531ce" + integrity sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw== + +"@img/sharp-libvips-linuxmusl-arm64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz#c8d6b48211df67137541007ee8d1b7b1f8ca8e06" + integrity sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw== + +"@img/sharp-libvips-linuxmusl-x64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz#be11c75bee5b080cbee31a153a8779448f919f75" + integrity sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg== + +"@img/sharp-linux-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz#7aa7764ef9c001f15e610546d42fce56911790cc" + integrity sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.2.4" + +"@img/sharp-linux-arm@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz#5fb0c3695dd12522d39c3ff7a6bc816461780a0d" + integrity sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.2.4" + +"@img/sharp-linux-ppc64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz#9c213a81520a20caf66978f3d4c07456ff2e0813" + integrity sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA== + optionalDependencies: + "@img/sharp-libvips-linux-ppc64" "1.2.4" + +"@img/sharp-linux-riscv64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz#cdd28182774eadbe04f62675a16aabbccb833f60" + integrity sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw== + optionalDependencies: + "@img/sharp-libvips-linux-riscv64" "1.2.4" + +"@img/sharp-linux-s390x@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz#93eac601b9f329bb27917e0e19098c722d630df7" + integrity sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg== + optionalDependencies: + "@img/sharp-libvips-linux-s390x" "1.2.4" + +"@img/sharp-linux-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz#55abc7cd754ffca5002b6c2b719abdfc846819a8" + integrity sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ== + optionalDependencies: + "@img/sharp-libvips-linux-x64" "1.2.4" + +"@img/sharp-linuxmusl-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz#d6515ee971bb62f73001a4829b9d865a11b77086" + integrity sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64" "1.2.4" + +"@img/sharp-linuxmusl-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz#d97978aec7c5212f999714f2f5b736457e12ee9f" + integrity sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.2.4" + +"@img/sharp-wasm32@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz#2f15803aa626f8c59dd7c9d0bbc766f1ab52cfa0" + integrity sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw== + dependencies: + "@emnapi/runtime" "^1.7.0" + +"@img/sharp-win32-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz#3706e9e3ac35fddfc1c87f94e849f1b75307ce0a" + integrity sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g== + +"@img/sharp-win32-ia32@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz#0b71166599b049e032f085fb9263e02f4e4788de" + integrity sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg== + +"@img/sharp-win32-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz#a81ffb00e69267cd0a1d626eaedb8a8430b2b2f8" + integrity sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw== + +"@internationalized/date@3.10.0": + version "3.10.0" + resolved "https://registry.yarnpkg.com/@internationalized/date/-/date-3.10.0.tgz#056db64a4facdf48c6937ad498a882a8151d640a" + integrity sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw== + dependencies: + "@swc/helpers" "^0.5.0" + +"@internationalized/number@3.6.5": + version "3.6.5" + resolved "https://registry.yarnpkg.com/@internationalized/number/-/number-3.6.5.tgz#1103f2832ca8d9dd3e4eecf95733d497791dbbbe" + integrity sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g== + dependencies: + "@swc/helpers" "^0.5.0" + +"@jridgewell/gen-mapping@^0.3.12": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@mdx-js/mdx@^3.0.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-3.1.1.tgz#c5ffd991a7536b149e17175eee57a1a2a511c6d1" + integrity sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ== + dependencies: + "@types/estree" "^1.0.0" + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdx" "^2.0.0" + acorn "^8.0.0" + collapse-white-space "^2.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + estree-util-scope "^1.0.0" + estree-walker "^3.0.0" + hast-util-to-jsx-runtime "^2.0.0" + markdown-extensions "^2.0.0" + recma-build-jsx "^1.0.0" + recma-jsx "^1.0.0" + recma-stringify "^1.0.0" + rehype-recma "^1.0.0" + remark-mdx "^3.0.0" + remark-parse "^11.0.0" + remark-rehype "^11.0.0" + source-map "^0.7.0" + unified "^11.0.0" + unist-util-position-from-estree "^2.0.0" + unist-util-stringify-position "^4.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +"@mdx-js/react@^3.0.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-3.1.1.tgz#24bda7fffceb2fe256f954482123cda1be5f5fef" + integrity sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw== + dependencies: + "@types/mdx" "^2.0.0" + +"@next/env@15.5.9": + version "15.5.9" + resolved "https://registry.yarnpkg.com/@next/env/-/env-15.5.9.tgz#53c2c34dc17cd87b61f70c6cc211e303123b2ab8" + integrity sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg== + +"@next/swc-darwin-arm64@15.5.7": + version "15.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz#f0c9ccfec2cd87cbd4b241ce4c779a7017aed958" + integrity sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw== + +"@next/swc-darwin-x64@15.5.7": + version "15.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz#18009e9fcffc5c0687cc9db24182ddeac56280d9" + integrity sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg== + +"@next/swc-linux-arm64-gnu@15.5.7": + version "15.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz#fe7c7e08264cf522d4e524299f6d3e63d68d579a" + integrity sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA== + +"@next/swc-linux-arm64-musl@15.5.7": + version "15.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz#94228fe293475ec34a5a54284e1056876f43a3cf" + integrity sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw== + +"@next/swc-linux-x64-gnu@15.5.7": + version "15.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz#078c71201dfe7fcfb8fa6dc92aae6c94bc011cdc" + integrity sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw== + +"@next/swc-linux-x64-musl@15.5.7": + version "15.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz#72947f5357f9226292353e0bb775643da3c7a182" + integrity sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA== + +"@next/swc-win32-arm64-msvc@15.5.7": + version "15.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz#397b912cd51c6a80e32b9c0507ecd82514353941" + integrity sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ== + +"@next/swc-win32-x64-msvc@15.5.7": + version "15.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz#e02b543d9dc6c1631d4ac239cb1177245dfedfe4" + integrity sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw== + +"@pagefind/darwin-arm64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz#0315030e6a89bec3121273b1851f7aadf0b12425" + integrity sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ== + +"@pagefind/darwin-x64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz#671e1fe0f0733350a3eb244ace2675166186793e" + integrity sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A== + +"@pagefind/freebsd-x64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz#3419701ce810e7ec050bbf4786b1c3bee78ec51b" + integrity sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q== + +"@pagefind/linux-arm64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz#ba2a5c8d10d5273fe61a8d230b546b08d22cb676" + integrity sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw== + +"@pagefind/linux-x64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz#1e56bb3c91fd0128be84e98897c9785c489fbbb7" + integrity sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg== + +"@pagefind/windows-x64@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz#ba68fd609621132e8e314a89e2d2d52516f61723" + integrity sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g== + +"@pandacss/is-valid-prop@^1.4.2": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@pandacss/is-valid-prop/-/is-valid-prop-1.8.1.tgz#8d23dd897a006c21f5ce0bcb66759b1b625d3b9a" + integrity sha512-gf2HTBCOboc65Jlb9swAjbffXSIv+A4vzSQ9iHyTCDLMcXTHYjPOQNliI36WkuQgR0pNXggBbQXGNaT9wKcrAw== + +"@shikijs/core@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.29.2.tgz#9c051d3ac99dd06ae46bd96536380c916e552bf3" + integrity sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ== + dependencies: + "@shikijs/engine-javascript" "1.29.2" + "@shikijs/engine-oniguruma" "1.29.2" + "@shikijs/types" "1.29.2" + "@shikijs/vscode-textmate" "^10.0.1" + "@types/hast" "^3.0.4" + hast-util-to-html "^9.0.4" + +"@shikijs/engine-javascript@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-1.29.2.tgz#a821ad713a3e0b7798a1926fd9e80116e38a1d64" + integrity sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A== + dependencies: + "@shikijs/types" "1.29.2" + "@shikijs/vscode-textmate" "^10.0.1" + oniguruma-to-es "^2.2.0" + +"@shikijs/engine-oniguruma@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz#d879717ced61d44e78feab16f701f6edd75434f1" + integrity sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA== + dependencies: + "@shikijs/types" "1.29.2" + "@shikijs/vscode-textmate" "^10.0.1" + +"@shikijs/langs@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-1.29.2.tgz#4f1de46fde8991468c5a68fa4a67dd2875d643cd" + integrity sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ== + dependencies: + "@shikijs/types" "1.29.2" + +"@shikijs/themes@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-1.29.2.tgz#293cc5c83dd7df3fdc8efa25cec8223f3a6acb0d" + integrity sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g== + dependencies: + "@shikijs/types" "1.29.2" + +"@shikijs/types@1.29.2": + version "1.29.2" + resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-1.29.2.tgz#a93fdb410d1af8360c67bf5fc1d1a68d58e21c4f" + integrity sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw== + dependencies: + "@shikijs/vscode-textmate" "^10.0.1" + "@types/hast" "^3.0.4" + +"@shikijs/vscode-textmate@^10.0.1": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz#a90ab31d0cc1dfb54c66a69e515bf624fa7b2224" + integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== + +"@swc/helpers@0.5.15": + version "0.5.15" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" + integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== + dependencies: + tslib "^2.8.0" + +"@swc/helpers@^0.5.0": + version "0.5.18" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.18.tgz#feeeabea0d10106ee25aaf900165df911ab6d3b1" + integrity sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ== + dependencies: + tslib "^2.8.0" + +"@types/debug@^4.0.0": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" + integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== + dependencies: + "@types/ms" "*" + +"@types/estree-jsx@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" + integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== + dependencies: + "@types/estree" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/hast@^3.0.0", "@types/hast@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + +"@types/mdast@^4.0.0": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" + integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== + dependencies: + "@types/unist" "*" + +"@types/mdx@^2.0.0": + version "2.0.13" + resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.13.tgz#68f6877043d377092890ff5b298152b0a21671bd" + integrity sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw== + +"@types/ms@*": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" + integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== + +"@types/node@^22.0.0": + version "22.19.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.7.tgz#434094ee1731ae76c16083008590a5835a8c39c1" + integrity sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw== + dependencies: + undici-types "~6.21.0" + +"@types/parse-json@^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== + +"@types/unist@*", "@types/unist@^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + +"@types/unist@^2.0.0": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" + integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== + +"@ungap/structured-clone@^1.0.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + +"@zag-js/accordion@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/accordion/-/accordion-1.31.1.tgz#ea16303036696bb49cf528b131e4bbe3c54437fc" + integrity sha512-3sGi4EZpGBz/O1IVkk9dzzWzP5vVVOj4Li6C+jHOnrgaWPouA/mBTP5L9HEL8qtFsECFZwpNo486eqiCmeHoGw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/anatomy@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/anatomy/-/anatomy-1.31.1.tgz#415564763b6e6b2f77ff3c2dac9c8683531bc5d3" + integrity sha512-BhIhf3Q0tRA0Jugd7AJfUBzeAb/iATBsw7KyYThMGcPWmrWssL7KWr5AB6RufzGKU7+DCb1QEhlqd4NSOJaYxQ== + +"@zag-js/angle-slider@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/angle-slider/-/angle-slider-1.31.1.tgz#fba6c241868ac8de3ee2318e9ac9a893dfb771c5" + integrity sha512-SfWrgnM0zMLX82rsIJOqWk430UnPA17UFGcDqMDRwXy1Wx4yptmx0aFAsSXnRnw4Ee7WaulF2RWBli6O6iYRCA== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/rect-utils" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/aria-hidden@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/aria-hidden/-/aria-hidden-1.31.1.tgz#5dfb1e9702a02f0964684ebace3592e935cf756c" + integrity sha512-SoNt4S2LkHNWPglQczWN0E5vAV15MT1GoK9MksZzbkMhl+pkDTdLytpXsQ1IgalC1YUng0XNps/Wt6P3uDuzTA== + dependencies: + "@zag-js/dom-query" "1.31.1" + +"@zag-js/async-list@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/async-list/-/async-list-1.31.1.tgz#db46419798d856a85a3de9b4907e502ba181cfd5" + integrity sha512-BDZEmr4KKh3JASgkXouOwoTWRS1UPE3gdZYZ7Sk7SJ1i8+Pk6zUQ4FnxaoF/cSAdCXyjSSr92Kns2bTk/QuNkQ== + dependencies: + "@zag-js/core" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/auto-resize@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/auto-resize/-/auto-resize-1.31.1.tgz#882d6313b71b6546b905aa718b6aa6e7b4ff46e4" + integrity sha512-qzWHibjBekSmFweG+EWY8g0lRzKtok7o9XtQ+JFlOu3s6x4D02z2YDzjDdfSLmS7j0NxISnwQkinWiDAZEYHog== + dependencies: + "@zag-js/dom-query" "1.31.1" + +"@zag-js/avatar@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/avatar/-/avatar-1.31.1.tgz#31f103b0984bc1e0b03704d14cbfd145fc9a4f38" + integrity sha512-Grosi2hRn4wfDYlPd8l+d4GCIFMsoj6ZFqii+1k14AqTDiCUJ/J0jCvOrRHkvkpEqektjuSD7e/GCX+yawqkuQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/bottom-sheet@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/bottom-sheet/-/bottom-sheet-1.31.1.tgz#9fe9b295bff3661b0a32a758df6d9c1be2f218c5" + integrity sha512-ZBbIpYyZX2zQeqW36aODVi9/I4J3zS1XmIHUjeXmfmf6TlQUA1ydgYl7ipREfmCzNWX2LEA5ZnPJQw0UBcrB8w== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/aria-hidden" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/focus-trap" "1.31.1" + "@zag-js/remove-scroll" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/carousel@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/carousel/-/carousel-1.31.1.tgz#4cf83807ae28b14b94947471bf260ccbe3855685" + integrity sha512-228Ol86G/lg8crcomy5cALkUYdOHCHcvJnSOQzeUj80JNjlELzrjBpaAj4lx8dZocfwou2Sg4NyZJ+mISSc+Dg== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/scroll-snap" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/checkbox@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/checkbox/-/checkbox-1.31.1.tgz#6547280b733b14c97cfb1da2d253f9dae91a8374" + integrity sha512-oLS8bqhimckLl6coCNmKPPUmB8wIbVhtkpLwLPLgz4vhhUe7gnpB5dea14Ow2JTBnmug8bMh/bJDtuPa9qQuTw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/focus-visible" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/clipboard@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/clipboard/-/clipboard-1.31.1.tgz#6f1803ec184440d61ca280dd6f21539f24d2ddc0" + integrity sha512-pv/gOmD9DMg+YmSMjahyd5oSp7/v9K0uQ3att6fPeaNMjB42b3tnY1S1GNVy5Ltf/qHDab6WVwlEN+1zKHXaYw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/collapsible@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/collapsible/-/collapsible-1.31.1.tgz#46082b9827d1d5893d46bdb71c396e7d8defae2a" + integrity sha512-eCC5G6bBZUwF8z2XULQXUNRxqte9I2Sv+WJ2brycPn1a68uYD76RzFBmLQ2er95VbshUdeo8nRuX8MooAFuYzg== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/collection@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/collection/-/collection-1.31.1.tgz#26724e1ffa359097f1f2836f9d66a95b56bd45ff" + integrity sha512-ecpfyfCj8Y0/GUPuHYsLxexIrx10VuR3Wd0H+lamcki3lYgQxZrpLRFMwgTqmI/m7t3zhm5QeEvMUJ1H14YMLA== + dependencies: + "@zag-js/utils" "1.31.1" + +"@zag-js/color-picker@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/color-picker/-/color-picker-1.31.1.tgz#547d2f354adf37f2dd47e551872c979452cc04a8" + integrity sha512-AWNZth49iEDxqh1DBZNSKpfEM/FF+MjL5bgUHVctnHdkpFsZLynJorWQQ4hNXNDFEc/I5w10KSxVCcO6tsPGFw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/color-utils" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/color-utils@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/color-utils/-/color-utils-1.31.1.tgz#444efc5c0fc0064b85915404980916137e8dde12" + integrity sha512-HdjTRU8C0tO6hK+PBVlu8iQH1MJaAnJAEdq2FcD97mq0PiPhrSj6iOftnrvPsE4CRieVFjnJWOvaubWFc4VmHA== + dependencies: + "@zag-js/utils" "1.31.1" + +"@zag-js/combobox@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/combobox/-/combobox-1.31.1.tgz#6719fe141ae01459814f8a242add2c56d127927f" + integrity sha512-IT0getSAGzngdRL20iX/iAh2d7DzVoMDDppOsOFBG2owKAgLpj8uLvUhy+lcrm6N8yxYOya89D6Aef7V5KdwlQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/aria-hidden" "1.31.1" + "@zag-js/collection" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/core@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/core/-/core-1.31.1.tgz#a8419dee9bb5e9b021009aba4b2aa44d6a48fda8" + integrity sha512-RaMJeqtjxG6k7iFD3WQnlyFJVT3yfQN+pJygAHH37GsMtiNzQQJOoesjb0LV9T27jwMXeNUzrh3MSDr1/0yVcQ== + dependencies: + "@zag-js/dom-query" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/date-picker@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/date-picker/-/date-picker-1.31.1.tgz#e9ec0419edd7e300279aa858e113e0038bb1b792" + integrity sha512-AOWN/IskGidVQt5g+uE9cILqJBTclE6OG1GC9WSWuyP/y4F+PdP/781SgYpYCZg/6pMGbL01PFKKb7xOOCeZAg== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/date-utils" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/live-region" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/date-utils@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/date-utils/-/date-utils-1.31.1.tgz#42633d8163d4d5aa9c50b0cc00f8d28ba77b977c" + integrity sha512-+Aq9g/rqLeiRmnazgdZMc59gAxqxbw3GGy8AngrtNipgRtMhPlzGa3S4Qsq1yau6OKaHZ13uckUS+MhLNbBY+Q== + +"@zag-js/dialog@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/dialog/-/dialog-1.31.1.tgz#a87edd3d67ce0fc6e66c036bbb15723920724ff9" + integrity sha512-iaWlYQ6TYoVjM/X5+UZVZzKiMboE50GnEzGUpbhbeRNRiLqSu5dODSFzior1G4kde/ns5eN+BTf/Tm6AT4N2og== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/aria-hidden" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/focus-trap" "1.31.1" + "@zag-js/remove-scroll" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/dismissable@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/dismissable/-/dismissable-1.31.1.tgz#93c56e85b2e0c71e0714da14d0600254da6c614e" + integrity sha512-jCdJwQmEkG6PlrN13fUk2l7ZclSu54FZwmT4xOtQpEbaiAiESm5KI5oyFh5jDPY47Goa28UJkEjWXVgKXKWb0g== + dependencies: + "@zag-js/dom-query" "1.31.1" + "@zag-js/interact-outside" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/dom-query@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/dom-query/-/dom-query-1.31.1.tgz#9b7e8ee3441dcb1b6c48f042177ffe293334d479" + integrity sha512-2tCZLwSfoXm62gwl0neiAN6u5VnzUhy5wHtKbX+klqGFatnca3Bm++H9+4PHMrwUWRbPg3H5N151lKFEOQhBfQ== + dependencies: + "@zag-js/types" "1.31.1" + +"@zag-js/editable@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/editable/-/editable-1.31.1.tgz#6bc4500a9c0acff1408a9ae83707d0f96e824159" + integrity sha512-JMICHw4/x0YqDy/n+I+TeaXlFbTA0j9w3UqOWMwUFQ+dAsq4JLXeqZDXu19MQN6yaTFdOpG1EFw4FEVTsu+d3Q== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/interact-outside" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/file-upload@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/file-upload/-/file-upload-1.31.1.tgz#2e6d063b68eb4b86d768dc543695cbc7e5cd35d5" + integrity sha512-cp7qMiXKrIcTfDamOz9wlnJLeBF8gucTI7Y+iKaP+hiIW+OG254GElfQiqXNDad3HUmD+Dt8Tx6uAzL/mw3sbQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/file-utils" "1.31.1" + "@zag-js/i18n-utils" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/file-utils@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/file-utils/-/file-utils-1.31.1.tgz#69a4f5142f7c2b6af7e1f1b42773af25e2df002f" + integrity sha512-MDDz52IdPh/mPUYrqUXvh7qDckJHs+mt5gjfx0N89qh2JNXuRU14zPotOKTzIKM4o+HFZkAT6BAfMpr9CX/0ug== + dependencies: + "@zag-js/i18n-utils" "1.31.1" + +"@zag-js/floating-panel@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/floating-panel/-/floating-panel-1.31.1.tgz#cfe2b6cc0e6ec12f3ea07b46f14cf4aa4dbc2788" + integrity sha512-Pjgd/wjdglZ90dtq/LC4o5sc6w0m+RehhPmJcIzq9T+E/Xrb6qrhf06QhxB9LwSj4DG/gIv87gmD2qF1VH7cRQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/rect-utils" "1.31.1" + "@zag-js/store" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/focus-trap@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/focus-trap/-/focus-trap-1.31.1.tgz#2417ce97dc6d28040378b152ddb22027e14f2492" + integrity sha512-omgUhAz1r81pYAujqYIIavdTKJzDRExioSiqhnx/xq10a6Q/xavMFflq8w7edMc9JHkTOnr9E5qh9abCVJjhpQ== + dependencies: + "@zag-js/dom-query" "1.31.1" + +"@zag-js/focus-visible@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/focus-visible/-/focus-visible-1.31.1.tgz#4cc4e8a391aab71f1be4141c741236a8ecf15fad" + integrity sha512-GC59A3yd7tj8aKhzvhrM+CEZZraXm5y/SpfIjz1J7kGV6eeXbUtjkbe75g99Ve8iJYfQVQlAj2GyN3oniHc5Zw== + dependencies: + "@zag-js/dom-query" "1.31.1" + +"@zag-js/highlight-word@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/highlight-word/-/highlight-word-1.31.1.tgz#d6554f7747f203075f33d3fb6b2510f65baca86e" + integrity sha512-nQw7t8LgWXW+6Z5E/p6T+OST0DDXp35mrFCzrkJL54aVTZ3GuLyIP2p0/HGQr2hE/KKLbZEs5i6UcXF84tiI4g== + +"@zag-js/hover-card@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/hover-card/-/hover-card-1.31.1.tgz#41b110332108ed2ed35bbe765b5b72460fb156fb" + integrity sha512-R74kz2wPgGwB3jKQeD91kdtlvVKpffWBJHqw8yCBd95GXGVmhym+BPoCToJzcqiemP8+0EtSuVPU9IHaSuJnSg== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/i18n-utils@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/i18n-utils/-/i18n-utils-1.31.1.tgz#8cb701774b7235260872419b421f217cea481336" + integrity sha512-SARkFuo1+Q0WcNv4jqvxp5hjCOqu/gBa7p6BTh7v5Bo00QhKRM/bCvVt0EB6V+h2oejrZfkwZ0MwbpQiL6L2aQ== + dependencies: + "@zag-js/dom-query" "1.31.1" + +"@zag-js/image-cropper@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/image-cropper/-/image-cropper-1.31.1.tgz#96b79d9af49023a456049c8563a2cd56fdbd9306" + integrity sha512-hFuy4I3jIJ/iyJsnfbLX1l/cJtN42j7lwhw8TeWVX8Y+hHxFPMSKx7AQirt/hALUbyy7QsQgAd5IslpsYq1Nlg== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/interact-outside@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/interact-outside/-/interact-outside-1.31.1.tgz#280bdcfb6f83aacca3b143197156cbdb98db62d6" + integrity sha512-oxBAlBqcatlxGUmhwUCRYTADIBrVoyxM1YrFzR1R8jhvVR/QCaxoLAyKwcA3mWXlZ8+NlXb7n5ELE11BZb/rEg== + dependencies: + "@zag-js/dom-query" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/json-tree-utils@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/json-tree-utils/-/json-tree-utils-1.31.1.tgz#54c495131dae7cdda09e77a38ad870e3f0b04fd2" + integrity sha512-wrNek2UBE69FWpo2f0E2MxiboBS+Uop79LeQU2jNDujA1o3x6b1Lp2r7Fl1sfnUWMdKVVQb44oqfIj2g3CTEmQ== + +"@zag-js/listbox@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/listbox/-/listbox-1.31.1.tgz#f83d8489d071187f1d4eb3d9e36af865df1c687d" + integrity sha512-LcTIr4I9eN4MR1nSRfQfseWgj4ybOXXAY2o5dBpEBL67dnCSX3swNb/4LQO+ebj077BViQb66pBb1KSoeHGkEQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/collection" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/focus-visible" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/live-region@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/live-region/-/live-region-1.31.1.tgz#6fbd62e5f2559f77756f0136d8f4ee1ef02888fa" + integrity sha512-RBx8jk1dgvkEUuFs77SBZn0WwvEkeZgVawVu6XUAy4ENfhP0D/qkvwNk+Els8InKmr1gWKajD7sh+g8M40Ex6A== + +"@zag-js/marquee@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/marquee/-/marquee-1.31.1.tgz#670222a46ea629f677321b3d017d1cd0f0eb8a13" + integrity sha512-Rt7+zy7CDOxXm0PqaTcmuWxcrZOPOpZY4T6IxOZk4ZcOXJQ2v7CkF3EK0pdI9PyI6Zpk/YIwQkENjidT55db0A== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/menu@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/menu/-/menu-1.31.1.tgz#bdfc59e9931e3e2e6336a94acdaad0a4d94753d0" + integrity sha512-eJPRM8tlauRTsAoJXchDBzMzL2RhXYSHmHak2IJCDMApCV51p0MqGYP8Er3DbMSQTPUFuTq779uUIarDqW+zmA== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/rect-utils" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/navigation-menu@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/navigation-menu/-/navigation-menu-1.31.1.tgz#68f05a2cbad76eaee597c514fdc32b0a0b2d07ae" + integrity sha512-xS4aynqmB9NYicPbEW8lPPakAfDfSgIDL1pRVSD6f1+VXkHD6LgNn6jUNDNbFt65mGhLpA2IczbvLCxv0g/ISQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/number-input@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/number-input/-/number-input-1.31.1.tgz#efad93bb62d1cc04c0b8aef148cf25160f957cf6" + integrity sha512-vn+BXEZ2/g2CMIFFyjjye/SbCeW3I/rlszL8EyBmhMcuA1l51OX2WKry6HeQNiU41uMyFg2rb1pb5KVw1gJsCg== + dependencies: + "@internationalized/number" "3.6.5" + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/pagination@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/pagination/-/pagination-1.31.1.tgz#60b34682e5e887d6cd7c8ea05ff331f82f12474a" + integrity sha512-icW6FNzIKNz7iXU+prlQWpMFJedDrhmCKzzI39SY+dv5g1Gnrlc0b44PxvNl5PWFLSkB5KBT/R1WCqd8Kh4cCA== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/password-input@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/password-input/-/password-input-1.31.1.tgz#d6dded0eef022487b27f0315f1c8836aae6a70d9" + integrity sha512-AivOeNO14a39xhxVMB2TVmIjmQ89OwVz0+2IjX3JjLS2Pmia+gg9xnVd2kBIcKfnqUN4MBnzmk7t46YWJMQVVQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/pin-input@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/pin-input/-/pin-input-1.31.1.tgz#0301f4eb724d173d656ff877b5e3d461f4d89b7d" + integrity sha512-k3ESoX5ve5sbWBLTCPYAzgLjRU7mVNEUiqAOhRgazOcBGV5wjGh398zWb1jr0FMxPnoAMrXDN/CQwJTmJcMKrg== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/popover@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/popover/-/popover-1.31.1.tgz#a6b6066b4898783929f21789cfad494d156a7d09" + integrity sha512-uCFJP3DFBkEBAre6lgGLw2xWS2ZIuT/DLeajIXb+8BmC9KCF0wY4c9qojx9F3rGMJQxcGl+WUoXENkOvkTaVhQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/aria-hidden" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/focus-trap" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/remove-scroll" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/popper@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/popper/-/popper-1.31.1.tgz#fb6292df602dd5146e6b238393e8c62c53bbf1c9" + integrity sha512-wLXcEqzn9MK1rGbsgnDH26o5ZWqR4oeb6ZepKKy0gcuJl/1S5/dr1VBvxJNMZlf9d6etvYklG5LRnIVkXCbrjA== + dependencies: + "@floating-ui/dom" "1.7.4" + "@zag-js/dom-query" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/presence@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/presence/-/presence-1.31.1.tgz#fa5b6be77b687fecae040f50261171c0b12b0cfa" + integrity sha512-tv+WsBnA0abIlDuEfZMh0lRPF4cMs6kWJosNkGBwzeXnGds+KXjzpL2KDtwDgbJgN3sI0xHPMYjRy2v3ZamcDA== + dependencies: + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + +"@zag-js/progress@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/progress/-/progress-1.31.1.tgz#d4c9e9c59df095d718f7ce4661016e49afb1015a" + integrity sha512-f9lIDHCRcFAG14LVEKOAPTdqPzphwIIraC6fTr9AwmNlYI6/qFDkz3jOlYVSyk5VsJAIFM/777x/CdqjliiOqg== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/qr-code@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/qr-code/-/qr-code-1.31.1.tgz#058f34c9b2ecb2b37991cb0e1bc6898ccac3c277" + integrity sha512-Rxh+HF12SgUp5rvTelp1qyLK3xkn37h2fT/L4eBQ0f8OUEo8wfowEbs36+1i61d6UuH7PJt4q/07eIf6vNVevA== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + proxy-memoize "3.0.1" + uqr "0.1.2" + +"@zag-js/radio-group@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/radio-group/-/radio-group-1.31.1.tgz#ef20d1267d1e2365c719bd586b2d2b0d9846296c" + integrity sha512-OfKIdEtSG0EuHM+cFVqcR+04yzZmcDRgG3j0QhoJsyS1my63ZHbwC2HNAtfPFh4U4sJx9yUexwSzPGZ6pOzIdw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/focus-visible" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/rating-group@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/rating-group/-/rating-group-1.31.1.tgz#b68a8d6dfc68cbdcd5d6348da096014e162a2a64" + integrity sha512-BkQUglKm4a+KXYPACYvIvBJSuEyzV0YQqjjiucwJ5UiOlK72C66VBvyGN+DqJRDnkU1K5azt6E1Ja5ANk3fgsg== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/react@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/react/-/react-1.31.1.tgz#7589c927e779848f4c2b46f9167173db2b93ff72" + integrity sha512-a7uYH+tcw1UYbcovyVBzlh6X8KztK/b1+s8sMs4Srhd24M+hZMetV94Z0bM1Km5aNAnoS4gkH3gtJjH0OphquQ== + dependencies: + "@zag-js/core" "1.31.1" + "@zag-js/store" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/rect-utils@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/rect-utils/-/rect-utils-1.31.1.tgz#c6dcbc96d76f4ebb8bad618b02eba6e4a31c466a" + integrity sha512-lBFheAnz8+3aGDFjqlkw0Iew/F03lFjiIf26hkkcFSZu0ltNZUMG/X3XLHUnHxdfbdBguc8ons6mr2MkVvisng== + +"@zag-js/remove-scroll@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/remove-scroll/-/remove-scroll-1.31.1.tgz#d9f3347dd28d1fc6369d736f479350cca62080ae" + integrity sha512-gVVJuFKaCjo652RmajYmkjXKgjJWLQ5ZhZLTaLUKWM1mAarvlqnLui8jrHEHLxqpfsjQylfdhJKkWmyF8NAgTA== + dependencies: + "@zag-js/dom-query" "1.31.1" + +"@zag-js/scroll-area@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/scroll-area/-/scroll-area-1.31.1.tgz#37d7412cdc9506ebb4ae417811aff51c74f75d86" + integrity sha512-GBXd1K3U0AHwWlJaqAMKQMZyeoxuBO6XYrVgdvzgiftQbJrZs5fuYOFyDvPLDWHTLYxaHso44/f+9EmAUAiytw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/scroll-snap@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/scroll-snap/-/scroll-snap-1.31.1.tgz#13f43987259fed9b7388f4bcc8b18c284f0d7d7e" + integrity sha512-YWsfhcQqiffu2X9HuB0fMnEQAu6rEOfGcvQYinvB6pjWPOvIJGxGMi/dYyy21XQDNJ9K1IcWRIo/yuaajoJyQQ== + dependencies: + "@zag-js/dom-query" "1.31.1" + +"@zag-js/select@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/select/-/select-1.31.1.tgz#aa514e8668e5d720a41bbadf10c4f6720247bc48" + integrity sha512-vKWb8BiRY83Y3HkDNnimf6cr1yvzJh1HwZlzXFz0y47zEvlikQaf+r96obR78RgTtMjNTTV15tTXdc1/WFoYkw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/collection" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/signature-pad@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/signature-pad/-/signature-pad-1.31.1.tgz#b48bac5dd2c38eb689886b822da1936df4dfad39" + integrity sha512-bz3WtLuIZoLrJDKcdS7fPAdD/Qi9wKiKACl5cu+ftv9zg8w+qqYNLtjH9HxeUFbCtQRKqcdXjO/UZ8iL07hgsQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + perfect-freehand "^1.2.2" + +"@zag-js/slider@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/slider/-/slider-1.31.1.tgz#af9ab16b79d65f7d6a7f36d18cbc3333ee902484" + integrity sha512-FILbLTMd3BnyclZ28+ippfyqzYPGK60qZapxtTERmWDC75Okf8AFnTCQf84Y8jRmBKCS1yhjF+IOtkFAENeB6w== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/splitter@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/splitter/-/splitter-1.31.1.tgz#29d84f3d689eb8e2ae26c18a1653178ddcdefd46" + integrity sha512-7SGBT2/xKsOzeSQEg+Otn1XV3RHrAz3jTySjBRKoEmdxubhfREqbKotbGVG65aTve11fQnmJ3Oyt3GJOeraxLA== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/steps@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/steps/-/steps-1.31.1.tgz#66dd823ffda8446999903c7182eb1f14bbe87073" + integrity sha512-KsBH38V3tH9/q8CDgx4sUSXLYwFdcp1crZy8hTIcN0RUiZ55PmqYKkN2znzBjTbaCW9yhP8kXsbuo2s8OIU5lQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/store@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/store/-/store-1.31.1.tgz#aceafec78957bcf5d8f94db8fa56f7a38f113834" + integrity sha512-d5ZTRciTuXOGQ3nML15kQLaTiR1wJPxT1Fu1nN659X6Rl8DPtubYaRCZ3RCk9Kyiyg2z5HxeVqDswaDvGbM9Rg== + dependencies: + proxy-compare "3.0.1" + +"@zag-js/switch@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/switch/-/switch-1.31.1.tgz#4198ca6edc54828959194a2e3f9daf889944e989" + integrity sha512-Jii3OSqSa9sQux+hvSRvp9dirzUF09+PAjrLjCQs+BT08EZ0XqeGvVzM0Wqf9LFy07HdLZntai3IUaXLF6byBw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/focus-visible" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/tabs@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/tabs/-/tabs-1.31.1.tgz#69bbbff16ae554e470ed4001565030b14872902c" + integrity sha512-QBq4ngpBNMNEI7Wuaq8llwHOqgcVbNHHEDC5zHg60Bf7MY5ltP8wSq6Kldu0zZRVwrLzanYoMELDUyf9H0vtnw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/tags-input@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/tags-input/-/tags-input-1.31.1.tgz#21b807d8c9aa457601bd5c4a4bd60e62cfba3e3c" + integrity sha512-V4lJe/aMIs7WVoXYfszU6E3iARLLRQFMiycu76/slb8NWJiLrkSIaMQ4FAe2pqkodgCWXA83tuaeAZRq7ouTFg== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/auto-resize" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/interact-outside" "1.31.1" + "@zag-js/live-region" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/timer@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/timer/-/timer-1.31.1.tgz#c4d8976da52530d16360c38c887de931f2eaa897" + integrity sha512-bXfeSbneWGOBKlD5dYq06T8CSY9Ky+qb1yIfJAFsRF4n34mpUYRdtfwpNQYyddGpkLD7oH4VibajeZXB7HaL0g== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/toast@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/toast/-/toast-1.31.1.tgz#5370c81d90550cf4d36cecb8e7de2d8e86a44ada" + integrity sha512-MueHEei9ol3H6tWBruLxF7yEUpV3vsJ8brTQVRRtPr/6pqBs5kGzfL4YskhQ2tiwO6egay8YrkbaS3xJfpKt4w== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/toggle-group@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/toggle-group/-/toggle-group-1.31.1.tgz#1490a114f6a4b515e12398a485fdf7495311eb77" + integrity sha512-Mojc7mex01/gvwXfrUIIThzT7HOktZoMge9rrb6+P7rQX7ulyNXYPjQrW2tay+t54GOJ3xODo9dU7PpRzXeHbw== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/toggle@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/toggle/-/toggle-1.31.1.tgz#3ed4d3fb602179bcfab094c98abe6291528b7cf7" + integrity sha512-HbFBuGfdyYkNvOp3cEB8Civ4E92finT4u3e4LKysB4/LboqKA0cJvFhSnHyThbROONTx06W/3CxwoSFR4o8IhA== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/tooltip@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/tooltip/-/tooltip-1.31.1.tgz#db7d0c063a674c1d8f7361365f08a32a90217cfe" + integrity sha512-pWEU5XhEPpnyl2VLrGJlyjj7+p+X0UX3Fld+WGhc/hCaWiuW2ZzD/ewDRhSOZu4/TzAO3axrPqG1YhW4fhogKQ== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/focus-visible" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/tour@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/tour/-/tour-1.31.1.tgz#0f0e3823316fb239cf168045b5d1103b17b53afb" + integrity sha512-ZmcAevXxoENHmHG0xwdIt1oCLe2/DW1CEBFPr7YuGKc+FU3QbBVZMzcBHrJCe0nkKXhUKzHOHM78bOHD/gM76w== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dismissable" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/focus-trap" "1.31.1" + "@zag-js/interact-outside" "1.31.1" + "@zag-js/popper" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/tree-view@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/tree-view/-/tree-view-1.31.1.tgz#3d1755d8729468b778ae48dec32fdcd64aee0000" + integrity sha512-Q+VSQz7X1XR8gT7ICWXlQOJIvzTWw/9BlF7B073UpEgAKRFlD11FmERka5y/BYqj8uE0vazcbSEA3Vc2dgCMJA== + dependencies: + "@zag-js/anatomy" "1.31.1" + "@zag-js/collection" "1.31.1" + "@zag-js/core" "1.31.1" + "@zag-js/dom-query" "1.31.1" + "@zag-js/types" "1.31.1" + "@zag-js/utils" "1.31.1" + +"@zag-js/types@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/types/-/types-1.31.1.tgz#a42e7059a33b81bd2d63e4625d45a3a413eafd77" + integrity sha512-mKw5DoeBjFykfUHv3ifCRjcogFTqp0aCCsmqQMfnf+J/mg2aXpAx76AXT1PYXAVVhxdP6qGXNd0mOQZDVrIlSQ== + dependencies: + csstype "3.2.3" + +"@zag-js/utils@1.31.1": + version "1.31.1" + resolved "https://registry.yarnpkg.com/@zag-js/utils/-/utils-1.31.1.tgz#4dfdc7d43eec354b585d9f5d9886681e3e1cbbe1" + integrity sha512-KLm0pmOtf4ydALbaVLboL7W98TDVxwVVLvSuvtRgV53XTjlsVopTRA5/Xmzq2NhWujDZAXv7bRV603NDgDcjSw== + +acorn-jsx@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.0.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +astring@^1.8.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/astring/-/astring-1.9.0.tgz#cc73e6062a7eb03e7d19c22d8b0b3451fd9bfeef" + integrity sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg== + +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + +bail@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" + integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caniuse-lite@^1.0.30001579: + version "1.0.30001765" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz#4a78d8a797fd4124ebaab2043df942eb091648ee" + integrity sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ== + +ccount@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" + integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== + +character-entities-html4@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" + integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== + +character-entities-legacy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" + integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== + +character-entities@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" + integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== + +character-reference-invalid@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" + integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== + +client-only@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + +collapse-white-space@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-2.1.0.tgz#640257174f9f42c740b40f3b55ee752924feefca" + integrity sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw== + +comma-separated-tokens@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" + integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== + +convert-source-map@^1.5.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +csstype@3.2.3, csstype@^3.0.2, csstype@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + +debug@^4.0.0, debug@^4.3.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +decode-named-character-reference@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz#3e40603760874c2e5867691b599d73a7da25b53f" + integrity sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q== + dependencies: + character-entities "^2.0.0" + +dequal@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +detect-libc@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + +devlop@^1.0.0, devlop@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" + integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== + dependencies: + dequal "^2.0.0" + +emoji-regex-xs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz#e8af22e5d9dbd7f7f22d280af3d19d2aab5b0724" + integrity sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg== + +entities@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" + integrity sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g== + +error-ex@^1.3.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" + integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== + dependencies: + is-arrayish "^0.2.1" + +esast-util-from-estree@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz#8d1cfb51ad534d2f159dc250e604f3478a79f1ad" + integrity sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ== + dependencies: + "@types/estree-jsx" "^1.0.0" + devlop "^1.0.0" + estree-util-visit "^2.0.0" + unist-util-position-from-estree "^2.0.0" + +esast-util-from-js@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz#5147bec34cc9da44accf52f87f239a40ac3e8225" + integrity sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw== + dependencies: + "@types/estree-jsx" "^1.0.0" + acorn "^8.0.0" + esast-util-from-estree "^2.0.0" + vfile-message "^4.0.0" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estree-util-attach-comments@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz#344bde6a64c8a31d15231e5ee9e297566a691c2d" + integrity sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw== + dependencies: + "@types/estree" "^1.0.0" + +estree-util-build-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/estree-util-build-jsx/-/estree-util-build-jsx-3.0.1.tgz#b6d0bced1dcc4f06f25cf0ceda2b2dcaf98168f1" + integrity sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ== + dependencies: + "@types/estree-jsx" "^1.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + estree-walker "^3.0.0" + +estree-util-is-identifier-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" + integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== + +estree-util-scope@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/estree-util-scope/-/estree-util-scope-1.0.0.tgz#9cbdfc77f5cb51e3d9ed4ad9c4adbff22d43e585" + integrity sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + +estree-util-to-js@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz#10a6fb924814e6abb62becf0d2bc4dea51d04f17" + integrity sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg== + dependencies: + "@types/estree-jsx" "^1.0.0" + astring "^1.8.0" + source-map "^0.7.0" + +estree-util-visit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/estree-util-visit/-/estree-util-visit-2.0.0.tgz#13a9a9f40ff50ed0c022f831ddf4b58d05446feb" + integrity sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/unist" "^3.0.0" + +estree-walker@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fathom-client@^3.6.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/fathom-client/-/fathom-client-3.7.2.tgz#819920316935974a200509dcf07774a3599676a9" + integrity sha512-sWtaNivhg7uwp/q1bUuIiNj4LeQZMEZ5NXXFFpZ8le4uDedAfQG84gPOdYehtVXbl+1yX2s8lmXZ2+IQ9a/xxA== + +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gray-matter@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" + integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== + dependencies: + js-yaml "^3.13.1" + kind-of "^6.0.2" + section-matter "^1.0.0" + strip-bom-string "^1.0.0" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hast-util-from-html@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz#485c74785358beb80c4ba6346299311ac4c49c82" + integrity sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw== + dependencies: + "@types/hast" "^3.0.0" + devlop "^1.1.0" + hast-util-from-parse5 "^8.0.0" + parse5 "^7.0.0" + vfile "^6.0.0" + vfile-message "^4.0.0" + +hast-util-from-parse5@^8.0.0: + version "8.0.3" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz#830a35022fff28c3fea3697a98c2f4cc6b835a2e" + integrity sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + hastscript "^9.0.0" + property-information "^7.0.0" + vfile "^6.0.0" + vfile-location "^5.0.0" + web-namespaces "^2.0.0" + +hast-util-parse-selector@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" + integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-to-estree@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hast-util-to-estree/-/hast-util-to-estree-3.1.3.tgz#e654c1c9374645135695cc0ab9f70b8fcaf733d7" + integrity sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w== + dependencies: + "@types/estree" "^1.0.0" + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + estree-util-attach-comments "^3.0.0" + estree-util-is-identifier-name "^3.0.0" + hast-util-whitespace "^3.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + style-to-js "^1.0.0" + unist-util-position "^5.0.0" + zwitch "^2.0.0" + +hast-util-to-html@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz#ccc673a55bb8e85775b08ac28380f72d47167005" + integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== + dependencies: + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + comma-separated-tokens "^2.0.0" + hast-util-whitespace "^3.0.0" + html-void-elements "^3.0.0" + mdast-util-to-hast "^13.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + stringify-entities "^4.0.0" + zwitch "^2.0.4" + +hast-util-to-jsx-runtime@^2.0.0: + version "2.3.6" + resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz#ff31897aae59f62232e21594eac7ef6b63333e98" + integrity sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg== + dependencies: + "@types/estree" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/unist" "^3.0.0" + comma-separated-tokens "^2.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + hast-util-whitespace "^3.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + style-to-js "^1.0.0" + unist-util-position "^5.0.0" + vfile-message "^4.0.0" + +hast-util-to-string@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz#a4f15e682849326dd211c97129c94b0c3e76527c" + integrity sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A== + dependencies: + "@types/hast" "^3.0.0" + +hast-util-whitespace@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" + integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== + dependencies: + "@types/hast" "^3.0.0" + +hastscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-9.0.1.tgz#dbc84bef6051d40084342c229c451cd9dc567dff" + integrity sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w== + dependencies: + "@types/hast" "^3.0.0" + comma-separated-tokens "^2.0.0" + hast-util-parse-selector "^4.0.0" + property-information "^7.0.0" + space-separated-tokens "^2.0.0" + +hoist-non-react-statics@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +html-void-elements@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" + integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +inline-style-parser@0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.7.tgz#b1fc68bfc0313b8685745e4464e37f9376b9c909" + integrity sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA== + +is-alphabetical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" + integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== + +is-alphanumerical@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" + integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== + dependencies: + is-alphabetical "^2.0.0" + is-decimal "^2.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-core-module@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-decimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" + integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== + +is-extendable@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-hexadecimal@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" + integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== + +is-plain-obj@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.2" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" + integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +longest-streak@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" + integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== + +markdown-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-2.0.0.tgz#34bebc83e9938cae16e0e017e4a9814a8330d3c4" + integrity sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q== + +mdast-util-from-markdown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" + integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + mdast-util-to-string "^4.0.0" + micromark "^4.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-decode-string "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-stringify-position "^4.0.0" + +mdast-util-mdx-expression@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" + integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-mdx-jsx@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz#fd04c67a2a7499efb905a8a5c578dddc9fdada0d" + integrity sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + ccount "^2.0.0" + devlop "^1.1.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + parse-entities "^4.0.0" + stringify-entities "^4.0.0" + unist-util-stringify-position "^4.0.0" + vfile-message "^4.0.0" + +mdast-util-mdx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-mdx/-/mdast-util-mdx-3.0.0.tgz#792f9cf0361b46bee1fdf1ef36beac424a099c41" + integrity sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w== + dependencies: + mdast-util-from-markdown "^2.0.0" + mdast-util-mdx-expression "^2.0.0" + mdast-util-mdx-jsx "^3.0.0" + mdast-util-mdxjs-esm "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-mdxjs-esm@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" + integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== + dependencies: + "@types/estree-jsx" "^1.0.0" + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + devlop "^1.0.0" + mdast-util-from-markdown "^2.0.0" + mdast-util-to-markdown "^2.0.0" + +mdast-util-phrasing@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" + integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== + dependencies: + "@types/mdast" "^4.0.0" + unist-util-is "^6.0.0" + +mdast-util-to-hast@^13.0.0: + version "13.2.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz#d7ff84ca499a57e2c060ae67548ad950e689a053" + integrity sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + "@ungap/structured-clone" "^1.0.0" + devlop "^1.0.0" + micromark-util-sanitize-uri "^2.0.0" + trim-lines "^3.0.0" + unist-util-position "^5.0.0" + unist-util-visit "^5.0.0" + vfile "^6.0.0" + +mdast-util-to-markdown@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" + integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== + dependencies: + "@types/mdast" "^4.0.0" + "@types/unist" "^3.0.0" + longest-streak "^3.0.0" + mdast-util-phrasing "^4.0.0" + mdast-util-to-string "^4.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-decode-string "^2.0.0" + unist-util-visit "^5.0.0" + zwitch "^2.0.0" + +mdast-util-to-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" + integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== + dependencies: + "@types/mdast" "^4.0.0" + +micromark-core-commonmark@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz#c691630e485021a68cf28dbc2b2ca27ebf678cd4" + integrity sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg== + dependencies: + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-factory-destination "^2.0.0" + micromark-factory-label "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-factory-title "^2.0.0" + micromark-factory-whitespace "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-classify-character "^2.0.0" + micromark-util-html-tag-name "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-mdx-expression@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-3.0.1.tgz#43d058d999532fb3041195a3c3c05c46fa84543b" + integrity sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-factory-mdx-expression "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-extension-mdx-jsx@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-3.0.2.tgz#ffc98bdb649798902fa9fc5689f67f9c1c902044" + integrity sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + estree-util-is-identifier-name "^3.0.0" + micromark-factory-mdx-expression "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + vfile-message "^4.0.0" + +micromark-extension-mdx-md@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdx-md/-/micromark-extension-mdx-md-2.0.0.tgz#1d252881ea35d74698423ab44917e1f5b197b92d" + integrity sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ== + dependencies: + micromark-util-types "^2.0.0" + +micromark-extension-mdxjs-esm@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-3.0.0.tgz#de21b2b045fd2059bd00d36746081de38390d54a" + integrity sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-position-from-estree "^2.0.0" + vfile-message "^4.0.0" + +micromark-extension-mdxjs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs/-/micromark-extension-mdxjs-3.0.0.tgz#b5a2e0ed449288f3f6f6c544358159557549de18" + integrity sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ== + dependencies: + acorn "^8.0.0" + acorn-jsx "^5.0.0" + micromark-extension-mdx-expression "^3.0.0" + micromark-extension-mdx-jsx "^3.0.0" + micromark-extension-mdx-md "^2.0.0" + micromark-extension-mdxjs-esm "^3.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-destination@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz#8fef8e0f7081f0474fbdd92deb50c990a0264639" + integrity sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-label@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz#5267efa97f1e5254efc7f20b459a38cb21058ba1" + integrity sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg== + dependencies: + devlop "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-mdx-expression@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-2.0.3.tgz#bb09988610589c07d1c1e4425285895041b3dfa9" + integrity sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ== + dependencies: + "@types/estree" "^1.0.0" + devlop "^1.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-events-to-acorn "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + unist-util-position-from-estree "^2.0.0" + vfile-message "^4.0.0" + +micromark-factory-space@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz#36d0212e962b2b3121f8525fc7a3c7c029f334fc" + integrity sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-title@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz#237e4aa5d58a95863f01032d9ee9b090f1de6e94" + integrity sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-factory-whitespace@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz#06b26b2983c4d27bfcc657b33e25134d4868b0b1" + integrity sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ== + dependencies: + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-character@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz#2f987831a40d4c510ac261e89852c4e9703ccda6" + integrity sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q== + dependencies: + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-chunked@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz#47fbcd93471a3fccab86cff03847fc3552db1051" + integrity sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-classify-character@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz#d399faf9c45ca14c8b4be98b1ea481bced87b629" + integrity sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-combine-extensions@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz#2a0f490ab08bff5cc2fd5eec6dd0ca04f89b30a9" + integrity sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg== + dependencies: + micromark-util-chunked "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-decode-numeric-character-reference@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz#fcf15b660979388e6f118cdb6bf7d79d73d26fe5" + integrity sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-decode-string@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz#6cb99582e5d271e84efca8e61a807994d7161eb2" + integrity sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ== + dependencies: + decode-named-character-reference "^1.0.0" + micromark-util-character "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-encode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz#0d51d1c095551cfaac368326963cf55f15f540b8" + integrity sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw== + +micromark-util-events-to-acorn@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-2.0.3.tgz#e7a8a6b55a47e5a06c720d5a1c4abae8c37c98f3" + integrity sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg== + dependencies: + "@types/estree" "^1.0.0" + "@types/unist" "^3.0.0" + devlop "^1.0.0" + estree-util-visit "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + vfile-message "^4.0.0" + +micromark-util-html-tag-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz#e40403096481986b41c106627f98f72d4d10b825" + integrity sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA== + +micromark-util-normalize-identifier@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz#c30d77b2e832acf6526f8bf1aa47bc9c9438c16d" + integrity sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q== + dependencies: + micromark-util-symbol "^2.0.0" + +micromark-util-resolve-all@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz#e1a2d62cdd237230a2ae11839027b19381e31e8b" + integrity sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg== + dependencies: + micromark-util-types "^2.0.0" + +micromark-util-sanitize-uri@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz#ab89789b818a58752b73d6b55238621b7faa8fd7" + integrity sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ== + dependencies: + micromark-util-character "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-symbol "^2.0.0" + +micromark-util-subtokenize@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz#d8ade5ba0f3197a1cf6a2999fbbfe6357a1a19ee" + integrity sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA== + dependencies: + devlop "^1.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +micromark-util-symbol@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz#e5da494e8eb2b071a0d08fb34f6cefec6c0a19b8" + integrity sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q== + +micromark-util-types@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz#f00225f5f5a0ebc3254f96c36b6605c4b393908e" + integrity sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA== + +micromark@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.2.tgz#91395a3e1884a198e62116e33c9c568e39936fdb" + integrity sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA== + dependencies: + "@types/debug" "^4.0.0" + debug "^4.0.0" + decode-named-character-reference "^1.0.0" + devlop "^1.0.0" + micromark-core-commonmark "^2.0.0" + micromark-factory-space "^2.0.0" + micromark-util-character "^2.0.0" + micromark-util-chunked "^2.0.0" + micromark-util-combine-extensions "^2.0.0" + micromark-util-decode-numeric-character-reference "^2.0.0" + micromark-util-encode "^2.0.0" + micromark-util-normalize-identifier "^2.0.0" + micromark-util-resolve-all "^2.0.0" + micromark-util-sanitize-uri "^2.0.0" + micromark-util-subtokenize "^2.0.0" + micromark-util-symbol "^2.0.0" + micromark-util-types "^2.0.0" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@^3.3.6: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +next-mdx-remote@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/next-mdx-remote/-/next-mdx-remote-5.0.0.tgz#028a2cf5cf7f814d988d7ab11a401bed0f31b4ee" + integrity sha512-RNNbqRpK9/dcIFZs/esQhuLA8jANqlH694yqoDBK8hkVdJUndzzGmnPHa2nyi90N4Z9VmzuSWNRpr5ItT3M7xQ== + dependencies: + "@babel/code-frame" "^7.23.5" + "@mdx-js/mdx" "^3.0.1" + "@mdx-js/react" "^3.0.1" + unist-util-remove "^3.1.0" + vfile "^6.0.1" + vfile-matter "^5.0.0" + +next-themes@^0.4.0: + version "0.4.6" + resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.6.tgz#8d7e92d03b8fea6582892a50a928c9b23502e8b6" + integrity sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA== + +next@^15.0.0: + version "15.5.9" + resolved "https://registry.yarnpkg.com/next/-/next-15.5.9.tgz#1b80d05865cc27e710fb4dcfc6fd9e726ed12ad4" + integrity sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg== + dependencies: + "@next/env" "15.5.9" + "@swc/helpers" "0.5.15" + caniuse-lite "^1.0.30001579" + postcss "8.4.31" + styled-jsx "5.1.6" + optionalDependencies: + "@next/swc-darwin-arm64" "15.5.7" + "@next/swc-darwin-x64" "15.5.7" + "@next/swc-linux-arm64-gnu" "15.5.7" + "@next/swc-linux-arm64-musl" "15.5.7" + "@next/swc-linux-x64-gnu" "15.5.7" + "@next/swc-linux-x64-musl" "15.5.7" + "@next/swc-win32-arm64-msvc" "15.5.7" + "@next/swc-win32-x64-msvc" "15.5.7" + sharp "^0.34.3" + +oniguruma-to-es@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-2.3.0.tgz#35ea9104649b7c05f3963c6b3b474d964625028b" + integrity sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g== + dependencies: + emoji-regex-xs "^1.0.0" + regex "^5.1.1" + regex-recursion "^5.1.1" + +pagefind@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/pagefind/-/pagefind-1.4.0.tgz#0154b0a44b5ef9ef55c156824a3244bfc0c4008d" + integrity sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g== + optionalDependencies: + "@pagefind/darwin-arm64" "1.4.0" + "@pagefind/darwin-x64" "1.4.0" + "@pagefind/freebsd-x64" "1.4.0" + "@pagefind/linux-arm64" "1.4.0" + "@pagefind/linux-x64" "1.4.0" + "@pagefind/windows-x64" "1.4.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-entities@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.2.tgz#61d46f5ed28e4ee62e9ddc43d6b010188443f159" + integrity sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw== + dependencies: + "@types/unist" "^2.0.0" + character-entities-legacy "^3.0.0" + character-reference-invalid "^2.0.0" + decode-named-character-reference "^1.0.0" + is-alphanumerical "^2.0.0" + is-decimal "^2.0.0" + is-hexadecimal "^2.0.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse-numeric-range@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz#7c63b61190d61e4d53a1197f0c83c47bb670ffa3" + integrity sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ== + +parse5@^7.0.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.3.0.tgz#d7e224fa72399c7a175099f45fc2ad024b05ec05" + integrity sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw== + dependencies: + entities "^6.0.0" + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +perfect-freehand@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-1.2.2.tgz#292f65b72df0c7f57a89c4b346b50d7139014172" + integrity sha512-eh31l019WICQ03pkF3FSzHxB8n07ItqIQ++G5UV8JX0zVOXzgTGCqnRR0jJ2h9U8/2uW4W4mtGJELt9kEV0CFQ== + +picocolors@^1.0.0, picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +postcss@8.4.31: + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +property-information@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-7.1.0.tgz#b622e8646e02b580205415586b40804d3e8bfd5d" + integrity sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ== + +proxy-compare@3.0.1, proxy-compare@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-3.0.1.tgz#3262cff3a25a6dedeaa299f6cf2369d6f7588a94" + integrity sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q== + +proxy-memoize@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/proxy-memoize/-/proxy-memoize-3.0.1.tgz#75eed518778b282abb0bc55e748995214b7f74a9" + integrity sha512-VDdG/VYtOgdGkWJx7y0o7p+zArSf2383Isci8C+BP3YXgMYDoPd3cCBjw0JdWb6YBb9sFiOPbAADDVTPJnh+9g== + dependencies: + proxy-compare "^3.0.0" + +react-dom@^19.0.0: + version "19.2.3" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.3.tgz#f0b61d7e5c4a86773889fcc1853af3ed5f215b17" + integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg== + dependencies: + scheduler "^0.27.0" + +"react-fathom@file:..": + version "0.2.0" + +react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react@^19.0.0: + version "19.2.3" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.3.tgz#d83e5e8e7a258cf6b4fe28640515f99b87cd19b8" + integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA== + +reading-time@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/reading-time/-/reading-time-1.5.0.tgz#d2a7f1b6057cb2e169beaf87113cc3411b5bc5bb" + integrity sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg== + +recma-build-jsx@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/recma-build-jsx/-/recma-build-jsx-1.0.0.tgz#c02f29e047e103d2fab2054954e1761b8ea253c4" + integrity sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew== + dependencies: + "@types/estree" "^1.0.0" + estree-util-build-jsx "^3.0.0" + vfile "^6.0.0" + +recma-jsx@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/recma-jsx/-/recma-jsx-1.0.1.tgz#58e718f45e2102ed0bf2fa994f05b70d76801a1a" + integrity sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w== + dependencies: + acorn-jsx "^5.0.0" + estree-util-to-js "^2.0.0" + recma-parse "^1.0.0" + recma-stringify "^1.0.0" + unified "^11.0.0" + +recma-parse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/recma-parse/-/recma-parse-1.0.0.tgz#c351e161bb0ab47d86b92a98a9d891f9b6814b52" + integrity sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ== + dependencies: + "@types/estree" "^1.0.0" + esast-util-from-js "^2.0.0" + unified "^11.0.0" + vfile "^6.0.0" + +recma-stringify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/recma-stringify/-/recma-stringify-1.0.0.tgz#54632030631e0c7546136ff9ef8fde8e7b44f130" + integrity sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g== + dependencies: + "@types/estree" "^1.0.0" + estree-util-to-js "^2.0.0" + unified "^11.0.0" + vfile "^6.0.0" + +regex-recursion@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/regex-recursion/-/regex-recursion-5.1.1.tgz#5a73772d18adbf00f57ad097bf54171b39d78f8b" + integrity sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w== + dependencies: + regex "^5.1.1" + regex-utilities "^2.3.0" + +regex-utilities@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/regex-utilities/-/regex-utilities-2.3.0.tgz#87163512a15dce2908cf079c8960d5158ff43280" + integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== + +regex@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/regex/-/regex-5.1.1.tgz#cf798903f24d6fe6e531050a36686e082b29bd03" + integrity sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw== + dependencies: + regex-utilities "^2.3.0" + +rehype-parse@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-9.0.1.tgz#9993bda129acc64c417a9d3654a7be38b2a94c20" + integrity sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag== + dependencies: + "@types/hast" "^3.0.0" + hast-util-from-html "^2.0.0" + unified "^11.0.0" + +rehype-pretty-code@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/rehype-pretty-code/-/rehype-pretty-code-0.14.1.tgz#603d2ddccb11f1cf95e42a10b37b842beb8ad78e" + integrity sha512-IpG4OL0iYlbx78muVldsK86hdfNoht0z63AP7sekQNW2QOTmjxB7RbTO+rhIYNGRljgHxgVZoPwUl6bIC9SbjA== + dependencies: + "@types/hast" "^3.0.4" + hast-util-to-string "^3.0.0" + parse-numeric-range "^1.3.0" + rehype-parse "^9.0.0" + unified "^11.0.5" + unist-util-visit "^5.0.0" + +rehype-recma@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rehype-recma/-/rehype-recma-1.0.0.tgz#d68ef6344d05916bd96e25400c6261775411aa76" + integrity sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw== + dependencies: + "@types/estree" "^1.0.0" + "@types/hast" "^3.0.0" + hast-util-to-estree "^3.0.0" + +remark-mdx@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-3.1.1.tgz#047f97038bc7ec387aebb4b0a4fe23779999d845" + integrity sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg== + dependencies: + mdast-util-mdx "^3.0.0" + micromark-extension-mdxjs "^3.0.0" + +remark-parse@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" + integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-from-markdown "^2.0.0" + micromark-util-types "^2.0.0" + unified "^11.0.0" + +remark-rehype@^11.0.0: + version "11.1.2" + resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.2.tgz#2addaadda80ca9bd9aa0da763e74d16327683b37" + integrity sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw== + dependencies: + "@types/hast" "^3.0.0" + "@types/mdast" "^4.0.0" + mdast-util-to-hast "^13.0.0" + unified "^11.0.0" + vfile "^6.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.19.0: + version "1.22.11" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== + dependencies: + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +scheduler@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" + integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== + +section-matter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" + integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== + dependencies: + extend-shallow "^2.0.1" + kind-of "^6.0.0" + +semver@^7.7.3: + version "7.7.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== + +sharp@^0.34.3: + version "0.34.5" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0" + integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg== + dependencies: + "@img/colour" "^1.0.0" + detect-libc "^2.1.2" + semver "^7.7.3" + optionalDependencies: + "@img/sharp-darwin-arm64" "0.34.5" + "@img/sharp-darwin-x64" "0.34.5" + "@img/sharp-libvips-darwin-arm64" "1.2.4" + "@img/sharp-libvips-darwin-x64" "1.2.4" + "@img/sharp-libvips-linux-arm" "1.2.4" + "@img/sharp-libvips-linux-arm64" "1.2.4" + "@img/sharp-libvips-linux-ppc64" "1.2.4" + "@img/sharp-libvips-linux-riscv64" "1.2.4" + "@img/sharp-libvips-linux-s390x" "1.2.4" + "@img/sharp-libvips-linux-x64" "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64" "1.2.4" + "@img/sharp-libvips-linuxmusl-x64" "1.2.4" + "@img/sharp-linux-arm" "0.34.5" + "@img/sharp-linux-arm64" "0.34.5" + "@img/sharp-linux-ppc64" "0.34.5" + "@img/sharp-linux-riscv64" "0.34.5" + "@img/sharp-linux-s390x" "0.34.5" + "@img/sharp-linux-x64" "0.34.5" + "@img/sharp-linuxmusl-arm64" "0.34.5" + "@img/sharp-linuxmusl-x64" "0.34.5" + "@img/sharp-wasm32" "0.34.5" + "@img/sharp-win32-arm64" "0.34.5" + "@img/sharp-win32-ia32" "0.34.5" + "@img/sharp-win32-x64" "0.34.5" + +shiki@^1.24.0: + version "1.29.2" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.29.2.tgz#5c93771f2d5305ce9c05975c33689116a27dc657" + integrity sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg== + dependencies: + "@shikijs/core" "1.29.2" + "@shikijs/engine-javascript" "1.29.2" + "@shikijs/engine-oniguruma" "1.29.2" + "@shikijs/langs" "1.29.2" + "@shikijs/themes" "1.29.2" + "@shikijs/types" "1.29.2" + "@shikijs/vscode-textmate" "^10.0.1" + "@types/hast" "^3.0.4" + +source-map-js@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.7.0: + version "0.7.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.6.tgz#a3658ab87e5b6429c8a1f3ba0083d4c61ca3ef02" + integrity sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ== + +space-separated-tokens@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" + integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stringify-entities@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" + integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== + dependencies: + character-entities-html4 "^2.0.0" + character-entities-legacy "^3.0.0" + +strip-bom-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" + integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== + +style-to-js@^1.0.0: + version "1.1.21" + resolved "https://registry.yarnpkg.com/style-to-js/-/style-to-js-1.1.21.tgz#2908941187f857e79e28e9cd78008b9a0b3e0e8d" + integrity sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ== + dependencies: + style-to-object "1.0.14" + +style-to-object@1.0.14: + version "1.0.14" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.14.tgz#1d22f0e7266bb8c6d8cae5caf4ec4f005e08f611" + integrity sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw== + dependencies: + inline-style-parser "0.2.7" + +styled-jsx@5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499" + integrity sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA== + dependencies: + client-only "0.0.1" + +stylis@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51" + integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw== + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +trim-lines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" + integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== + +trough@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" + integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== + +tslib@^2.4.0, tslib@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +typescript@^5.7.0: + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +unified@^11.0.0, unified@^11.0.5: + version "11.0.5" + resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" + integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== + dependencies: + "@types/unist" "^3.0.0" + bail "^2.0.0" + devlop "^1.0.0" + extend "^3.0.0" + is-plain-obj "^4.0.0" + trough "^2.0.0" + vfile "^6.0.0" + +unist-util-is@^5.0.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.2.1.tgz#b74960e145c18dcb6226bc57933597f5486deae9" + integrity sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw== + dependencies: + "@types/unist" "^2.0.0" + +unist-util-is@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.1.tgz#d0a3f86f2dd0db7acd7d8c2478080b5c67f9c6a9" + integrity sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-position-from-estree@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz#d94da4df596529d1faa3de506202f0c9a23f2200" + integrity sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-position@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" + integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-remove@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-3.1.1.tgz#8bfa181aff916bd32a4ed30b3ed76d0c21c077df" + integrity sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + unist-util-visit-parents "^5.0.0" + +unist-util-stringify-position@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" + integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== + dependencies: + "@types/unist" "^3.0.0" + +unist-util-visit-parents@^5.0.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb" + integrity sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^5.0.0" + +unist-util-visit-parents@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz#777df7fb98652ce16b4b7cd999d0a1a40efa3a02" + integrity sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + +unist-util-visit@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.1.0.tgz#9a2a28b0aa76a15e0da70a08a5863a2f060e2468" + integrity sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg== + dependencies: + "@types/unist" "^3.0.0" + unist-util-is "^6.0.0" + unist-util-visit-parents "^6.0.0" + +uqr@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/uqr/-/uqr-0.1.2.tgz#5c6cd5dcff9581f9bb35b982cb89e2c483a41d7d" + integrity sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA== + +vfile-location@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.3.tgz#cb9eacd20f2b6426d19451e0eafa3d0a846225c3" + integrity sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg== + dependencies: + "@types/unist" "^3.0.0" + vfile "^6.0.0" + +vfile-matter@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vfile-matter/-/vfile-matter-5.0.1.tgz#3f701840dde13c68d72d5c5ebd9cf233dff84419" + integrity sha512-o6roP82AiX0XfkyTHyRCMXgHfltUNlXSEqCIS80f+mbAyiQBE2fxtDVMtseyytGx75sihiJFo/zR6r/4LTs2Cw== + dependencies: + vfile "^6.0.0" + yaml "^2.0.0" + +vfile-message@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.3.tgz#87b44dddd7b70f0641c2e3ed0864ba73e2ea8df4" + integrity sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw== + dependencies: + "@types/unist" "^3.0.0" + unist-util-stringify-position "^4.0.0" + +vfile@^6.0.0, vfile@^6.0.1: + version "6.0.3" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" + integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== + dependencies: + "@types/unist" "^3.0.0" + vfile-message "^4.0.0" + +web-namespaces@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" + integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== + +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yaml@^2.0.0: + version "2.8.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.2.tgz#5694f25eca0ce9c3e7a9d9e00ce0ddabbd9e35c5" + integrity sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A== + +zwitch@^2.0.0, zwitch@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" + integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== diff --git a/examples/next-app/package.json b/examples/next-app/package.json index 4565dc9..7f40711 100644 --- a/examples/next-app/package.json +++ b/examples/next-app/package.json @@ -12,7 +12,7 @@ "next": "^16.1.0", "react": "^19.2.3", "react-dom": "^19.2.3", - "react-fathom": "^0.2.0", + "react-fathom": "file:../..", "fathom-client": "^3.6.0" }, "devDependencies": { diff --git a/examples/next-pages/package.json b/examples/next-pages/package.json index cb86d3b..ae53057 100644 --- a/examples/next-pages/package.json +++ b/examples/next-pages/package.json @@ -12,7 +12,7 @@ "next": "^16.1.0", "react": "^19.2.3", "react-dom": "^19.2.3", - "react-fathom": "^0.2.0", + "react-fathom": "file:../..", "fathom-client": "^3.6.0" }, "devDependencies": { diff --git a/examples/react-native/package.json b/examples/react-native/package.json index ce957ea..6adee2f 100644 --- a/examples/react-native/package.json +++ b/examples/react-native/package.json @@ -20,7 +20,7 @@ "react-native-safe-area-context": "^4.14.0", "react-native-screens": "~4.4.0", "react-native-webview": "^13.12.0", - "react-fathom": "^0.2.0" + "react-fathom": "file:../.." }, "devDependencies": { "@babel/core": "^7.25.0", diff --git a/examples/react/package.json b/examples/react/package.json index c7e4550..8d983e0 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -14,7 +14,7 @@ "fathom-client": "^3.6.0", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-fathom": "^0.2.0", + "react-fathom": "file:../..", "react-router-dom": "^7.0.0" }, "devDependencies": { From 4a7414207bcd13723a6d29e17b1b73bebe1e81bc Mon Sep 17 00:00:00 2001 From: Ryan Hefner Date: Thu, 22 Jan 2026 16:55:09 -0500 Subject: [PATCH 29/54] Bump docs dependencies to latest versions --- docs/package.json | 8 +- docs/tsconfig.json | 30 ++++- docs/yarn.lock | 287 +++++++++++++++++++++++---------------------- 3 files changed, 173 insertions(+), 152 deletions(-) diff --git a/docs/package.json b/docs/package.json index ebaf28d..29bed3d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -13,19 +13,19 @@ "@emotion/react": "^11.13.0", "fathom-client": "^3.6.0", "gray-matter": "^4.0.3", - "next": "^15.0.0", - "next-themes": "^0.4.0", + "next": "^16.1.4", "next-mdx-remote": "^5.0.0", + "next-themes": "^0.4.0", "pagefind": "^1.3.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-fathom": "file:..", "reading-time": "^1.5.0", "rehype-pretty-code": "^0.14.0", - "shiki": "^1.24.0" + "shiki": "^3.21.0" }, "devDependencies": { - "@types/node": "^22.0.0", + "@types/node": "^25.0.10", "typescript": "^5.7.0" } } diff --git a/docs/tsconfig.json b/docs/tsconfig.json index bd3ee29..ff87ced 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -12,12 +16,26 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", - "plugins": [{ "name": "next" }], + "jsx": "react-jsx", + "plugins": [ + { + "name": "next" + } + ], "paths": { - "@/*": ["./*"] + "@/*": [ + "./*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } diff --git a/docs/yarn.lock b/docs/yarn.lock index 606dec7..d462200 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -517,50 +517,50 @@ dependencies: "@types/mdx" "^2.0.0" -"@next/env@15.5.9": - version "15.5.9" - resolved "https://registry.yarnpkg.com/@next/env/-/env-15.5.9.tgz#53c2c34dc17cd87b61f70c6cc211e303123b2ab8" - integrity sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg== - -"@next/swc-darwin-arm64@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz#f0c9ccfec2cd87cbd4b241ce4c779a7017aed958" - integrity sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw== - -"@next/swc-darwin-x64@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz#18009e9fcffc5c0687cc9db24182ddeac56280d9" - integrity sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg== - -"@next/swc-linux-arm64-gnu@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz#fe7c7e08264cf522d4e524299f6d3e63d68d579a" - integrity sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA== - -"@next/swc-linux-arm64-musl@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz#94228fe293475ec34a5a54284e1056876f43a3cf" - integrity sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw== - -"@next/swc-linux-x64-gnu@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz#078c71201dfe7fcfb8fa6dc92aae6c94bc011cdc" - integrity sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw== - -"@next/swc-linux-x64-musl@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz#72947f5357f9226292353e0bb775643da3c7a182" - integrity sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA== - -"@next/swc-win32-arm64-msvc@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz#397b912cd51c6a80e32b9c0507ecd82514353941" - integrity sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ== - -"@next/swc-win32-x64-msvc@15.5.7": - version "15.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz#e02b543d9dc6c1631d4ac239cb1177245dfedfe4" - integrity sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw== +"@next/env@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/env/-/env-16.1.4.tgz#1f5155b16bad9825432b5e398b83df687b7b86f9" + integrity sha512-gkrXnZyxPUy0Gg6SrPQPccbNVLSP3vmW8LU5dwEttEEC1RwDivk8w4O+sZIjFvPrSICXyhQDCG+y3VmjlJf+9A== + +"@next/swc-darwin-arm64@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.4.tgz#2d5ee68da80c9b822edd06caa360aef1917d0f37" + integrity sha512-T8atLKuvk13XQUdVLCv1ZzMPgLPW0+DWWbHSQXs0/3TjPrKNxTmUIhOEaoEyl3Z82k8h/gEtqyuoZGv6+Ugawg== + +"@next/swc-darwin-x64@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.4.tgz#2f8d4462f48d4cb3c927de1962ca7a7b2f8a5b03" + integrity sha512-AKC/qVjUGUQDSPI6gESTx0xOnOPQ5gttogNS3o6bA83yiaSZJek0Am5yXy82F1KcZCx3DdOwdGPZpQCluonuxg== + +"@next/swc-linux-arm64-gnu@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.4.tgz#79fecac25ad4a0ee1081110f4c8863b87e754943" + integrity sha512-POQ65+pnYOkZNdngWfMEt7r53bzWiKkVNbjpmCt1Zb3V6lxJNXSsjwRuTQ8P/kguxDC8LRkqaL3vvsFrce4dMQ== + +"@next/swc-linux-arm64-musl@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.4.tgz#e9a99b1ea9a68908c3d36a847a6fe367b4fc3855" + integrity sha512-3Wm0zGYVCs6qDFAiSSDL+Z+r46EdtCv/2l+UlIdMbAq9hPJBvGu/rZOeuvCaIUjbArkmXac8HnTyQPJFzFWA0Q== + +"@next/swc-linux-x64-gnu@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.4.tgz#4804de5f42ac8333e0049ab538473cbd996507f6" + integrity sha512-lWAYAezFinaJiD5Gv8HDidtsZdT3CDaCeqoPoJjeB57OqzvMajpIhlZFce5sCAH6VuX4mdkxCRqecCJFwfm2nQ== + +"@next/swc-linux-x64-musl@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.4.tgz#4aa01e59b0e0fd19ab493ee239e3904c42419ca6" + integrity sha512-fHaIpT7x4gA6VQbdEpYUXRGyge/YbRrkG6DXM60XiBqDM2g2NcrsQaIuj375egnGFkJow4RHacgBOEsHfGbiUw== + +"@next/swc-win32-arm64-msvc@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.4.tgz#67652a5c57889f44c11e145d49f777ac2e6cde58" + integrity sha512-MCrXxrTSE7jPN1NyXJr39E+aNFBrQZtO154LoCz7n99FuKqJDekgxipoodLNWdQP7/DZ5tKMc/efybx1l159hw== + +"@next/swc-win32-x64-msvc@16.1.4": + version "16.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.4.tgz#3c51597eb64a96b8fcade74ab3f21ef3ad278a33" + integrity sha512-JSVlm9MDhmTXw/sO2PE/MRj+G6XOSMZB+BcZ0a7d6KwVFZVpkHcb2okyoYFBaco6LeiL53BBklRlOrDDbOeE5w== "@pagefind/darwin-arm64@1.4.0": version "1.4.0" @@ -597,58 +597,56 @@ resolved "https://registry.yarnpkg.com/@pandacss/is-valid-prop/-/is-valid-prop-1.8.1.tgz#8d23dd897a006c21f5ce0bcb66759b1b625d3b9a" integrity sha512-gf2HTBCOboc65Jlb9swAjbffXSIv+A4vzSQ9iHyTCDLMcXTHYjPOQNliI36WkuQgR0pNXggBbQXGNaT9wKcrAw== -"@shikijs/core@1.29.2": - version "1.29.2" - resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.29.2.tgz#9c051d3ac99dd06ae46bd96536380c916e552bf3" - integrity sha512-vju0lY9r27jJfOY4Z7+Rt/nIOjzJpZ3y+nYpqtUZInVoXQ/TJZcfGnNOGnKjFdVZb8qexiCuSlZRKcGfhhTTZQ== +"@shikijs/core@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-3.21.0.tgz#9641d09865c43612b28e7931f9af68c8a62edd90" + integrity sha512-AXSQu/2n1UIQekY8euBJlvFYZIw0PHY63jUzGbrOma4wPxzznJXTXkri+QcHeBNaFxiiOljKxxJkVSoB3PjbyA== dependencies: - "@shikijs/engine-javascript" "1.29.2" - "@shikijs/engine-oniguruma" "1.29.2" - "@shikijs/types" "1.29.2" - "@shikijs/vscode-textmate" "^10.0.1" + "@shikijs/types" "3.21.0" + "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" - hast-util-to-html "^9.0.4" + hast-util-to-html "^9.0.5" -"@shikijs/engine-javascript@1.29.2": - version "1.29.2" - resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-1.29.2.tgz#a821ad713a3e0b7798a1926fd9e80116e38a1d64" - integrity sha512-iNEZv4IrLYPv64Q6k7EPpOCE/nuvGiKl7zxdq0WFuRPF5PAE9PRo2JGq/d8crLusM59BRemJ4eOqrFrC4wiQ+A== +"@shikijs/engine-javascript@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@shikijs/engine-javascript/-/engine-javascript-3.21.0.tgz#f04554fe87bed39d00ba4b140894b41cd207f5cb" + integrity sha512-ATwv86xlbmfD9n9gKRiwuPpWgPENAWCLwYCGz9ugTJlsO2kOzhOkvoyV/UD+tJ0uT7YRyD530x6ugNSffmvIiQ== dependencies: - "@shikijs/types" "1.29.2" - "@shikijs/vscode-textmate" "^10.0.1" - oniguruma-to-es "^2.2.0" + "@shikijs/types" "3.21.0" + "@shikijs/vscode-textmate" "^10.0.2" + oniguruma-to-es "^4.3.4" -"@shikijs/engine-oniguruma@1.29.2": - version "1.29.2" - resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-1.29.2.tgz#d879717ced61d44e78feab16f701f6edd75434f1" - integrity sha512-7iiOx3SG8+g1MnlzZVDYiaeHe7Ez2Kf2HrJzdmGwkRisT7r4rak0e655AcM/tF9JG/kg5fMNYlLLKglbN7gBqA== +"@shikijs/engine-oniguruma@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@shikijs/engine-oniguruma/-/engine-oniguruma-3.21.0.tgz#0e666454a03fd85d6c634d9dbe70a63f007a6323" + integrity sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ== dependencies: - "@shikijs/types" "1.29.2" - "@shikijs/vscode-textmate" "^10.0.1" + "@shikijs/types" "3.21.0" + "@shikijs/vscode-textmate" "^10.0.2" -"@shikijs/langs@1.29.2": - version "1.29.2" - resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-1.29.2.tgz#4f1de46fde8991468c5a68fa4a67dd2875d643cd" - integrity sha512-FIBA7N3LZ+223U7cJDUYd5shmciFQlYkFXlkKVaHsCPgfVLiO+e12FmQE6Tf9vuyEsFe3dIl8qGWKXgEHL9wmQ== +"@shikijs/langs@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@shikijs/langs/-/langs-3.21.0.tgz#da33400a85c7cba75fc9f4a6b9feb69a6c39c800" + integrity sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA== dependencies: - "@shikijs/types" "1.29.2" + "@shikijs/types" "3.21.0" -"@shikijs/themes@1.29.2": - version "1.29.2" - resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-1.29.2.tgz#293cc5c83dd7df3fdc8efa25cec8223f3a6acb0d" - integrity sha512-i9TNZlsq4uoyqSbluIcZkmPL9Bfi3djVxRnofUHwvx/h6SRW3cwgBC5SML7vsDcWyukY0eCzVN980rqP6qNl9g== +"@shikijs/themes@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@shikijs/themes/-/themes-3.21.0.tgz#1955d642ea37d70d1137e6cf47da7dc9c34ff4c0" + integrity sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw== dependencies: - "@shikijs/types" "1.29.2" + "@shikijs/types" "3.21.0" -"@shikijs/types@1.29.2": - version "1.29.2" - resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-1.29.2.tgz#a93fdb410d1af8360c67bf5fc1d1a68d58e21c4f" - integrity sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw== +"@shikijs/types@3.21.0": + version "3.21.0" + resolved "https://registry.yarnpkg.com/@shikijs/types/-/types-3.21.0.tgz#510d6ddbea65add27980a6ca36cc7bdabc7afe90" + integrity sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA== dependencies: - "@shikijs/vscode-textmate" "^10.0.1" + "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" -"@shikijs/vscode-textmate@^10.0.1": +"@shikijs/vscode-textmate@^10.0.2": version "10.0.2" resolved "https://registry.yarnpkg.com/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz#a90ab31d0cc1dfb54c66a69e515bf624fa7b2224" integrity sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg== @@ -710,12 +708,12 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== -"@types/node@^22.0.0": - version "22.19.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.19.7.tgz#434094ee1731ae76c16083008590a5835a8c39c1" - integrity sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw== +"@types/node@^25.0.10": + version "25.0.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.10.tgz#4864459c3c9459376b8b75fd051315071c8213e7" + integrity sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg== dependencies: - undici-types "~6.21.0" + undici-types "~7.16.0" "@types/parse-json@^4.0.0": version "4.0.2" @@ -1527,6 +1525,11 @@ bail@^2.0.0: resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== +baseline-browser-mapping@^2.8.3: + version "2.9.17" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz#9d6019766cd7eba738cb5f32c84b9f937cc87780" + integrity sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ== + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1629,11 +1632,6 @@ devlop@^1.0.0, devlop@^1.1.0: dependencies: dequal "^2.0.0" -emoji-regex-xs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz#e8af22e5d9dbd7f7f22d280af3d19d2aab5b0724" - integrity sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg== - entities@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.1.tgz#c28c34a43379ca7f61d074130b2f5f7020a30694" @@ -1829,7 +1827,7 @@ hast-util-to-estree@^3.0.0: unist-util-position "^5.0.0" zwitch "^2.0.0" -hast-util-to-html@^9.0.4: +hast-util-to-html@^9.0.5: version "9.0.5" resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz#ccc673a55bb8e85775b08ac28380f72d47167005" integrity sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw== @@ -2436,35 +2434,41 @@ next-themes@^0.4.0: resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.6.tgz#8d7e92d03b8fea6582892a50a928c9b23502e8b6" integrity sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA== -next@^15.0.0: - version "15.5.9" - resolved "https://registry.yarnpkg.com/next/-/next-15.5.9.tgz#1b80d05865cc27e710fb4dcfc6fd9e726ed12ad4" - integrity sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg== +next@^16.1.4: + version "16.1.4" + resolved "https://registry.yarnpkg.com/next/-/next-16.1.4.tgz#d024bace2d52a2bea1dec33149b8bbd0852632c5" + integrity sha512-gKSecROqisnV7Buen5BfjmXAm7Xlpx9o2ueVQRo5DxQcjC8d330dOM1xiGWc2k3Dcnz0In3VybyRPOsudwgiqQ== dependencies: - "@next/env" "15.5.9" + "@next/env" "16.1.4" "@swc/helpers" "0.5.15" + baseline-browser-mapping "^2.8.3" caniuse-lite "^1.0.30001579" postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "15.5.7" - "@next/swc-darwin-x64" "15.5.7" - "@next/swc-linux-arm64-gnu" "15.5.7" - "@next/swc-linux-arm64-musl" "15.5.7" - "@next/swc-linux-x64-gnu" "15.5.7" - "@next/swc-linux-x64-musl" "15.5.7" - "@next/swc-win32-arm64-msvc" "15.5.7" - "@next/swc-win32-x64-msvc" "15.5.7" - sharp "^0.34.3" - -oniguruma-to-es@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-2.3.0.tgz#35ea9104649b7c05f3963c6b3b474d964625028b" - integrity sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g== - dependencies: - emoji-regex-xs "^1.0.0" - regex "^5.1.1" - regex-recursion "^5.1.1" + "@next/swc-darwin-arm64" "16.1.4" + "@next/swc-darwin-x64" "16.1.4" + "@next/swc-linux-arm64-gnu" "16.1.4" + "@next/swc-linux-arm64-musl" "16.1.4" + "@next/swc-linux-x64-gnu" "16.1.4" + "@next/swc-linux-x64-musl" "16.1.4" + "@next/swc-win32-arm64-msvc" "16.1.4" + "@next/swc-win32-x64-msvc" "16.1.4" + sharp "^0.34.4" + +oniguruma-parser@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz#82ba2208d7a2b69ee344b7efe0ae930c627dcc4a" + integrity sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w== + +oniguruma-to-es@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz#0b909d960faeb84511c979b1f2af64e9bc37ce34" + integrity sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA== + dependencies: + oniguruma-parser "^0.12.1" + regex "^6.0.1" + regex-recursion "^6.0.2" pagefind@^1.3.0: version "1.4.0" @@ -2631,12 +2635,11 @@ recma-stringify@^1.0.0: unified "^11.0.0" vfile "^6.0.0" -regex-recursion@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/regex-recursion/-/regex-recursion-5.1.1.tgz#5a73772d18adbf00f57ad097bf54171b39d78f8b" - integrity sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w== +regex-recursion@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/regex-recursion/-/regex-recursion-6.0.2.tgz#a0b1977a74c87f073377b938dbedfab2ea582b33" + integrity sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg== dependencies: - regex "^5.1.1" regex-utilities "^2.3.0" regex-utilities@^2.3.0: @@ -2644,10 +2647,10 @@ regex-utilities@^2.3.0: resolved "https://registry.yarnpkg.com/regex-utilities/-/regex-utilities-2.3.0.tgz#87163512a15dce2908cf079c8960d5158ff43280" integrity sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng== -regex@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/regex/-/regex-5.1.1.tgz#cf798903f24d6fe6e531050a36686e082b29bd03" - integrity sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw== +regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/regex/-/regex-6.1.0.tgz#d7ce98f8ee32da7497c13f6601fca2bc4a6a7803" + integrity sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg== dependencies: regex-utilities "^2.3.0" @@ -2742,7 +2745,7 @@ semver@^7.7.3: resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== -sharp@^0.34.3: +sharp@^0.34.4: version "0.34.5" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0" integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg== @@ -2776,18 +2779,18 @@ sharp@^0.34.3: "@img/sharp-win32-ia32" "0.34.5" "@img/sharp-win32-x64" "0.34.5" -shiki@^1.24.0: - version "1.29.2" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.29.2.tgz#5c93771f2d5305ce9c05975c33689116a27dc657" - integrity sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg== - dependencies: - "@shikijs/core" "1.29.2" - "@shikijs/engine-javascript" "1.29.2" - "@shikijs/engine-oniguruma" "1.29.2" - "@shikijs/langs" "1.29.2" - "@shikijs/themes" "1.29.2" - "@shikijs/types" "1.29.2" - "@shikijs/vscode-textmate" "^10.0.1" +shiki@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-3.21.0.tgz#64686fe6bfc6b2b602d209eb6c8cdbc79d0eff22" + integrity sha512-N65B/3bqL/TI2crrXr+4UivctrAGEjmsib5rPMMPpFp1xAx/w03v8WZ9RDDFYteXoEgY7qZ4HGgl5KBIu1153w== + dependencies: + "@shikijs/core" "3.21.0" + "@shikijs/engine-javascript" "3.21.0" + "@shikijs/engine-oniguruma" "3.21.0" + "@shikijs/langs" "3.21.0" + "@shikijs/themes" "3.21.0" + "@shikijs/types" "3.21.0" + "@shikijs/vscode-textmate" "^10.0.2" "@types/hast" "^3.0.4" source-map-js@^1.0.2: @@ -2879,10 +2882,10 @@ typescript@^5.7.0: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== -undici-types@~6.21.0: - version "6.21.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" - integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== unified@^11.0.0, unified@^11.0.5: version "11.0.5" From cc41dd3fc0727827f36d3a81129a3e6d8b4f2cd0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 22:00:13 +0000 Subject: [PATCH 30/54] Fix search result URLs by stripping .html extension --- docs/components/docs/Search.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/components/docs/Search.tsx b/docs/components/docs/Search.tsx index 592d200..89116cc 100644 --- a/docs/components/docs/Search.tsx +++ b/docs/components/docs/Search.tsx @@ -102,8 +102,10 @@ export function Search() { const searchResults = await Promise.all( search.results.slice(0, 8).map(async (result) => { const data = await result.data() + // Strip .html extension from URLs since Next.js routes don't have them + const url = data.url.replace(/\.html$/, '') return { - url: data.url, + url, title: data.meta?.title || 'Untitled', excerpt: data.excerpt, } From 7235e7833cad07f042fae880aca2f4322184c481 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 22:27:06 +0000 Subject: [PATCH 31/54] Update landing page with all supported frameworks - Add React Router, Gatsby, and TanStack Router to frameworks grid - Add Debug Mode and Tree-Shakeable to features list - Simplify framework cards to link directly to docs - Update grid layouts for better responsive display --- docs/components/LandingPage.tsx | 107 +++++++++++++++++--------------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/docs/components/LandingPage.tsx b/docs/components/LandingPage.tsx index f0d160f..997abcf 100644 --- a/docs/components/LandingPage.tsx +++ b/docs/components/LandingPage.tsx @@ -22,24 +22,36 @@ const frameworks = [ icon: 'βš›οΈ', description: 'Drop-in provider and hooks for any React app', href: '/docs/react', - demos: [{ label: 'Live Demo', url: 'https://react.react-fathom.com' }], }, { name: 'Next.js', icon: 'β–²', description: 'App Router and Pages Router support with SSR handling', href: '/docs/nextjs', - demos: [ - { label: 'App Router', url: 'https://next-app.react-fathom.com' }, - { label: 'Pages Router', url: 'https://next-pages.react-fathom.com' }, - ], + }, + { + name: 'React Router', + icon: 'πŸ›€οΈ', + description: 'Automatic pageview tracking for React Router v6+ and Remix', + href: '/docs/react-router', + }, + { + name: 'Gatsby', + icon: '🏠', + description: 'Integration with @reach/router for Gatsby sites', + href: '/docs/gatsby', + }, + { + name: 'TanStack Router', + icon: '🧭', + description: 'Type-safe routing with automatic pageview tracking', + href: '/docs/tanstack-router', }, { name: 'React Native', icon: 'πŸ“±', description: 'Navigation tracking and app state handling for mobile', href: '/docs/react-native', - demos: [{ label: 'Live Demo', url: 'https://native.react-fathom.com' }], }, ] @@ -52,7 +64,7 @@ const features = [ { icon: '⚑', title: 'Lightweight', - description: 'Tiny bundle size with zero dependencies.', + description: 'Tree-shakeable with tiny bundle size.', }, { icon: 'πŸ“˜', @@ -64,6 +76,16 @@ const features = [ title: 'Simple API', description: 'Intuitive hooks and components that just work.', }, + { + icon: 'πŸ›', + title: 'Debug Mode', + description: 'Built-in event stream for development and testing.', + }, + { + icon: '🌳', + title: 'Tree-Shakeable', + description: 'Import only what you need for minimal bundle impact.', + }, ] const installCommand = 'npm install react-fathom' @@ -215,55 +237,40 @@ export function LandingPage() { {frameworks.map((framework) => ( - - - {framework.icon} - - - {framework.name} - - - {framework.description} - - - + - Documentation - - {framework.demos.map((demo, idx) => ( - - {demo.label} β†’ - - ))} - - + + {framework.icon} + + + {framework.name} + + + {framework.description} + + +
+ ))} @@ -283,7 +290,7 @@ export function LandingPage() { From 21cb90502295e08c789fa3dd1cf7cbe365ec819c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Jan 2026 22:35:57 +0000 Subject: [PATCH 32/54] Add react-fathom/debug subpackage with EventStream component - Create /debug subpackage for debugging utilities - Add EventStream component that displays tracked events in a panel - Export useDebugSubscription and debug types from /debug - Update all examples to use react-fathom/debug EventStream - Enable debug mode in all example sites --- examples/next-app/app/layout.tsx | 4 +- examples/next-pages/pages/_app.tsx | 4 +- examples/react/src/main.tsx | 12 +- package.json | 9 ++ rollup.config.js | 25 +++ src/debug/EventStream.tsx | 236 +++++++++++++++++++++++++++++ src/debug/index.ts | 7 + 7 files changed, 288 insertions(+), 9 deletions(-) create mode 100644 src/debug/EventStream.tsx create mode 100644 src/debug/index.ts diff --git a/examples/next-app/app/layout.tsx b/examples/next-app/app/layout.tsx index bed04e6..23019e0 100644 --- a/examples/next-app/app/layout.tsx +++ b/examples/next-app/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from 'next' import Link from 'next/link' import { NextFathomProviderApp } from 'react-fathom/next' +import { EventStream } from 'react-fathom/debug' import './globals.css' @@ -19,7 +20,7 @@ export default function RootLayout({ return ( - +