-
Welcome to react-fathom
-
Next.js App Router Example
-
-
-
- This is an example Next.js application using the App Router with{' '}
- react-fathom for analytics tracking.
-
+
+ Next.js App Router
+ Example
+
- Navigate between pages to see automatic pageview tracking in action.
- You can also use the buttons below to test event and goal tracking.
+ This example demonstrates how to integrate react-fathom into a Next.js
+ application using the App Router.
-
+
+
+
+
Try It Out
+
trackEvent('button_click')}
>
Track Event
trackGoal('EXAMPLE01', 100)}
>
- Track Goal
+ Track Goal ($1.00)
-
- Features Demonstrated
-
-
-
- Automatic pageview tracking - Pageviews are tracked
- automatically when you navigate between pages
-
-
- Event tracking - Use the "Track Event"
- button to manually track custom events
-
-
- Goal tracking - Use the "Track Goal"
- button to track goal conversions
-
-
- TypeScript support - Full type safety for all
- tracking methods
-
+
+
+
+
Features
+
+ Automatic pageview tracking on route changes
+ Custom event tracking
+ Goal conversion tracking
+ Debug mode with event visualization
+ Privacy-focused (no cookies)
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-app/package.json b/examples/next-app/package.json
index 7f40711..d11bac2 100644
--- a/examples/next-app/package.json
+++ b/examples/next-app/package.json
@@ -9,11 +9,12 @@
"lint": "next lint"
},
"dependencies": {
+ "@react-fathom/example-ui": "file:../shared",
+ "fathom-client": "^3.6.0",
"next": "^16.1.0",
"react": "^19.2.3",
"react-dom": "^19.2.3",
- "react-fathom": "file:../..",
- "fathom-client": "^3.6.0"
+ "react-fathom": "file:../.."
},
"devDependencies": {
"@types/node": "^22.0.0",
diff --git a/examples/next-app/vercel.json b/examples/next-app/vercel.json
new file mode 100644
index 0000000..bbfec8a
--- /dev/null
+++ b/examples/next-app/vercel.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://openapi.vercel.sh/vercel.json",
+ "framework": "nextjs",
+ "buildCommand": "cd ../.. && npm run build && cd examples/next-app && npm run build",
+ "installCommand": "cd ../.. && npm install && cd examples/shared && npm install && cd ../next-app && npm install"
+}
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/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"
- }
- }
-}
diff --git a/examples/next-pages/package.json b/examples/next-pages/package.json
index ae53057..3bf3284 100644
--- a/examples/next-pages/package.json
+++ b/examples/next-pages/package.json
@@ -9,11 +9,12 @@
"lint": "next lint"
},
"dependencies": {
+ "@react-fathom/example-ui": "file:../shared",
+ "fathom-client": "^3.6.0",
"next": "^16.1.0",
"react": "^19.2.3",
"react-dom": "^19.2.3",
- "react-fathom": "file:../..",
- "fathom-client": "^3.6.0"
+ "react-fathom": "file:../.."
},
"devDependencies": {
"@types/node": "^22.0.0",
diff --git a/examples/next-pages/pages/_app.tsx b/examples/next-pages/pages/_app.tsx
index 3735ec3..1116161 100644
--- a/examples/next-pages/pages/_app.tsx
+++ b/examples/next-pages/pages/_app.tsx
@@ -1,8 +1,10 @@
import type { AppProps } from 'next/app'
+import Head from 'next/head'
import Link from 'next/link'
import { FathomProvider } from 'react-fathom'
import { NextFathomTrackViewPages } from 'react-fathom/next'
+import { ExampleProviderNext, ExampleLayout } from '@react-fathom/example-ui/next'
import '../styles/globals.css'
@@ -10,35 +12,40 @@ export default function App({ Component, pageProps }: AppProps) {
const siteId = process.env.NEXT_PUBLIC_FATHOM_SITE_ID || 'DEMO'
return (
-
-
-
-
-
- react-fathom
-
-
- Home
- About
- Contact
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ Next.js Pages Router Example - react-fathom
+
+
+
+
+
+
+
)
}
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 (
+ <>
+ trackEvent('button-click')}>
+ Track Event
+
+ trackGoal('purchase', 2999)}>
+ Track Goal with Value
+
+ >
+ )
+}`}
+
+
+
+
+ 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`}
+
+
+
+
+
+
+ )
+}
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
+
+
+ handleTrackEvent('button-click')}
+ style={{
+ padding: '0.75rem 1.5rem',
+ background: '#0070f3',
+ color: '#fff',
+ border: 'none',
+ borderRadius: '0.5rem',
+ cursor: 'pointer',
+ fontSize: '1rem',
+ }}
+ >
+ Button Click
+
+ handleTrackEvent('signup-started')}
+ style={{
+ padding: '0.75rem 1.5rem',
+ background: '#0070f3',
+ color: '#fff',
+ border: 'none',
+ borderRadius: '0.5rem',
+ cursor: 'pointer',
+ fontSize: '1rem',
+ }}
+ >
+ Signup Started
+
+ handleTrackEvent('feature-used')}
+ style={{
+ padding: '0.75rem 1.5rem',
+ background: '#0070f3',
+ color: '#fff',
+ border: 'none',
+ borderRadius: '0.5rem',
+ cursor: 'pointer',
+ fontSize: '1rem',
+ }}
+ >
+ Feature Used
+
+
+
+
+
+ Goal Tracking
+
+ Track conversions with optional monetary values (in cents)
+
+
+ handleTrackGoal('signup-complete', 0)}
+ style={{
+ padding: '0.75rem 1.5rem',
+ background: '#10b981',
+ color: '#fff',
+ border: 'none',
+ borderRadius: '0.5rem',
+ cursor: 'pointer',
+ fontSize: '1rem',
+ }}
+ >
+ Signup Complete
+
+ handleTrackGoal('purchase', 2999)}
+ style={{
+ padding: '0.75rem 1.5rem',
+ background: '#10b981',
+ color: '#fff',
+ border: 'none',
+ borderRadius: '0.5rem',
+ cursor: 'pointer',
+ fontSize: '1rem',
+ }}
+ >
+ Purchase ($29.99)
+
+ handleTrackGoal('premium-upgrade', 9900)}
+ style={{
+ padding: '0.75rem 1.5rem',
+ background: '#10b981',
+ color: '#fff',
+ border: 'none',
+ borderRadius: '0.5rem',
+ cursor: 'pointer',
+ fontSize: '1rem',
+ }}
+ >
+ Premium ($99.00)
+
+
+
+
+
+ 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 */}
+ trackEvent('button-click')}>
+ Click Me
+
+
+ {/* Track a goal with monetary value (in cents) */}
+ trackGoal('purchase', 2999)}>
+ Buy Now - $29.99
+
+ >
+ )
+}`}
+
+
+
+
+ )
+}
diff --git a/examples/next-pages/pages/index.tsx b/examples/next-pages/pages/index.tsx
index dc1acad..042e630 100644
--- a/examples/next-pages/pages/index.tsx
+++ b/examples/next-pages/pages/index.tsx
@@ -3,83 +3,45 @@ import { useFathom } from 'react-fathom'
export default function Home() {
const { trackEvent, trackGoal } = useFathom()
- const handleButtonClick = () => {
- trackEvent('button-click')
- alert('Button click tracked! Check your Fathom Analytics dashboard.')
- }
-
- const handleGoalClick = () => {
- trackGoal('demo-signup', 0)
- alert('Goal tracked! Check your Fathom Analytics dashboard.')
- }
-
return (
-
Welcome to react-fathom
-
Next.js Pages Router Example
-
-
-
- This is an example Next.js application using the Pages Router with{' '}
- react-fathom for analytics tracking.
-
+
+ Next.js Pages Router
+ Example
+
- Navigate between pages to see automatic pageview tracking in action.
- You can also use the buttons below to test event and goal tracking.
+ This example demonstrates how to integrate react-fathom into a Next.js
+ application using the Pages Router.
-
+
+
+
+
Try It Out
+
trackEvent('button_click')}
>
Track Event
trackGoal('EXAMPLE01', 100)}
>
- Track Goal
+ Track Goal ($1.00)
-
- Features Demonstrated
-
-
-
- Automatic pageview tracking - Pageviews are tracked
- automatically when you navigate between pages
-
-
- Event tracking - Use the "Track Event"
- button to manually track custom events
-
-
- Goal tracking - Use the "Track Goal"
- button to track goal conversions
-
-
- TypeScript support - Full type safety for all
- tracking methods
-
+
+
+
+
Features
+
+ Automatic pageview tracking on route changes
+ Custom event tracking
+ Goal conversion tracking
+ Debug mode with event visualization
+ Privacy-focused (no cookies)
diff --git a/examples/next-pages/styles/globals.css b/examples/next-pages/styles/globals.css
index e49ff1c..b0727d3 100644
--- a/examples/next-pages/styles/globals.css
+++ b/examples/next-pages/styles/globals.css
@@ -16,7 +16,7 @@ body {
}
body {
- color: #333;
+ color: #1a1a1a;
background: #fff;
min-height: 100vh;
display: flex;
@@ -28,96 +28,220 @@ a {
text-decoration: none;
}
+a:hover {
+ opacity: 0.7;
+}
+
+/* Header */
.nav {
- border-bottom: 1px solid #e5e5e5;
- background: #fff;
- position: sticky;
- top: 0;
- z-index: 100;
+ padding: 1.5rem 0 1rem;
}
.nav-container {
- max-width: 1200px;
+ max-width: 640px;
margin: 0 auto;
- padding: 1rem 2rem;
+ padding: 0 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
}
+.nav-brand {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+}
+
.nav-logo {
- font-size: 1.25rem;
- font-weight: 600;
- color: #0070f3;
+ font-size: 0.875rem;
+ font-weight: 500;
+}
+
+.nav-framework {
+ font-size: 0.875rem;
+ color: #666;
}
.nav-links {
display: flex;
- gap: 2rem;
+ gap: 1.25rem;
}
.nav-links a {
+ font-size: 0.875rem;
color: #666;
transition: color 0.2s;
}
.nav-links a:hover {
- color: #0070f3;
+ color: #1a1a1a;
+ opacity: 1;
}
+/* Main */
.main {
flex: 1;
- max-width: 1200px;
+ max-width: 640px;
width: 100%;
margin: 0 auto;
- padding: 3rem 2rem;
+ padding: 2rem 1.5rem 3rem;
}
+/* Page Header */
.page-header {
- margin-bottom: 2rem;
+ margin-bottom: 2.5rem;
}
.page-header h1 {
- font-size: 2.5rem;
- margin-bottom: 0.5rem;
+ font-size: 2.75rem;
+ font-weight: 700;
+ line-height: 1.1;
+ margin-bottom: 1rem;
+}
+
+.page-header h1 span {
+ display: block;
+ color: #666;
}
.page-header p {
- font-size: 1.125rem;
+ font-size: 1rem;
color: #666;
+ max-width: 480px;
}
+/* Content */
.content {
- line-height: 1.75;
+ line-height: 1.6;
}
.content p {
margin-bottom: 1rem;
+ color: #666;
}
.content ul {
- margin-left: 2rem;
- margin-bottom: 1rem;
+ list-style: none;
+ margin: 0;
}
.content li {
- margin-bottom: 0.5rem;
+ margin-bottom: 0.75rem;
+ color: #666;
+}
+
+.content li::before {
+ content: "β ";
+}
+
+/* Section */
+.section {
+ border-top: 1px solid #e5e5e5;
+ padding-top: 2rem;
+ margin-top: 2.5rem;
+}
+
+.section h2 {
+ font-size: 1.25rem;
+ font-weight: 600;
+ margin-bottom: 1.5rem;
+}
+
+/* Buttons */
+.btn {
+ padding: 0.625rem 1.25rem;
+ border: none;
+ border-radius: 0.5rem;
+ cursor: pointer;
+ font-size: 0.875rem;
+ font-weight: 500;
+ transition: opacity 0.2s;
+}
+
+.btn:hover {
+ opacity: 0.8;
+}
+
+.btn-primary {
+ background: #1a1a1a;
+ color: #fff;
+}
+
+.btn-accent {
+ background: #E53935;
+ color: #fff;
+}
+
+.btn-group {
+ display: flex;
+ gap: 0.75rem;
+ margin-top: 1.5rem;
+}
+
+/* Feature list */
+.feature-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.feature-item h3 {
+ font-size: 1rem;
+ font-weight: 500;
+ margin-bottom: 0.25rem;
+}
+
+.feature-item p {
+ font-size: 0.875rem;
+ color: #666;
+ margin: 0;
}
+/* Footer */
.footer {
border-top: 1px solid #e5e5e5;
- background: #f9f9f9;
- padding: 2rem;
- text-align: center;
+ padding: 1.5rem 0 2rem;
+}
+
+.footer-container {
+ max-width: 640px;
+ margin: 0 auto;
+ padding: 0 1.5rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+
+.footer-copyright {
+ font-size: 0.75rem;
color: #666;
- font-size: 0.875rem;
}
-.footer a {
- color: #0070f3;
- text-decoration: underline;
+.footer-links {
+ display: flex;
+ gap: 1rem;
}
-.footer a:hover {
- text-decoration: none;
+.footer-links a {
+ font-size: 0.75rem;
+ color: #666;
+}
+
+.footer-links a:hover {
+ color: #1a1a1a;
+ opacity: 1;
+}
+
+/* Responsive */
+@media (max-width: 640px) {
+ .page-header h1 {
+ font-size: 2.25rem;
+ }
+
+ .footer-container {
+ flex-direction: column;
+ text-align: center;
+ }
}
diff --git a/examples/next-pages/vercel.json b/examples/next-pages/vercel.json
new file mode 100644
index 0000000..24ab35b
--- /dev/null
+++ b/examples/next-pages/vercel.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://openapi.vercel.sh/vercel.json",
+ "framework": "nextjs",
+ "buildCommand": "cd ../.. && npm run build && cd examples/next-pages && npm run build",
+ "installCommand": "cd ../.. && npm install && cd examples/shared && npm install && cd ../next-pages && npm install"
+}
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 (
+ trackEvent('signup-click')}
+ />
+ )
+}`}
+
+
+
+
+ {/* Navigation Tracking */}
+
+ Navigation Tracking
+
+ Track screen views with React Navigation:
+
+
+
+ {`import { useNavigationTracking } from 'react-fathom/native'
+
+function App() {
+ const navigationRef = useNavigationContainerRef()
+
+ useNavigationTracking(navigationRef)
+
+ return (
+
+ {/* screens */}
+
+ )
+}`}
+
+
+
+
+ {/* Offline Support */}
+
+ Offline Support
+
+ react-fathom automatically queues events when the device is offline and
+ sends them when connectivity is restored. No additional configuration needed.
+
+
+
+ {/* Environment Variables */}
+
+ Environment Variables
+
+ Store your site ID in an environment variable:
+
+
+
+ {'# .env\n'}
+ {'EXPO_PUBLIC_FATHOM_SITE_ID=YOUR_SITE_ID'}
+
+
+
+
+ {/* Links */}
+
+ Learn More
+ Linking.openURL('https://react-fathom.com/docs/react-native')}
+ >
+ Full Documentation β
+
+ Linking.openURL('https://react-fathom.com/docs/api/native')}
+ >
+ API Reference β
+
+ Linking.openURL('https://github.com/ryanhefner/react-fathom')}
+ >
+ GitHub Repository β
+
+
+
+ )
+}
+
+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,
+ },
+ codeBlock: {
+ backgroundColor: '#1a1a1a',
+ padding: 16,
+ borderRadius: 8,
+ },
+ code: {
+ fontFamily: 'monospace',
+ fontSize: 12,
+ color: '#e5e5e5',
+ },
+ link: {
+ fontSize: 16,
+ color: '#3b82f6',
+ marginBottom: 12,
+ },
+})
diff --git a/examples/react-native/app/events.tsx b/examples/react-native/app/events.tsx
new file mode 100644
index 0000000..386c795
--- /dev/null
+++ b/examples/react-native/app/events.tsx
@@ -0,0 +1,194 @@
+import { View, Text, StyleSheet, TouchableOpacity, ScrollView, TextInput } from 'react-native'
+import { useState } from 'react'
+import { useFathom } from 'react-fathom/native'
+
+export default function Events() {
+ const { trackEvent } = useFathom()
+ const [customEventName, setCustomEventName] = useState('custom-event')
+ const [eventCount, setEventCount] = useState(0)
+
+ const handleTrackEvent = (name: string, value?: number) => {
+ trackEvent(name, value ? { _value: value } : undefined)
+ setEventCount((c) => c + 1)
+ }
+
+ return (
+
+
+ Event Tracking Demo
+
+ Events tracked this session: {eventCount}
+
+
+
+ {/* Basic Event Tracking */}
+
+ Basic Event Tracking
+
+ Use useFathom() hook to track custom events
+
+
+ handleTrackEvent('button-click')}
+ >
+ Track "button-click"
+
+ handleTrackEvent('signup-intent')}
+ >
+ Track "signup-intent"
+
+
+
+
+ {'const { trackEvent } = useFathom()\n'}
+ {'trackEvent("button-click")'}
+
+
+
+
+ {/* Custom Event Name */}
+
+ Custom Event Name
+
+ Track any event name you want
+
+
+ handleTrackEvent(customEventName)}
+ >
+ Track Event
+
+
+
+ {/* Revenue Tracking */}
+
+ Revenue Tracking
+
+ Track events with monetary values (in cents)
+
+
+ handleTrackEvent('purchase', 1999)}
+ >
+ Purchase ($19.99)
+
+ handleTrackEvent('subscription', 9900)}
+ >
+ Subscription ($99)
+
+
+
+
+ {'trackEvent("purchase", { _value: 1999 })'}
+
+
+
+
+ {/* Offline Support */}
+
+ Offline Support
+
+ Events are automatically queued when offline and sent when the device
+ reconnects. Try enabling airplane mode and tracking some events!
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#fff',
+ },
+ header: {
+ padding: 24,
+ },
+ title: {
+ fontSize: 24,
+ fontWeight: 'bold',
+ marginBottom: 8,
+ },
+ subtitle: {
+ fontSize: 14,
+ color: '#666',
+ },
+ section: {
+ padding: 24,
+ borderTopWidth: 1,
+ borderTopColor: '#eee',
+ },
+ sectionTitle: {
+ fontSize: 18,
+ fontWeight: '600',
+ marginBottom: 8,
+ },
+ description: {
+ fontSize: 14,
+ color: '#666',
+ marginBottom: 16,
+ lineHeight: 20,
+ },
+ buttons: {
+ gap: 12,
+ marginBottom: 16,
+ },
+ button: {
+ padding: 16,
+ borderRadius: 8,
+ borderWidth: 1,
+ borderColor: '#ddd',
+ alignItems: 'center',
+ },
+ buttonPrimary: {
+ backgroundColor: '#3b82f6',
+ borderColor: '#3b82f6',
+ },
+ buttonPurple: {
+ backgroundColor: '#8b5cf6',
+ borderColor: '#8b5cf6',
+ },
+ buttonGreen: {
+ backgroundColor: '#22c55e',
+ borderColor: '#22c55e',
+ },
+ buttonText: {
+ fontSize: 16,
+ fontWeight: '500',
+ },
+ buttonTextPrimary: {
+ fontSize: 16,
+ fontWeight: '500',
+ color: '#fff',
+ },
+ input: {
+ borderWidth: 1,
+ borderColor: '#ddd',
+ borderRadius: 8,
+ padding: 12,
+ fontSize: 16,
+ marginBottom: 12,
+ },
+ codeBlock: {
+ backgroundColor: '#1a1a1a',
+ padding: 16,
+ borderRadius: 8,
+ },
+ code: {
+ fontFamily: 'monospace',
+ fontSize: 12,
+ color: '#e5e5e5',
+ },
+})
diff --git a/examples/react-native/app/index.tsx b/examples/react-native/app/index.tsx
new file mode 100644
index 0000000..8ac156f
--- /dev/null
+++ b/examples/react-native/app/index.tsx
@@ -0,0 +1,149 @@
+import { View, Text, StyleSheet, TouchableOpacity, ScrollView } from 'react-native'
+import { Link } from 'expo-router'
+
+export default function Home() {
+ return (
+
+
+ react-fathom
+ React Native Example
+
+ Privacy-focused analytics for React Native with offline support and navigation tracking.
+
+
+
+
+
+
+ Events Demo
+
+ Track custom events with the useFathom hook
+
+
+
+
+
+
+ Documentation
+
+ Learn how to integrate react-fathom in React Native
+
+
+
+
+
+
+ About
+
+ Learn about Fathom Analytics and privacy
+
+
+
+
+
+
+ Features
+
+ π±
+
+ React Native Support
+
+ Full support for iOS and Android apps
+
+
+
+
+ π‘
+
+ Offline Queue
+
+ Events queued when offline, sent when connected
+
+
+
+
+ π§
+
+ Navigation Tracking
+
+ Automatic screen tracking with React Navigation
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#fff',
+ },
+ hero: {
+ padding: 24,
+ alignItems: 'center',
+ },
+ title: {
+ fontSize: 32,
+ fontWeight: 'bold',
+ marginBottom: 8,
+ },
+ subtitle: {
+ fontSize: 18,
+ color: '#666',
+ marginBottom: 16,
+ },
+ description: {
+ fontSize: 16,
+ color: '#888',
+ textAlign: 'center',
+ lineHeight: 24,
+ },
+ cards: {
+ padding: 16,
+ gap: 12,
+ },
+ card: {
+ backgroundColor: '#f5f5f5',
+ padding: 20,
+ borderRadius: 12,
+ },
+ cardTitle: {
+ fontSize: 18,
+ fontWeight: '600',
+ marginBottom: 4,
+ },
+ cardDescription: {
+ fontSize: 14,
+ color: '#666',
+ },
+ features: {
+ padding: 24,
+ },
+ sectionTitle: {
+ fontSize: 20,
+ fontWeight: '600',
+ marginBottom: 16,
+ },
+ feature: {
+ flexDirection: 'row',
+ marginBottom: 16,
+ gap: 12,
+ },
+ featureIcon: {
+ fontSize: 24,
+ },
+ featureContent: {
+ flex: 1,
+ },
+ featureTitle: {
+ fontSize: 16,
+ fontWeight: '500',
+ marginBottom: 2,
+ },
+ featureDescription: {
+ fontSize: 14,
+ color: '#666',
+ },
+})
diff --git a/examples/react-native/package.json b/examples/react-native/package.json
new file mode 100644
index 0000000..6adee2f
--- /dev/null
+++ b/examples/react-native/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "react-fathom-react-native-example",
+ "version": "0.1.0",
+ "private": true,
+ "main": "expo-router/entry",
+ "scripts": {
+ "start": "expo start",
+ "android": "expo start --android",
+ "ios": "expo start --ios",
+ "web": "expo start --web"
+ },
+ "dependencies": {
+ "@react-navigation/native": "^7.0.0",
+ "@react-navigation/native-stack": "^7.0.0",
+ "expo": "~52.0.0",
+ "expo-router": "~4.0.0",
+ "expo-status-bar": "~2.0.0",
+ "react": "^19.0.0",
+ "react-native": "^0.76.0",
+ "react-native-safe-area-context": "^4.14.0",
+ "react-native-screens": "~4.4.0",
+ "react-native-webview": "^13.12.0",
+ "react-fathom": "file:../.."
+ },
+ "devDependencies": {
+ "@babel/core": "^7.25.0",
+ "@types/react": "^19.0.0",
+ "typescript": "^5.7.0"
+ }
+}
diff --git a/examples/react-native/tsconfig.json b/examples/react-native/tsconfig.json
new file mode 100644
index 0000000..ec96c84
--- /dev/null
+++ b/examples/react-native/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "expo/tsconfig.base",
+ "compilerOptions": {
+ "strict": true,
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ },
+ "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
+}
diff --git a/examples/react/.env.example b/examples/react/.env.example
new file mode 100644
index 0000000..58df8db
--- /dev/null
+++ b/examples/react/.env.example
@@ -0,0 +1,3 @@
+# Fathom Analytics Site ID
+# Get your site ID from https://app.usefathom.com β Settings β Site ID
+VITE_FATHOM_SITE_ID=
diff --git a/examples/react/.gitignore b/examples/react/.gitignore
new file mode 100644
index 0000000..b1640b5
--- /dev/null
+++ b/examples/react/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+dist
+.env
+.env.local
diff --git a/examples/react/README.md b/examples/react/README.md
new file mode 100644
index 0000000..53769df
--- /dev/null
+++ b/examples/react/README.md
@@ -0,0 +1,60 @@
+# react-fathom React Example
+
+A demonstration of integrating react-fathom into a standard React application using Vite and React Router.
+
+## Live Demo
+
+Visit [react.react-fathom.com](https://react.react-fathom.com) to see this example in action.
+
+## Features
+
+- **Automatic Pageview Tracking** β Track page views on React Router navigation
+- **Custom Event Tracking** β Track user interactions with `useFathom` hook
+- **Declarative Tracking** β Use `
` component for click tracking
+- **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
+ ```
+
+2. Install dependencies:
+ ```bash
+ npm install
+ ```
+
+3. Create a `.env` file with your Fathom site ID:
+ ```bash
+ cp .env.example .env
+ # Edit .env and add your site ID
+ ```
+
+4. Start the development server:
+ ```bash
+ npm run dev
+ ```
+
+5. Open [http://localhost:5173](http://localhost:5173)
+
+## Project Structure
+
+```
+src/
+βββ main.tsx # App entry with FathomProvider
+βββ App.tsx # Router setup
+βββ components/
+β βββ Layout.tsx # Layout with pageview tracking
+βββ pages/
+ βββ Home.tsx # Landing page
+ βββ About.tsx # About page
+ βββ Docs.tsx # Self-documenting docs
+ βββ Events.tsx # Event tracking demo
+```
+
+## Learn More
+
+- [react-fathom Documentation](https://react-fathom.com/docs)
+- [Fathom Analytics](https://usefathom.com/ref/EKONBS)
diff --git a/examples/react/index.html b/examples/react/index.html
new file mode 100644
index 0000000..7692159
--- /dev/null
+++ b/examples/react/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ React Example - react-fathom
+
+
+
+
+
+
diff --git a/examples/react/package.json b/examples/react/package.json
new file mode 100644
index 0000000..55fdc5c
--- /dev/null
+++ b/examples/react/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "react-fathom-react-example",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@react-fathom/example-ui": "file:../shared",
+ "fathom-client": "^3.6.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-fathom": "file:../..",
+ "react-router-dom": "^7.0.0"
+ },
+ "devDependencies": {
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "@vitejs/plugin-react": "^4.3.0",
+ "typescript": "^5.7.0",
+ "vite": "^6.0.0"
+ }
+}
diff --git a/examples/react/src/App.tsx b/examples/react/src/App.tsx
new file mode 100644
index 0000000..fd77c59
--- /dev/null
+++ b/examples/react/src/App.tsx
@@ -0,0 +1,19 @@
+import { Routes, Route } from 'react-router-dom'
+import { Layout } from './components/Layout'
+import { Home } from './pages/Home'
+import { About } from './pages/About'
+import { Docs } from './pages/Docs'
+import { Events } from './pages/Events'
+
+export default function App() {
+ return (
+
+ }>
+ } />
+ } />
+ } />
+ } />
+
+
+ )
+}
diff --git a/examples/react/src/components/Layout.tsx b/examples/react/src/components/Layout.tsx
new file mode 100644
index 0000000..31bd280
--- /dev/null
+++ b/examples/react/src/components/Layout.tsx
@@ -0,0 +1,21 @@
+import { Link as RouterLink, Outlet } from 'react-router-dom'
+import { ExampleLayoutSimple } from '@react-fathom/example-ui'
+
+const navLinks = [
+ { href: '/', label: 'Home' },
+ { href: '/about', label: 'About' },
+ { href: '/docs', label: 'Docs' },
+ { href: '/events', label: 'Events Demo' },
+]
+
+export function Layout() {
+ return (
+
+
+
+ )
+}
diff --git a/examples/react/src/main.tsx b/examples/react/src/main.tsx
new file mode 100644
index 0000000..7a947da
--- /dev/null
+++ b/examples/react/src/main.tsx
@@ -0,0 +1,22 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { BrowserRouter } from 'react-router-dom'
+import { FathomProvider } from 'react-fathom'
+import { ReactRouterFathomTrackView } from 'react-fathom/react-router'
+import { ExampleProvider } from '@react-fathom/example-ui'
+import App from './App'
+
+const siteId = import.meta.env.VITE_FATHOM_SITE_ID
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+
+
+
+
+
+
+
+)
diff --git a/examples/react/src/pages/About.tsx b/examples/react/src/pages/About.tsx
new file mode 100644
index 0000000..d38e9f4
--- /dev/null
+++ b/examples/react/src/pages/About.tsx
@@ -0,0 +1,50 @@
+import { Box, Heading, Text, VStack, Link } from '@chakra-ui/react'
+
+export function About() {
+ return (
+
+
+
+ About This Example
+
+
+ This is a demonstration of react-fathom integration in a React application.
+
+
+
+
+
+ Tech Stack
+
+
+ β’ Vite β Fast build tool and dev server
+ β’ React 19 β UI library
+ β’ React Router β Client-side routing
+ β’ Chakra UI β Component library
+ β’ react-fathom β Privacy-focused analytics
+
+
+
+
+
+ Why Fathom Analytics?
+
+
+ Fathom Analytics is a privacy-focused alternative to Google Analytics that:
+
+
+ β’ Doesn't use cookies β GDPR/CCPA compliant by default
+ β’ Respects user privacy β No personal data collection
+ β’ Lightweight β Won't slow down your site
+ β’ Simple β Easy to understand dashboard
+
+
+
+
+
+ Try Fathom Analytics (Get $10 credit) β
+
+
+
+ )
+}
diff --git a/examples/react/src/pages/Docs.tsx b/examples/react/src/pages/Docs.tsx
new file mode 100644
index 0000000..e1a1cb2
--- /dev/null
+++ b/examples/react/src/pages/Docs.tsx
@@ -0,0 +1,154 @@
+import { Box, Heading, Text, VStack, Code, Link } from '@chakra-ui/react'
+
+export function Docs() {
+ return (
+
+
+
+ Documentation
+
+
+ How to integrate react-fathom into your React application.
+
+
+
+ {/* Installation */}
+
+
+ Installation
+
+
+ $ npm install react-fathom fathom-client
+
+
+
+ {/* Setup */}
+
+
+ Setup
+
+
+ Wrap your application with FathomProvider:
+
+
+{`import { FathomProvider } from 'react-fathom'
+
+function App() {
+ return (
+
+
+
+ )
+}`}
+
+
+
+ {/* Pageview Tracking */}
+
+
+ Pageview Tracking
+
+
+ For React Router, track pageviews on route changes using the useFathom hook:
+
+
+{`import { useLocation } from 'react-router-dom'
+import { useFathom } from 'react-fathom'
+import { useEffect } from 'react'
+
+function Layout() {
+ const location = useLocation()
+ const { trackPageview } = useFathom()
+
+ useEffect(() => {
+ trackPageview()
+ }, [location.pathname, trackPageview])
+
+ return
+}`}
+
+
+
+ {/* Event Tracking */}
+
+
+ Event Tracking
+
+
+ Track custom events using the useFathom hook:
+
+
+{`import { useFathom } from 'react-fathom'
+
+function SignupButton() {
+ const { trackEvent } = useFathom()
+
+ return (
+ trackEvent('signup-click')}>
+ Sign Up
+
+ )
+}`}
+
+
+
+ {/* Declarative Tracking */}
+
+
+ Declarative Tracking
+
+
+ Use {''} for declarative event tracking:
+
+
+{`import { TrackClick } from 'react-fathom'
+
+function CTAButton() {
+ return (
+
+ Get Started
+
+ )
+}`}
+
+
+
+ {/* 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.
+
+
+ handleTrackEvent('button-click')}
+ >
+ Track "button-click"
+
+ handleTrackEvent('signup-intent')}
+ >
+ Track "signup-intent"
+
+
+
+ const
+ {'{ '}trackEvent {' }'} =
+ useFathom
+ ()
+ trackEvent ('button-click' )
+
+
+
+ {/* TrackClick Component */}
+
+
+ Using the {''} Component
+
+
+ Wrap any clickable element to track clicks declaratively.
+
+
+ setEventCount((c) => c + 1)}>
+
+ Tracked CTA Button
+
+
+ setEventCount((c) => c + 1)}>
+
+ Tracked Link Button
+
+
+
+
+ {'<'}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"
+ />
+ handleTrackEvent(customEventName)}
+ >
+ Track Event
+
+
+
+
+ {/* Event with Value */}
+
+
+ Events with Monetary Value
+
+
+ Track events with an associated value (in cents) for revenue tracking.
+
+
+ {
+ trackEvent('purchase', { _value: 1999 }) // $19.99
+ setEventCount((c) => c + 1)
+ }}
+ >
+ Track Purchase ($19.99)
+
+ {
+ trackEvent('subscription', { _value: 9900 }) // $99.00
+ setEventCount((c) => c + 1)
+ }}
+ >
+ Track Subscription ($99)
+
+
+
+ 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..9c2e940
--- /dev/null
+++ b/examples/react/src/pages/Home.tsx
@@ -0,0 +1,73 @@
+import { Box, Heading, Text, VStack, Link, Code } from '@chakra-ui/react'
+import { Link as RouterLink } from 'react-router-dom'
+
+export function Home() {
+ return (
+
+
+
+ React + Vite
+
+ Example
+
+
+
+ This example demonstrates how to integrate react-fathom into a standard React application
+ using Vite and React Router.
+
+
+
+
+
+ Features
+
+
+
+ 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/vercel.json b/examples/react/vercel.json
new file mode 100644
index 0000000..0aa5c4f
--- /dev/null
+++ b/examples/react/vercel.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://openapi.vercel.sh/vercel.json",
+ "framework": "vite",
+ "buildCommand": "cd ../.. && npm run build && cd examples/react && npm run build",
+ "installCommand": "cd ../.. && npm install && cd examples/shared && npm install && cd ../react && npm install",
+ "outputDirectory": "dist"
+}
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()],
+})
diff --git a/examples/shared/package.json b/examples/shared/package.json
new file mode 100644
index 0000000..0d272d9
--- /dev/null
+++ b/examples/shared/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "@react-fathom/example-ui",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "main": "./src/index.ts",
+ "types": "./src/index.ts",
+ "exports": {
+ ".": "./src/index.ts",
+ "./next": "./src/next.ts"
+ },
+ "dependencies": {
+ "@chakra-ui/react": "^3.2.0",
+ "@emotion/react": "^11.13.0",
+ "next-themes": "^0.4.0"
+ },
+ "peerDependencies": {
+ "react": ">=18.0.0",
+ "react-dom": ">=18.0.0"
+ }
+}
diff --git a/examples/shared/src/ColorModeButton.tsx b/examples/shared/src/ColorModeButton.tsx
new file mode 100644
index 0000000..8f46502
--- /dev/null
+++ b/examples/shared/src/ColorModeButton.tsx
@@ -0,0 +1,44 @@
+'use client'
+
+import { IconButton } from '@chakra-ui/react'
+import { useTheme } from 'next-themes'
+import { useEffect, useState } from 'react'
+
+/**
+ * A button that toggles between light and dark mode.
+ * Uses next-themes under the hood.
+ */
+export function ColorModeButton() {
+ const { theme, setTheme } = useTheme()
+ const [mounted, setMounted] = useState(false)
+
+ useEffect(() => {
+ setMounted(true)
+ }, [])
+
+ if (!mounted) {
+ return (
+
+ π
+
+ )
+ }
+
+ const isDark = theme === 'dark'
+
+ return (
+ setTheme(isDark ? 'light' : 'dark')}
+ >
+ {isDark ? 'βοΈ' : 'π'}
+
+ )
+}
diff --git a/examples/shared/src/ColorModeButtonSimple.tsx b/examples/shared/src/ColorModeButtonSimple.tsx
new file mode 100644
index 0000000..59384cf
--- /dev/null
+++ b/examples/shared/src/ColorModeButtonSimple.tsx
@@ -0,0 +1,23 @@
+'use client'
+
+import { IconButton } from '@chakra-ui/react'
+import { useColorMode } from './ColorModeContext'
+
+/**
+ * A button that toggles between light and dark mode.
+ * Uses the simple ColorModeContext (for non-Next.js apps).
+ */
+export function ColorModeButtonSimple() {
+ const { colorMode, toggleColorMode } = useColorMode()
+
+ return (
+
+ {colorMode === 'dark' ? 'βοΈ' : 'π'}
+
+ )
+}
diff --git a/examples/shared/src/ColorModeContext.tsx b/examples/shared/src/ColorModeContext.tsx
new file mode 100644
index 0000000..4aaf7c6
--- /dev/null
+++ b/examples/shared/src/ColorModeContext.tsx
@@ -0,0 +1,75 @@
+'use client'
+
+import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'
+
+type ColorMode = 'light' | 'dark'
+
+interface ColorModeContextValue {
+ colorMode: ColorMode
+ toggleColorMode: () => void
+}
+
+const ColorModeContext = createContext(null)
+
+const STORAGE_KEY = 'react-fathom-example-color-mode'
+
+function getInitialColorMode(): ColorMode {
+ if (typeof window === 'undefined') return 'light'
+
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY)
+ if (stored === 'dark' || stored === 'light') {
+ return stored
+ }
+ } catch {
+ // localStorage unavailable
+ }
+
+ // Check system preference
+ if (window.matchMedia?.('(prefers-color-scheme: dark)').matches) {
+ return 'dark'
+ }
+
+ return 'light'
+}
+
+export function ColorModeProvider({ children }: { children: ReactNode }) {
+ const [colorMode, setColorMode] = useState('light')
+ const [mounted, setMounted] = useState(false)
+
+ useEffect(() => {
+ setMounted(true)
+ setColorMode(getInitialColorMode())
+ }, [])
+
+ useEffect(() => {
+ if (!mounted) return
+
+ document.documentElement.classList.remove('light', 'dark')
+ document.documentElement.classList.add(colorMode)
+
+ try {
+ localStorage.setItem(STORAGE_KEY, colorMode)
+ } catch {
+ // localStorage unavailable
+ }
+ }, [colorMode, mounted])
+
+ const toggleColorMode = () => {
+ setColorMode((prev) => (prev === 'light' ? 'dark' : 'light'))
+ }
+
+ return (
+
+ {children}
+
+ )
+}
+
+export function useColorMode() {
+ const context = useContext(ColorModeContext)
+ if (!context) {
+ throw new Error('useColorMode must be used within a ColorModeProvider')
+ }
+ return context
+}
diff --git a/examples/shared/src/EventStreamPanel.tsx b/examples/shared/src/EventStreamPanel.tsx
new file mode 100644
index 0000000..5d797cc
--- /dev/null
+++ b/examples/shared/src/EventStreamPanel.tsx
@@ -0,0 +1,246 @@
+'use client'
+
+import {
+ Box,
+ Button,
+ Flex,
+ Heading,
+ HStack,
+ IconButton,
+ Text,
+ VStack,
+} from '@chakra-ui/react'
+import { useDebugSubscription, type DebugEvent } from 'react-fathom/debug'
+import { useState, useEffect } from 'react'
+
+const STORAGE_KEY = 'react-fathom-event-stream-visible'
+
+function formatTime(timestamp: number): string {
+ return new Date(timestamp).toLocaleTimeString('en-US', {
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ })
+}
+
+interface EventCardProps {
+ event: DebugEvent
+}
+
+function EventCard({ event }: EventCardProps) {
+ const colors = {
+ pageview: 'blue.500',
+ event: 'purple.500',
+ goal: 'green.500',
+ }
+
+ let title = ''
+ let subtitle = ''
+
+ switch (event.type) {
+ case 'pageview':
+ title = 'Pageview'
+ subtitle = event.url || (typeof window !== 'undefined' ? 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 interface EventStreamPanelProps {
+ /**
+ * Maximum number of events to display
+ * @default 20
+ */
+ maxEvents?: number
+ /**
+ * Position of the toggle button
+ * @default 'bottom-right'
+ */
+ position?: 'bottom-right' | 'bottom-left'
+ /**
+ * Initial visibility state (overridden by localStorage if available)
+ * @default false
+ */
+ defaultVisible?: boolean
+}
+
+/**
+ * A Chakra UI styled debug panel that displays tracked events in real-time.
+ * Only renders when debug mode is enabled in the FathomProvider.
+ */
+export function EventStreamPanel({
+ maxEvents = 20,
+ position = 'bottom-right',
+ defaultVisible = false,
+}: EventStreamPanelProps = {}) {
+ const [isVisible, setIsVisible] = useState(defaultVisible)
+ const [isHydrated, setIsHydrated] = useState(false)
+ const { events, debugEnabled, clearEvents } = useDebugSubscription({ maxEvents })
+
+ useEffect(() => {
+ setIsHydrated(true)
+ if (typeof window !== 'undefined') {
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY)
+ if (stored !== null) {
+ setIsVisible(stored === 'true')
+ }
+ } catch {
+ // localStorage unavailable
+ }
+ }
+ }, [])
+
+ 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)
+ }, [])
+
+ useEffect(() => {
+ if (isHydrated && typeof window !== 'undefined') {
+ try {
+ localStorage.setItem(STORAGE_KEY, String(isVisible))
+ } catch {
+ // localStorage unavailable
+ }
+ }
+ }, [isVisible, isHydrated])
+
+ if (!debugEnabled || !isHydrated) return null
+
+ const isRight = position === 'bottom-right'
+
+ return (
+ <>
+ {/* Toggle Button */}
+ setIsVisible(!isVisible)}
+ position="fixed"
+ bottom={4}
+ {...(isRight ? { right: 4 } : { left: 4 })}
+ zIndex={1000}
+ rounded="full"
+ size="lg"
+ colorPalette="purple"
+ boxShadow="lg"
+ >
+ {isVisible ? 'β' : 'π'}
+
+
+ {/* Panel */}
+ {isVisible && (
+
+ {/* Header */}
+
+ π Event Stream
+
+ Clear
+
+
+
+ {/* Event List */}
+
+ {events.length === 0 ? (
+
+ π
+
+ No events yet.
+
+ Navigate or interact to see tracking events.
+
+
+ ) : (
+ events.map((event) => )
+ )}
+
+
+ {/* Footer */}
+
+
+ {events.length} event{events.length !== 1 ? 's' : ''}
+ β’
+
+ β.
+
+ to toggle
+
+
+
+ )}
+ >
+ )
+}
diff --git a/examples/shared/src/ExampleLayout.tsx b/examples/shared/src/ExampleLayout.tsx
new file mode 100644
index 0000000..abb6015
--- /dev/null
+++ b/examples/shared/src/ExampleLayout.tsx
@@ -0,0 +1,163 @@
+'use client'
+
+import { Box, Container, Flex, HStack, Link, Text } from '@chakra-ui/react'
+import type { ComponentType, ReactNode } from 'react'
+import { ColorModeButton } from './ColorModeButton'
+import { EventStreamPanel } from './EventStreamPanel'
+
+export interface NavLink {
+ href: string
+ label: string
+}
+
+export interface ExampleLayoutProps {
+ children: ReactNode
+ /**
+ * The Link component to use for navigation.
+ * Pass Next.js Link or React Router Link component.
+ */
+ linkComponent: ComponentType<{ href: string; children: ReactNode; className?: string }>
+ /**
+ * Navigation links to display in the header.
+ */
+ navLinks?: NavLink[]
+ /**
+ * The title/brand shown in the header.
+ * @default 'react-fathom'
+ */
+ title?: string
+ /**
+ * Show the color mode toggle button.
+ * @default true
+ */
+ showColorModeButton?: boolean
+ /**
+ * Show the debug EventStream panel.
+ * @default true
+ */
+ showEventStream?: boolean
+ /**
+ * The framework name shown in the footer.
+ */
+ frameworkName?: string
+}
+
+const defaultNavLinks: NavLink[] = [
+ { href: '/', label: 'Home' },
+ { href: '/about', label: 'About' },
+ { href: '/events', label: 'Events' },
+ { href: '/contact', label: 'Contact' },
+]
+
+/**
+ * Shared layout component for example sites.
+ * Minimal, content-focused design.
+ */
+export function ExampleLayout({
+ children,
+ linkComponent: LinkComponent,
+ navLinks = defaultNavLinks,
+ title = 'react-fathom',
+ showColorModeButton = true,
+ showEventStream = true,
+ frameworkName,
+}: ExampleLayoutProps) {
+ return (
+ <>
+
+ {/* Header */}
+
+
+
+
+
+ {title}
+
+ {frameworkName && (
+
+ β {frameworkName}
+
+ )}
+
+
+
+ {navLinks.map((link) => (
+
+ {link.label}
+
+ ))}
+ {showColorModeButton && }
+
+
+
+
+
+ {/* Main Content */}
+
+
+ {children}
+
+
+
+ {/* Footer */}
+
+
+
+
+ Β© {new Date().getFullYear()} β{' '}
+
+ react-fathom
+
+
+
+
+ Docs
+
+
+ GitHub
+
+
+ Fathom
+
+
+
+
+
+
+ {showEventStream && }
+ >
+ )
+}
diff --git a/examples/shared/src/ExampleLayoutSimple.tsx b/examples/shared/src/ExampleLayoutSimple.tsx
new file mode 100644
index 0000000..9b12ba4
--- /dev/null
+++ b/examples/shared/src/ExampleLayoutSimple.tsx
@@ -0,0 +1,163 @@
+'use client'
+
+import { Box, Container, Flex, HStack, Link, Text } from '@chakra-ui/react'
+import { EventStreamPanel } from './EventStreamPanel'
+import type { ComponentType, ReactNode } from 'react'
+import { ColorModeButtonSimple } from './ColorModeButtonSimple'
+
+export interface NavLink {
+ href: string
+ label: string
+}
+
+export interface ExampleLayoutSimpleProps {
+ children: ReactNode
+ /**
+ * The Link component to use for navigation.
+ * Pass React Router's Link component.
+ */
+ linkComponent: ComponentType<{ to: string; children: ReactNode; className?: string }>
+ /**
+ * Navigation links to display in the header.
+ */
+ navLinks?: NavLink[]
+ /**
+ * The title/brand shown in the header.
+ * @default 'react-fathom'
+ */
+ title?: string
+ /**
+ * Show the color mode toggle button.
+ * @default true
+ */
+ showColorModeButton?: boolean
+ /**
+ * Show the debug EventStream panel.
+ * @default true
+ */
+ showEventStream?: boolean
+ /**
+ * The framework name shown in the footer.
+ */
+ frameworkName?: string
+}
+
+const defaultNavLinks: NavLink[] = [
+ { href: '/', label: 'Home' },
+ { href: '/about', label: 'About' },
+ { href: '/events', label: 'Events' },
+ { href: '/contact', label: 'Contact' },
+]
+
+/**
+ * Shared layout component for example sites using React Router.
+ * Minimal, content-focused design.
+ */
+export function ExampleLayoutSimple({
+ children,
+ linkComponent: LinkComponent,
+ navLinks = defaultNavLinks,
+ title = 'react-fathom',
+ showColorModeButton = true,
+ showEventStream = true,
+ frameworkName,
+}: ExampleLayoutSimpleProps) {
+ return (
+ <>
+
+ {/* Header */}
+
+
+
+
+
+ {title}
+
+ {frameworkName && (
+
+ β {frameworkName}
+
+ )}
+
+
+
+ {navLinks.map((link) => (
+
+ {link.label}
+
+ ))}
+ {showColorModeButton && }
+
+
+
+
+
+ {/* Main Content */}
+
+
+ {children}
+
+
+
+ {/* Footer */}
+
+
+
+
+ Β© {new Date().getFullYear()} β{' '}
+
+ react-fathom
+
+
+
+
+ Docs
+
+
+ GitHub
+
+
+ Fathom
+
+
+
+
+
+
+ {showEventStream && }
+ >
+ )
+}
diff --git a/examples/shared/src/ExampleProvider.tsx b/examples/shared/src/ExampleProvider.tsx
new file mode 100644
index 0000000..ae3b535
--- /dev/null
+++ b/examples/shared/src/ExampleProvider.tsx
@@ -0,0 +1,22 @@
+'use client'
+
+import { ChakraProvider, defaultSystem } from '@chakra-ui/react'
+import type { ReactNode } from 'react'
+import { ColorModeProvider } from './ColorModeContext'
+
+export interface ExampleProviderProps {
+ children: ReactNode
+}
+
+/**
+ * Provider for non-Next.js example sites (React, Vite, etc.).
+ * Wraps children with Chakra UI and simple color mode support.
+ * For Next.js apps, use ExampleProviderNext which uses next-themes.
+ */
+export function ExampleProvider({ children }: ExampleProviderProps) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/examples/shared/src/ExampleProviderNext.tsx b/examples/shared/src/ExampleProviderNext.tsx
new file mode 100644
index 0000000..f418c60
--- /dev/null
+++ b/examples/shared/src/ExampleProviderNext.tsx
@@ -0,0 +1,23 @@
+'use client'
+
+import { ChakraProvider, defaultSystem } from '@chakra-ui/react'
+import { ThemeProvider } from 'next-themes'
+import type { ReactNode } from 'react'
+
+export interface ExampleProviderNextProps {
+ children: ReactNode
+}
+
+/**
+ * Provider for Next.js example sites.
+ * Wraps children with Chakra UI and next-themes for color mode support.
+ */
+export function ExampleProviderNext({ children }: ExampleProviderNextProps) {
+ return (
+
+
+ {children}
+
+
+ )
+}
diff --git a/examples/shared/src/index.ts b/examples/shared/src/index.ts
new file mode 100644
index 0000000..2330f9a
--- /dev/null
+++ b/examples/shared/src/index.ts
@@ -0,0 +1,11 @@
+export { ExampleProvider } from './ExampleProvider'
+export type { ExampleProviderProps } from './ExampleProvider'
+
+export { ExampleLayout } from './ExampleLayout'
+export type { ExampleLayoutProps, NavLink } from './ExampleLayout'
+
+export { ExampleLayoutSimple } from './ExampleLayoutSimple'
+export type { ExampleLayoutSimpleProps } from './ExampleLayoutSimple'
+
+export { ColorModeProvider, useColorMode } from './ColorModeContext'
+export { ColorModeButtonSimple } from './ColorModeButtonSimple'
diff --git a/examples/shared/src/next.ts b/examples/shared/src/next.ts
new file mode 100644
index 0000000..35fe082
--- /dev/null
+++ b/examples/shared/src/next.ts
@@ -0,0 +1,7 @@
+export { ExampleProviderNext } from './ExampleProviderNext'
+export type { ExampleProviderNextProps } from './ExampleProviderNext'
+
+export { ColorModeButton } from './ColorModeButton'
+
+export { ExampleLayout } from './ExampleLayout'
+export type { ExampleLayoutProps, NavLink } from './ExampleLayout'
diff --git a/examples/tanstack-router/.env.example b/examples/tanstack-router/.env.example
new file mode 100644
index 0000000..bc5d2ad
--- /dev/null
+++ b/examples/tanstack-router/.env.example
@@ -0,0 +1,5 @@
+# Get your site ID from https://usefathom.com
+VITE_FATHOM_SITE_ID=YOUR_SITE_ID
+
+# Optional: Enable debug mode (enabled by default in this example)
+# VITE_FATHOM_DEBUG=true
diff --git a/examples/tanstack-router/.gitignore b/examples/tanstack-router/.gitignore
new file mode 100644
index 0000000..9c97bbd
--- /dev/null
+++ b/examples/tanstack-router/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+dist
+.env
diff --git a/examples/tanstack-router/index.html b/examples/tanstack-router/index.html
new file mode 100644
index 0000000..e0294cb
--- /dev/null
+++ b/examples/tanstack-router/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TanStack Router Example - react-fathom
+
+
+
+
+
+
diff --git a/examples/tanstack-router/package.json b/examples/tanstack-router/package.json
new file mode 100644
index 0000000..d2ed1c4
--- /dev/null
+++ b/examples/tanstack-router/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "react-fathom-tanstack-router-example",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@react-fathom/example-ui": "file:../shared",
+ "@tanstack/react-router": "^1.120.3",
+ "fathom-client": "^3.6.0",
+ "react": "^19.0.0",
+ "react-dom": "^19.0.0",
+ "react-fathom": "file:../.."
+ },
+ "devDependencies": {
+ "@tanstack/router-plugin": "^1.120.3",
+ "@types/react": "^19.0.0",
+ "@types/react-dom": "^19.0.0",
+ "@vitejs/plugin-react": "^4.3.0",
+ "typescript": "^5.7.0",
+ "vite": "^6.0.0"
+ }
+}
diff --git a/examples/tanstack-router/src/main.tsx b/examples/tanstack-router/src/main.tsx
new file mode 100644
index 0000000..ecc7b67
--- /dev/null
+++ b/examples/tanstack-router/src/main.tsx
@@ -0,0 +1,24 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { RouterProvider, createRouter } from '@tanstack/react-router'
+import { ExampleProvider } from '@react-fathom/example-ui'
+
+import { routeTree } from './routeTree.gen'
+
+// Create a new router instance
+const router = createRouter({ routeTree })
+
+// Register the router instance for type safety
+declare module '@tanstack/react-router' {
+ interface Register {
+ router: typeof router
+ }
+}
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+
+
+
+)
diff --git a/examples/tanstack-router/src/routes/__root.tsx b/examples/tanstack-router/src/routes/__root.tsx
new file mode 100644
index 0000000..5fda3b5
--- /dev/null
+++ b/examples/tanstack-router/src/routes/__root.tsx
@@ -0,0 +1,25 @@
+import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
+import { FathomProvider } from 'react-fathom'
+import { TanStackRouterFathomTrackView } from 'react-fathom/tanstack-router'
+import { ExampleLayoutSimple } from '@react-fathom/example-ui'
+
+const siteId = import.meta.env.VITE_FATHOM_SITE_ID || 'DEMO'
+
+// Custom Link component for ExampleLayoutSimple
+function NavLink({ to, children }: { to: string; children: React.ReactNode }) {
+ return {children}
+}
+
+export const Route = createRootRoute({
+ component: () => (
+
+
+
+
+
+
+ ),
+})
diff --git a/examples/tanstack-router/src/routes/about.tsx b/examples/tanstack-router/src/routes/about.tsx
new file mode 100644
index 0000000..23dac33
--- /dev/null
+++ b/examples/tanstack-router/src/routes/about.tsx
@@ -0,0 +1,37 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { Box, Heading, Text, VStack } from '@chakra-ui/react'
+
+export const Route = createFileRoute('/about')({
+ component: AboutPage,
+})
+
+function AboutPage() {
+ return (
+
+
+
+ About
+
+
+ This page demonstrates automatic pageview tracking. When you navigate
+ here, react-fathom automatically tracks the pageview.
+
+
+
+
+
+ How It Works
+
+
+ The TanStackRouterFathomTrackView component listens for
+ route changes and automatically sends pageview events to Fathom
+ Analytics.
+
+
+ Check the debug panel in the bottom-right corner to see the events
+ being tracked in real-time.
+
+
+
+ )
+}
diff --git a/examples/tanstack-router/src/routes/contact.tsx b/examples/tanstack-router/src/routes/contact.tsx
new file mode 100644
index 0000000..9b8b31a
--- /dev/null
+++ b/examples/tanstack-router/src/routes/contact.tsx
@@ -0,0 +1,57 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { Box, Heading, Text, VStack, Input, Button } from '@chakra-ui/react'
+import { useFathom } from 'react-fathom'
+
+export const Route = createFileRoute('/contact')({
+ component: ContactPage,
+})
+
+function ContactPage() {
+ const { trackEvent } = useFathom()
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault()
+ trackEvent('contact_form_submit')
+ alert('Form submitted! Check the debug panel to see the tracked event.')
+ }
+
+ return (
+
+
+
+ Contact
+
+
+ This page demonstrates form tracking. Submit the form to see custom
+ event tracking in action.
+
+
+
+
+
+
+
+ Name
+
+
+
+
+
+ Email
+
+
+
+
+
+ Message
+
+
+
+
+ Send Message
+
+
+
+
+ )
+}
diff --git a/examples/tanstack-router/src/routes/index.tsx b/examples/tanstack-router/src/routes/index.tsx
new file mode 100644
index 0000000..71384d4
--- /dev/null
+++ b/examples/tanstack-router/src/routes/index.tsx
@@ -0,0 +1,66 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { Box, Heading, Text, VStack, Button, HStack } from '@chakra-ui/react'
+import { useFathom } from 'react-fathom'
+
+export const Route = createFileRoute('/')({
+ component: HomePage,
+})
+
+function HomePage() {
+ const { trackEvent, trackGoal } = useFathom()
+
+ return (
+
+
+
+ TanStack Router
+
+ Example
+
+
+
+ This example demonstrates the TanStack Router integration with
+ react-fathom for privacy-focused analytics.
+
+
+
+
+
+ Try It Out
+
+
+ trackEvent('button_click')}
+ >
+ Track Event
+
+ trackGoal('EXAMPLE01', 100)}
+ >
+ Track Goal ($1.00)
+
+
+
+
+
+
+ Features
+
+
+ β Automatic pageview tracking on route changes
+ β Custom event tracking
+ β Goal conversion tracking
+ β Debug mode with event visualization
+ β Privacy-focused (no cookies)
+
+
+
+ )
+}
diff --git a/examples/tanstack-router/tsconfig.json b/examples/tanstack-router/tsconfig.json
new file mode 100644
index 0000000..0426f7b
--- /dev/null
+++ b/examples/tanstack-router/tsconfig.json
@@ -0,0 +1,20 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "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/tanstack-router/vercel.json b/examples/tanstack-router/vercel.json
new file mode 100644
index 0000000..ff3a0be
--- /dev/null
+++ b/examples/tanstack-router/vercel.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://openapi.vercel.sh/vercel.json",
+ "framework": "vite",
+ "buildCommand": "cd ../.. && npm run build && cd examples/tanstack-router && npm run build",
+ "installCommand": "cd ../.. && npm install && cd examples/shared && npm install && cd ../tanstack-router && npm install",
+ "outputDirectory": "dist"
+}
diff --git a/examples/tanstack-router/vite.config.ts b/examples/tanstack-router/vite.config.ts
new file mode 100644
index 0000000..74ab169
--- /dev/null
+++ b/examples/tanstack-router/vite.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
+
+export default defineConfig({
+ plugins: [TanStackRouterVite(), react()],
+})
diff --git a/package.json b/package.json
index 37cd172..860c6b1 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,10 @@
"react",
"react-native",
"react-hooks",
+ "react-router",
+ "remix",
+ "gatsby",
+ "tanstack-router",
"nextjs",
"next.js",
"typescript",
@@ -86,11 +90,23 @@
"sideEffects": false,
"typesVersions": {
"*": {
+ "debug": [
+ "./types/debug/index.d.ts"
+ ],
"next": [
"./types/next/index.d.ts"
],
"native": [
"./types/native/index.d.ts"
+ ],
+ "react-router": [
+ "./types/react-router/index.d.ts"
+ ],
+ "gatsby": [
+ "./types/gatsby/index.d.ts"
+ ],
+ "tanstack-router": [
+ "./types/tanstack-router/index.d.ts"
]
}
},
@@ -101,6 +117,12 @@
"require": "./dist/cjs/index.cjs",
"default": "./dist/es/index.js"
},
+ "./debug": {
+ "types": "./types/debug/index.d.ts",
+ "import": "./dist/es/debug/index.js",
+ "require": "./dist/cjs/debug/index.cjs",
+ "default": "./dist/es/debug/index.js"
+ },
"./next": {
"types": "./types/next/index.d.ts",
"import": "./dist/es/next/index.js",
@@ -112,6 +134,24 @@
"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"
+ },
+ "./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"
+ },
+ "./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": {
@@ -126,17 +166,32 @@
"push-release": "git push origin master && git push --tags",
"tslint": "npx eslint types/index.d.ts",
"test": "snyk test && vitest run",
- "test:ci": "vitest run --coverage"
+ "test:ci": "vitest run --coverage",
+ "dev:all": "node scripts/dev-all.js",
+ "dev:proxy": "node scripts/dev-all.js --proxy"
},
"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",
"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": {
+ "@reach/router": {
+ "optional": true
+ },
+ "@tanstack/react-router": {
+ "optional": true
+ },
+ "gatsby": {
+ "optional": true
+ },
"next": {
"optional": true
},
@@ -146,6 +201,9 @@
"react-native-webview": {
"optional": true
},
+ "react-router-dom": {
+ "optional": true
+ },
"fathom-client": {
"optional": true
}
@@ -160,13 +218,16 @@
"@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",
+ "@types/reach__router": "^1.3.15",
"@types/react": "^19.2.7",
"@typescript-eslint/eslint-plugin": "^8.50.0",
"@vitest/coverage-v8": "^4.0.16",
@@ -186,6 +247,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..4bd048c 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -138,10 +138,17 @@ const nextExternal = [
'next/navigation',
]
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 debugInput = 'src/debug/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
@@ -190,6 +197,30 @@ export default [
external: makeExternal(external),
plugins: defaultPlugins,
},
+ // ES - debug
+ {
+ input: debugInput,
+ output: {
+ ...defaultOutputOptions,
+ dir: 'dist/es/debug',
+ format: 'esm',
+ entryFileNames: '[name].js',
+ },
+ external: makeExternal(external),
+ plugins: defaultPlugins,
+ },
+ // CJS - debug
+ {
+ input: debugInput,
+ output: {
+ ...defaultOutputOptions,
+ dir: 'dist/cjs/debug',
+ format: 'cjs',
+ entryFileNames: '[name].cjs',
+ },
+ external: makeExternal(external),
+ plugins: defaultPlugins,
+ },
// ES - next
{
input: nextInput,
@@ -238,4 +269,76 @@ 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,
+ },
+ // 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,
+ },
+ // 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/scripts/dev-all.js b/scripts/dev-all.js
new file mode 100644
index 0000000..9f6afb4
--- /dev/null
+++ b/scripts/dev-all.js
@@ -0,0 +1,175 @@
+#!/usr/bin/env node
+
+/**
+ * Development script to run docs and all example sites simultaneously
+ *
+ * Sites and their ports:
+ * - docs: http://localhost:3000 or http://docs.localhost:8080
+ * - next-app: http://localhost:3001 or http://next-app.localhost:8080
+ * - next-pages: http://localhost:3002 or http://next-pages.localhost:8080
+ * - react: http://localhost:3003 or http://react.localhost:8080
+ * - tanstack: http://localhost:3004 or http://tanstack.localhost:8080
+ * - gatsby: http://localhost:3005 or http://gatsby.localhost:8080
+ *
+ * Usage:
+ * node scripts/dev-all.js # Run all sites
+ * node scripts/dev-all.js --proxy # Run all sites with subdomain proxy on port 8080
+ */
+
+import { spawn } from 'child_process'
+import { fileURLToPath } from 'url'
+import path from 'path'
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
+const rootDir = path.resolve(__dirname, '..')
+
+const useProxy = process.argv.includes('--proxy')
+
+// Site configurations
+const sites = [
+ {
+ name: 'docs',
+ dir: 'docs',
+ command: 'npm',
+ args: ['run', 'dev', '--', '-p', '3000'],
+ port: 3000,
+ color: '\x1b[36m', // cyan
+ },
+ {
+ name: 'next-app',
+ dir: 'examples/next-app',
+ command: 'npm',
+ args: ['run', 'dev', '--', '-p', '3001'],
+ port: 3001,
+ color: '\x1b[33m', // yellow
+ },
+ {
+ name: 'next-pages',
+ dir: 'examples/next-pages',
+ command: 'npm',
+ args: ['run', 'dev', '--', '-p', '3002'],
+ port: 3002,
+ color: '\x1b[35m', // magenta
+ },
+ {
+ name: 'react',
+ dir: 'examples/react',
+ command: 'npm',
+ args: ['run', 'dev', '--', '--port', '3003'],
+ port: 3003,
+ color: '\x1b[32m', // green
+ },
+ {
+ name: 'tanstack',
+ dir: 'examples/tanstack-router',
+ command: 'npm',
+ args: ['run', 'dev', '--', '--port', '3004'],
+ port: 3004,
+ color: '\x1b[34m', // blue
+ },
+ {
+ name: 'gatsby',
+ dir: 'examples/gatsby',
+ command: 'npm',
+ args: ['run', 'develop', '--', '-p', '3005'],
+ port: 3005,
+ color: '\x1b[31m', // red
+ },
+]
+
+const reset = '\x1b[0m'
+
+console.log('\nπ¦ Starting react-fathom development servers...\n')
+
+if (useProxy) {
+ console.log('π Subdomain proxy enabled on port 8080')
+ console.log(' Access sites via: .localhost:8080\n')
+} else {
+ console.log(' Use --proxy flag to enable subdomain routing on port 8080\n')
+}
+
+console.log('Sites:')
+sites.forEach(site => {
+ const url = useProxy
+ ? `http://${site.name}.localhost:8080`
+ : `http://localhost:${site.port}`
+ console.log(` ${site.color}${site.name.padEnd(12)}${reset} β ${url}`)
+})
+console.log('')
+
+// Track child processes for cleanup
+const processes = []
+
+// Cleanup function
+function cleanup() {
+ console.log('\n\nShutting down all servers...')
+ processes.forEach(proc => {
+ if (proc && !proc.killed) {
+ proc.kill('SIGTERM')
+ }
+ })
+ process.exit(0)
+}
+
+process.on('SIGINT', cleanup)
+process.on('SIGTERM', cleanup)
+
+// Start each site
+sites.forEach(site => {
+ const cwd = path.join(rootDir, site.dir)
+ const proc = spawn(site.command, site.args, {
+ cwd,
+ stdio: ['ignore', 'pipe', 'pipe'],
+ shell: true,
+ })
+
+ processes.push(proc)
+
+ const prefix = `${site.color}[${site.name}]${reset}`
+
+ proc.stdout.on('data', data => {
+ const lines = data.toString().split('\n').filter(Boolean)
+ lines.forEach(line => console.log(`${prefix} ${line}`))
+ })
+
+ proc.stderr.on('data', data => {
+ const lines = data.toString().split('\n').filter(Boolean)
+ lines.forEach(line => console.log(`${prefix} ${line}`))
+ })
+
+ proc.on('error', err => {
+ console.error(`${prefix} Failed to start: ${err.message}`)
+ })
+
+ proc.on('exit', (code, signal) => {
+ if (code !== null && code !== 0) {
+ console.log(`${prefix} Exited with code ${code}`)
+ }
+ })
+})
+
+// Start proxy if requested
+if (useProxy) {
+ const proxyPath = path.join(__dirname, 'subdomain-proxy.js')
+ const proxyProc = spawn('node', [proxyPath], {
+ cwd: rootDir,
+ stdio: ['ignore', 'pipe', 'pipe'],
+ })
+
+ processes.push(proxyProc)
+
+ const prefix = '\x1b[37m[proxy]\x1b[0m'
+
+ proxyProc.stdout.on('data', data => {
+ const lines = data.toString().split('\n').filter(Boolean)
+ lines.forEach(line => console.log(`${prefix} ${line}`))
+ })
+
+ proxyProc.stderr.on('data', data => {
+ const lines = data.toString().split('\n').filter(Boolean)
+ lines.forEach(line => console.log(`${prefix} ${line}`))
+ })
+}
+
+// Keep process running
+process.stdin.resume()
diff --git a/scripts/subdomain-proxy.js b/scripts/subdomain-proxy.js
new file mode 100644
index 0000000..1aaeda7
--- /dev/null
+++ b/scripts/subdomain-proxy.js
@@ -0,0 +1,166 @@
+#!/usr/bin/env node
+
+/**
+ * Simple subdomain proxy for local development
+ *
+ * Routes requests based on subdomain:
+ * docs.localhost:8080 β localhost:3000
+ * next-app.localhost:8080 β localhost:3001
+ * next-pages.localhost:8080 β localhost:3002
+ * react.localhost:8080 β localhost:3003
+ * tanstack.localhost:8080 β localhost:3004
+ * gatsby.localhost:8080 β localhost:3005
+ *
+ * Note: Most modern browsers resolve *.localhost to 127.0.0.1 automatically.
+ */
+
+import http from 'http'
+import { URL } from 'url'
+
+const PROXY_PORT = 8080
+
+// Subdomain to port mapping
+const routes = {
+ docs: 3000,
+ 'next-app': 3001,
+ 'next-pages': 3002,
+ react: 3003,
+ tanstack: 3004,
+ gatsby: 3005,
+}
+
+// Extract subdomain from host header
+function getSubdomain(host) {
+ if (!host) return null
+ // Remove port if present
+ const hostname = host.split(':')[0]
+ // Match patterns like "docs.localhost" or "next-app.localhost"
+ const match = hostname.match(/^([a-z0-9-]+)\.localhost$/i)
+ return match ? match[1] : null
+}
+
+// Proxy request to target server
+function proxyRequest(req, res, targetPort) {
+ const targetUrl = new URL(req.url, `http://localhost:${targetPort}`)
+
+ const proxyReq = http.request(
+ {
+ hostname: 'localhost',
+ port: targetPort,
+ path: req.url,
+ method: req.method,
+ headers: {
+ ...req.headers,
+ host: `localhost:${targetPort}`,
+ },
+ },
+ proxyRes => {
+ res.writeHead(proxyRes.statusCode, proxyRes.headers)
+ proxyRes.pipe(res, { end: true })
+ }
+ )
+
+ proxyReq.on('error', err => {
+ console.error(`Proxy error to port ${targetPort}: ${err.message}`)
+ res.writeHead(502, { 'Content-Type': 'text/plain' })
+ res.end(`Bad Gateway: Could not connect to localhost:${targetPort}`)
+ })
+
+ req.pipe(proxyReq, { end: true })
+}
+
+// Handle WebSocket upgrades (for HMR)
+function handleUpgrade(req, socket, head, targetPort) {
+ const proxyReq = http.request({
+ hostname: 'localhost',
+ port: targetPort,
+ path: req.url,
+ method: req.method,
+ headers: {
+ ...req.headers,
+ host: `localhost:${targetPort}`,
+ },
+ })
+
+ proxyReq.on('upgrade', (proxyRes, proxySocket, proxyHead) => {
+ socket.write(
+ `HTTP/1.1 101 Switching Protocols\r\n` +
+ Object.entries(proxyRes.headers)
+ .map(([k, v]) => `${k}: ${v}`)
+ .join('\r\n') +
+ '\r\n\r\n'
+ )
+ proxySocket.pipe(socket)
+ socket.pipe(proxySocket)
+ })
+
+ proxyReq.on('error', err => {
+ console.error(`WebSocket proxy error: ${err.message}`)
+ socket.end()
+ })
+
+ proxyReq.end()
+}
+
+// Create proxy server
+const server = http.createServer((req, res) => {
+ const host = req.headers.host
+ const subdomain = getSubdomain(host)
+
+ if (!subdomain || !routes[subdomain]) {
+ // Show available routes
+ res.writeHead(200, { 'Content-Type': 'text/html' })
+ res.end(`
+
+
+
+ react-fathom Dev Sites
+
+
+
+ react-fathom Development Sites
+
+ ${Object.entries(routes)
+ .map(
+ ([name, port]) =>
+ `${name}.localhost (port ${port}) `
+ )
+ .join('\n ')}
+
+
+
+ `)
+ return
+ }
+
+ const targetPort = routes[subdomain]
+ proxyRequest(req, res, targetPort)
+})
+
+// Handle WebSocket upgrades
+server.on('upgrade', (req, socket, head) => {
+ const host = req.headers.host
+ const subdomain = getSubdomain(host)
+
+ if (subdomain && routes[subdomain]) {
+ handleUpgrade(req, socket, head, routes[subdomain])
+ } else {
+ socket.end()
+ }
+})
+
+server.listen(PROXY_PORT, () => {
+ console.log(`Subdomain proxy listening on port ${PROXY_PORT}`)
+ console.log('Available sites:')
+ Object.entries(routes).forEach(([name, port]) => {
+ console.log(` http://${name}.localhost:${PROXY_PORT} β localhost:${port}`)
+ })
+})
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.test.tsx b/src/FathomProvider.test.tsx
index a3937df..257545f 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')
})
@@ -629,4 +733,158 @@ describe('FathomProvider', () => {
expect(clientRefValue).toBe(parentClient)
})
})
+
+ describe('onError callback', () => {
+ it('should call onError when trackEvent throws', () => {
+ const error = new Error('trackEvent failed')
+ const onError = vi.fn()
+ const mockClient = {
+ trackEvent: vi.fn().mockImplementation(() => {
+ throw error
+ }),
+ 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')
+
+ expect(onError).toHaveBeenCalledWith(error, {
+ method: 'trackEvent',
+ args: ['test-event', {}],
+ })
+ })
+
+ it('should call onError when trackPageview throws', () => {
+ const error = new Error('trackPageview failed')
+ const onError = vi.fn()
+ const mockClient = {
+ trackEvent: vi.fn(),
+ trackPageview: vi.fn().mockImplementation(() => {
+ throw error
+ }),
+ 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?.({ url: '/test' })
+
+ expect(onError).toHaveBeenCalledWith(error, {
+ method: 'trackPageview',
+ args: [{ url: '/test' }],
+ })
+ })
+
+ it('should call onError when trackGoal throws', () => {
+ const error = new Error('trackGoal failed')
+ const onError = vi.fn()
+ const mockClient = {
+ trackEvent: vi.fn(),
+ trackPageview: vi.fn(),
+ trackGoal: vi.fn().mockImplementation(() => {
+ throw error
+ }),
+ 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.trackGoal?.('GOAL_CODE', 1000)
+
+ expect(onError).toHaveBeenCalledWith(error, {
+ method: 'trackGoal',
+ args: ['GOAL_CODE', 1000],
+ })
+ })
+
+ it('should call onError when load throws', () => {
+ const error = new Error('load failed')
+ const onError = vi.fn()
+ const mockClient = {
+ trackEvent: vi.fn(),
+ trackPageview: vi.fn(),
+ trackGoal: vi.fn(),
+ load: vi.fn().mockImplementation(() => {
+ throw error
+ }),
+ 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.load?.('SITE_ID')
+
+ expect(onError).toHaveBeenCalledWith(error, {
+ method: 'load',
+ args: ['SITE_ID', undefined],
+ })
+ })
+
+ it('should not throw when onError is not provided', () => {
+ const mockClient = {
+ trackEvent: vi.fn().mockImplementation(() => {
+ throw new Error('trackEvent failed')
+ }),
+ 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 })
+
+ // Should not throw
+ expect(() => result.current.trackEvent?.('test-event')).not.toThrow()
+ })
+ })
})
diff --git a/src/FathomProvider.tsx b/src/FathomProvider.tsx
index 4d5e1d8..09f239a 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,26 +20,145 @@ const FathomProvider: React.FC = ({
siteId,
defaultPageviewOptions: providedDefaultPageviewOptions,
defaultEventOptions: providedDefaultEventOptions,
+ debug: debugProp,
+ onError,
}) => {
// 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
+
+ // Log debug state when enabled
+ useEffect(() => {
+ if (debugEnabled) {
+ console.log('[react-fathom] Debug mode: enabled')
+ }
+ }, [debugEnabled])
+
+ // Log debug prop for diagnostics
+ useEffect(() => {
+ console.log('[react-fathom] FathomProvider mounted, debugProp:', debugProp, 'debugEnabled:', debugEnabled)
+ }, [])
+
+ // 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) => {
+ // Always emit global custom event when debug is enabled
+ // This helps with linked packages where React context may not be shared
+ if (debugEnabled && typeof window !== 'undefined') {
+ window.dispatchEvent(new CustomEvent('react-fathom:debug', { detail: event }))
+ }
+
+ 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,
[providedClient, parentContext.client],
)
- // Merge defaultPageviewOptions: provided > parent > undefined
+ // Helper to safely call client methods with error handling
+ const safeClientCall = useCallback(
+ (method: string, fn: () => T, args?: unknown[]): T | undefined => {
+ try {
+ return fn()
+ } catch (error) {
+ if (process.env.NODE_ENV !== 'production') {
+ console.error(`[react-fathom] ${method}() failed:`, error)
+ }
+ onError?.(error, { method, args })
+ return undefined
+ }
+ },
+ [onError],
+ )
+
+ // 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],
)
@@ -53,9 +176,9 @@ const FathomProvider: React.FC = ({
const load = useCallback(
(siteId: string, clientOptions?: LoadOptions) => {
- client.load(siteId, clientOptions)
+ safeClientCall('load', () => client.load(siteId, clientOptions), [siteId, clientOptions])
},
- [client],
+ [client, safeClientCall],
)
const setSite = useCallback(
@@ -67,34 +190,74 @@ 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
+ safeClientCall('trackEvent', () => client.trackEvent(eventName, mergedOptions), [eventName, mergedOptions])
},
- [client, defaultEventOptions],
+ [client, defaultEventOptions, emitDebugEvent, safeClientCall],
)
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
+ safeClientCall('trackPageview', () => client.trackPageview(mergedOptions), [mergedOptions])
},
- [client, defaultPageviewOptions],
+ [client, defaultPageviewOptions, emitDebugEvent, safeClientCall],
)
const trackGoal = useCallback(
(code: string, cents: number) => {
- client.trackGoal(code, cents)
+ // Emit debug event
+ emitDebugEvent({
+ id: generateDebugEventId(),
+ timestamp: Date.now(),
+ type: 'goal',
+ goalCode: code,
+ goalCents: cents,
+ })
+
+ // Track to Fathom
+ safeClientCall('trackGoal', () => client.trackGoal(code, cents), [code, cents])
},
- [client],
+ [client, emitDebugEvent, safeClientCall],
)
useEffect(() => {
if (siteId !== undefined) {
load(siteId, clientOptions)
+ } else if (process.env.NODE_ENV !== 'production') {
+ console.warn(
+ '[react-fathom] No siteId provided to FathomProvider. ' +
+ 'Analytics tracking will not be sent to Fathom until a siteId is configured. ' +
+ 'Debug events will still be captured if debug mode is enabled.'
+ )
}
}, [clientOptions, load, siteId])
@@ -119,6 +282,8 @@ const FathomProvider: React.FC = ({
client,
defaultPageviewOptions,
defaultEventOptions,
+ subscribeToDebug: debugEnabled ? subscribeToDebug : undefined,
+ debugEnabled,
}}
>
{children}
diff --git a/src/debug/EventStream.test.tsx b/src/debug/EventStream.test.tsx
new file mode 100644
index 0000000..7588267
--- /dev/null
+++ b/src/debug/EventStream.test.tsx
@@ -0,0 +1,329 @@
+import React from 'react'
+
+import { beforeEach, describe, expect, it, vi, afterEach } from 'vitest'
+
+import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
+
+import { FathomProvider } from '../FathomProvider'
+import { EventStream } from './EventStream'
+
+// 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('EventStream', () => {
+ 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 localStorageMock = (() => {
+ let store: Record = {}
+ return {
+ getItem: vi.fn((key: string) => store[key] || null),
+ setItem: vi.fn((key: string, value: string) => {
+ store[key] = value
+ }),
+ removeItem: vi.fn((key: string) => {
+ delete store[key]
+ }),
+ clear: vi.fn(() => {
+ store = {}
+ }),
+ }
+ })()
+
+ beforeEach(() => {
+ vi.clearAllMocks()
+ localStorageMock.clear()
+ Object.defineProperty(window, 'localStorage', {
+ value: localStorageMock,
+ writable: true,
+ })
+ })
+
+ afterEach(() => {
+ vi.restoreAllMocks()
+ })
+
+ it('should not render when debug is disabled', async () => {
+ render(
+
+
+
+ )
+
+ // Wait for hydration
+ await waitFor(() => {
+ expect(screen.queryByRole('button')).not.toBeInTheDocument()
+ })
+ })
+
+ it('should render toggle button when debug is enabled', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: /show event stream/i })).toBeInTheDocument()
+ })
+ })
+
+ it('should show panel when button is clicked', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: /show event stream/i })).toBeInTheDocument()
+ })
+
+ fireEvent.click(screen.getByRole('button', { name: /show event stream/i }))
+
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ expect(screen.getByText(/No events yet/)).toBeInTheDocument()
+ })
+
+ it('should hide panel when close button is clicked', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ })
+
+ fireEvent.click(screen.getByRole('button', { name: /hide event stream/i }))
+
+ expect(screen.queryByText('π Event Stream')).not.toBeInTheDocument()
+ })
+
+ it('should display events when tracking calls are made', async () => {
+ const TestComponent = () => {
+ const [tracked, setTracked] = React.useState(false)
+ return (
+
+
+ {!tracked && (
+ {
+ mockClient.trackEvent('test-event')
+ setTracked(true)
+ }}
+ >
+ Track Event
+
+ )}
+
+ )
+ }
+
+ render( )
+
+ await waitFor(() => {
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ })
+
+ // Initial state shows no events
+ expect(screen.getByText(/No events yet/)).toBeInTheDocument()
+ })
+
+ it('should toggle with keyboard shortcut', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: /show event stream/i })).toBeInTheDocument()
+ })
+
+ // Panel should not be visible initially
+ expect(screen.queryByText('π Event Stream')).not.toBeInTheDocument()
+
+ // Press Cmd + .
+ act(() => {
+ fireEvent.keyDown(document, { key: '.', metaKey: true })
+ })
+
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+
+ // Press Cmd + . again to hide
+ act(() => {
+ fireEvent.keyDown(document, { key: '.', metaKey: true })
+ })
+
+ expect(screen.queryByText('π Event Stream')).not.toBeInTheDocument()
+ })
+
+ it('should toggle with Ctrl + . on non-Mac', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: /show event stream/i })).toBeInTheDocument()
+ })
+
+ // Press Ctrl + .
+ act(() => {
+ fireEvent.keyDown(document, { key: '.', ctrlKey: true })
+ })
+
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ })
+
+ it('should persist visibility state to localStorage', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: /show event stream/i })).toBeInTheDocument()
+ })
+
+ fireEvent.click(screen.getByRole('button', { name: /show event stream/i }))
+
+ expect(localStorageMock.setItem).toHaveBeenCalledWith(
+ 'react-fathom-event-stream-visible',
+ 'true'
+ )
+ })
+
+ it('should load visibility state from localStorage', async () => {
+ localStorageMock.getItem.mockReturnValue('true')
+
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ })
+ })
+
+ it('should handle localStorage errors gracefully', async () => {
+ const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
+ localStorageMock.getItem.mockImplementation(() => {
+ throw new Error('localStorage unavailable')
+ })
+
+ // Should not throw
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ })
+
+ errorSpy.mockRestore()
+ })
+
+ it('should handle localStorage setItem errors gracefully', async () => {
+ localStorageMock.setItem.mockImplementation(() => {
+ throw new Error('localStorage full')
+ })
+
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: /show event stream/i })).toBeInTheDocument()
+ })
+
+ // Should not throw when clicking
+ fireEvent.click(screen.getByRole('button', { name: /show event stream/i }))
+
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ })
+
+ it('should position panel on the left when position is bottom-left', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ })
+
+ // Panel container should be rendered - find the outer positioned div
+ const panelContainer = screen.getByText('π Event Stream').parentElement?.parentElement
+ expect(panelContainer).toHaveAttribute('style', expect.stringContaining('left: 0'))
+ })
+
+ it('should show keyboard shortcut hint in footer', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ })
+
+ expect(screen.getByText('β.')).toBeInTheDocument()
+ })
+
+ it('should disable clear button when no events', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByText('π Event Stream')).toBeInTheDocument()
+ })
+
+ const clearButton = screen.getByRole('button', { name: 'Clear' })
+ expect(clearButton).toBeDisabled()
+ })
+
+ it('should use debug={true} shorthand', async () => {
+ render(
+
+
+
+ )
+
+ await waitFor(() => {
+ expect(screen.getByRole('button', { name: /show event stream/i })).toBeInTheDocument()
+ })
+ })
+})
diff --git a/src/debug/EventStream.tsx b/src/debug/EventStream.tsx
new file mode 100644
index 0000000..f936a04
--- /dev/null
+++ b/src/debug/EventStream.tsx
@@ -0,0 +1,244 @@
+'use client'
+
+import React, { useState, useEffect } from 'react'
+import { useDebugSubscription } from '../hooks/useDebugSubscription'
+import type { DebugEvent } from '../types'
+
+const STORAGE_KEY = 'react-fathom-event-stream-visible'
+
+function formatTime(timestamp: number): string {
+ return new Date(timestamp).toLocaleTimeString('en-US', {
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ })
+}
+
+interface EventCardProps {
+ event: DebugEvent
+}
+
+function EventCard({ event }: EventCardProps) {
+ const colors = {
+ pageview: '#3b82f6',
+ event: '#8b5cf6',
+ goal: '#22c55e',
+ }
+
+ let title = ''
+ let subtitle = ''
+
+ switch (event.type) {
+ case 'pageview':
+ title = 'Pageview'
+ subtitle = event.url || (typeof window !== 'undefined' ? 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 interface EventStreamProps {
+ /**
+ * Maximum number of events to display
+ * @default 20
+ */
+ maxEvents?: number
+ /**
+ * Position of the toggle button
+ * @default 'bottom-right'
+ */
+ position?: 'bottom-right' | 'bottom-left'
+ /**
+ * Initial visibility state (overridden by localStorage if available)
+ * @default false
+ */
+ defaultVisible?: boolean
+}
+
+/**
+ * A debug panel component that displays tracked events in real-time.
+ * Only renders when debug mode is enabled in the FathomProvider.
+ *
+ * @example
+ * ```tsx
+ *
+ *
+ *
+ *
+ * ```
+ */
+export function EventStream({
+ maxEvents = 20,
+ position = 'bottom-right',
+ defaultVisible = false,
+}: EventStreamProps = {}) {
+ const [isVisible, setIsVisible] = useState(defaultVisible)
+ const [isHydrated, setIsHydrated] = useState(false)
+ const { events, debugEnabled, clearEvents } = useDebugSubscription({ maxEvents })
+
+ useEffect(() => {
+ setIsHydrated(true)
+ if (typeof window !== 'undefined') {
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY)
+ if (stored !== null) {
+ setIsVisible(stored === 'true')
+ }
+ } catch {
+ // localStorage may be unavailable (private browsing, security restrictions, etc.)
+ }
+ }
+ }, [])
+
+ 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)
+ }, [])
+
+ useEffect(() => {
+ if (isHydrated && typeof window !== 'undefined') {
+ try {
+ localStorage.setItem(STORAGE_KEY, String(isVisible))
+ } catch {
+ // localStorage may be unavailable (private browsing, security restrictions, etc.)
+ }
+ }
+ }, [isVisible, isHydrated])
+
+ if (!debugEnabled || !isHydrated) return null
+
+ const isRight = position === 'bottom-right'
+
+ return (
+ <>
+ setIsVisible(!isVisible)}
+ aria-label={isVisible ? 'Hide event stream' : 'Show event stream'}
+ style={{
+ position: 'fixed',
+ bottom: '16px',
+ [isRight ? 'right' : 'left']: '16px',
+ zIndex: 1000,
+ width: '48px',
+ height: '48px',
+ borderRadius: '50%',
+ backgroundColor: '#8b5cf6',
+ color: 'white',
+ border: 'none',
+ cursor: 'pointer',
+ fontSize: '20px',
+ boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
+ }}
+ >
+ {isVisible ? 'β' : 'π'}
+
+
+ {isVisible && (
+
+
+ π Event Stream
+
+ Clear
+
+
+
+
+ {events.length === 0 ? (
+
+
π
+
+ No events yet. Navigate or interact to see tracking events.
+
+
+ ) : (
+ events.map((event) =>
)
+ )}
+
+
+
+ {events.length} event{events.length !== 1 ? 's' : ''} β’{' '}
+ β. to toggle
+
+
+ )}
+ >
+ )
+}
diff --git a/src/debug/index.ts b/src/debug/index.ts
new file mode 100644
index 0000000..3293cf3
--- /dev/null
+++ b/src/debug/index.ts
@@ -0,0 +1,7 @@
+export * from './EventStream'
+export { useDebugSubscription } from '../hooks/useDebugSubscription'
+export type {
+ DebugEvent,
+ DebugEventCallback,
+ DebugOptions,
+} from '../types'
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/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 (
+ *
+ *
Clear
+ * {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/next/NextFathomProviderApp.tsx b/src/next/NextFathomProviderApp.tsx
index 5d0d23f..61761bd 100644
--- a/src/next/NextFathomProviderApp.tsx
+++ b/src/next/NextFathomProviderApp.tsx
@@ -1,6 +1,6 @@
'use client'
-import React from 'react'
+import React, { Suspense } from 'react'
import { FathomProvider } from '../FathomProvider'
import type { FathomProviderProps } from '../types'
@@ -53,7 +53,9 @@ export const NextFathomProviderApp: React.FC = ({
}) => {
return (
-
+
+
+
{children}
)
diff --git a/src/next/NextFathomTrackViewApp.test.tsx b/src/next/NextFathomTrackViewApp.test.tsx
index ecc19a6..09009f0 100644
--- a/src/next/NextFathomTrackViewApp.test.tsx
+++ b/src/next/NextFathomTrackViewApp.test.tsx
@@ -259,6 +259,50 @@ describe('NextFathomTrackViewApp', () => {
)
})
+ it('should apply transformUrl to tracked URL', 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 nextNavigation = await import('next/navigation')
+ vi.mocked(nextNavigation.usePathname).mockReturnValue('/test-page')
+ vi.mocked(nextNavigation.useSearchParams).mockReturnValue(
+ new URLSearchParams('?token=secret&page=1'),
+ )
+
+ const transformUrl = (url: string) => {
+ const u = new URL(url)
+ u.searchParams.delete('token')
+ return u.toString()
+ }
+
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
+
+
+ {children}
+
+ )
+
+ renderHook(() => useFathom(), { wrapper })
+
+ await waitFor(() => {
+ expect(trackPageviewSpy).toHaveBeenCalled()
+ })
+
+ // URL should have token param stripped
+ expect(trackPageviewSpy).toHaveBeenCalledWith({
+ url: 'https://example.com/test-page?page=1',
+ })
+ })
+
it('should have displayName', () => {
expect(NextFathomTrackViewApp.displayName).toBe('NextFathomTrackViewApp')
})
diff --git a/src/next/NextFathomTrackViewApp.tsx b/src/next/NextFathomTrackViewApp.tsx
index 1fd4b06..943b32e 100644
--- a/src/next/NextFathomTrackViewApp.tsx
+++ b/src/next/NextFathomTrackViewApp.tsx
@@ -5,6 +5,7 @@ import React, { useEffect, useRef } from 'react'
import { usePathname, useSearchParams } from 'next/navigation.js'
import { useFathom } from '../hooks/useFathom'
+import { buildTrackingUrl } from './utils'
export interface NextFathomTrackViewAppProps {
/**
@@ -12,6 +13,22 @@ export interface NextFathomTrackViewAppProps {
* @default false
*/
disableAutoTrack?: boolean
+ /**
+ * Transform the URL before tracking.
+ * Useful for stripping sensitive parameters or normalizing URLs.
+ *
+ * @example
+ * ```tsx
+ * {
+ * const u = new URL(url)
+ * u.searchParams.delete('token')
+ * return u.toString()
+ * }}
+ * />
+ * ```
+ */
+ transformUrl?: (url: string) => string
}
/**
@@ -40,6 +57,7 @@ export interface NextFathomTrackViewAppProps {
*/
export const NextFathomTrackViewApp: React.FC = ({
disableAutoTrack = false,
+ transformUrl,
}) => {
const pathname = usePathname()
const searchParams = useSearchParams()
@@ -53,25 +71,20 @@ export const NextFathomTrackViewApp: React.FC = ({
}
const searchString = searchParams?.toString()
- const url =
+ const path =
pathname +
(searchString !== undefined && searchString !== ''
? `?${searchString}`
: '')
+ const url = buildTrackingUrl(path, transformUrl)
+
// Track initial pageview only once
if (!hasTrackedInitialPageview.current) {
hasTrackedInitialPageview.current = true
- trackPageview({
- url: window.location.origin + url,
- })
- } else {
- // Track subsequent route changes
- trackPageview({
- url: window.location.origin + url,
- })
}
- }, [pathname, searchParams, trackPageview, client, disableAutoTrack])
+ trackPageview({ url })
+ }, [pathname, searchParams, trackPageview, client, disableAutoTrack, transformUrl])
// This component doesn't render anything
return null
diff --git a/src/next/NextFathomTrackViewPages.test.tsx b/src/next/NextFathomTrackViewPages.test.tsx
index 4cb7e0f..5e8647f 100644
--- a/src/next/NextFathomTrackViewPages.test.tsx
+++ b/src/next/NextFathomTrackViewPages.test.tsx
@@ -55,6 +55,8 @@ describe('NextFathomTrackViewPages', () => {
window.location = {
href: 'https://example.com/test-page',
origin: 'https://example.com',
+ pathname: '/test-page',
+ search: '',
} as Location
})
@@ -219,6 +221,99 @@ describe('NextFathomTrackViewPages', () => {
)
})
+ it('should apply transformUrl to initial pageview', async () => {
+ // Set up window.location with a token parameter
+ delete (window as { location?: unknown }).location
+ window.location = {
+ href: 'https://example.com/test-page?token=secret&page=1',
+ origin: 'https://example.com',
+ pathname: '/test-page',
+ search: '?token=secret&page=1',
+ } as Location
+
+ 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) => {
+ const u = new URL(url)
+ u.searchParams.delete('token')
+ return u.toString()
+ }
+
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
+
+
+ {children}
+
+ )
+
+ renderHook(() => useFathom(), { wrapper })
+
+ await waitFor(() => {
+ expect(trackPageviewSpy).toHaveBeenCalled()
+ })
+
+ // URL should have token param stripped
+ expect(trackPageviewSpy).toHaveBeenCalledWith({
+ url: 'https://example.com/test-page?page=1',
+ })
+ })
+
+ it('should apply transformUrl to route change pageviews', 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) => {
+ const u = new URL(url)
+ u.searchParams.delete('token')
+ return u.toString()
+ }
+
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
+
+
+ {children}
+
+ )
+
+ renderHook(() => useFathom(), { wrapper })
+
+ await waitFor(() => {
+ expect(mockRouter.events.on).toHaveBeenCalled()
+ })
+
+ // Get the handler that was registered
+ const onCall = mockRouter.events.on.mock.calls.find(
+ (call) => call[0] === 'routeChangeComplete',
+ )
+ const handler = onCall?.[1]
+
+ // Simulate a route change with a token parameter
+ handler?.('/new-page?token=secret&id=123')
+
+ expect(trackPageviewSpy).toHaveBeenLastCalledWith({
+ url: 'https://example.com/new-page?id=123',
+ })
+ })
+
it('should have displayName', () => {
expect(NextFathomTrackViewPages.displayName).toBe(
'NextFathomTrackViewPages',
diff --git a/src/next/NextFathomTrackViewPages.tsx b/src/next/NextFathomTrackViewPages.tsx
index 8eb7c95..be43a32 100644
--- a/src/next/NextFathomTrackViewPages.tsx
+++ b/src/next/NextFathomTrackViewPages.tsx
@@ -3,6 +3,7 @@ import React, { useEffect, useRef } from 'react'
import { useRouter } from 'next/compat/router.js'
import { useFathom } from '../hooks/useFathom'
+import { buildTrackingUrl } from './utils'
export interface NextFathomTrackViewPagesProps {
/**
@@ -10,6 +11,22 @@ export interface NextFathomTrackViewPagesProps {
* @default false
*/
disableAutoTrack?: boolean
+ /**
+ * Transform the URL before tracking.
+ * Useful for stripping sensitive parameters or normalizing URLs.
+ *
+ * @example
+ * ```tsx
+ * {
+ * const u = new URL(url)
+ * u.searchParams.delete('token')
+ * return u.toString()
+ * }}
+ * />
+ * ```
+ */
+ transformUrl?: (url: string) => string
}
/**
@@ -34,7 +51,7 @@ export interface NextFathomTrackViewPagesProps {
*/
export const NextFathomTrackViewPages: React.FC<
NextFathomTrackViewPagesProps
-> = ({ disableAutoTrack = false }) => {
+> = ({ disableAutoTrack = false, transformUrl }) => {
const hasTrackedInitialPageview = useRef(false)
const { trackPageview, client } = useFathom()
@@ -54,10 +71,9 @@ export const NextFathomTrackViewPages: React.FC<
return
}
- const handleRouteChangeComplete = (url: string): void => {
- trackPageview({
- url: window.location.origin + url,
- })
+ const handleRouteChangeComplete = (path: string): void => {
+ const url = buildTrackingUrl(path, transformUrl)
+ trackPageview({ url })
}
// router.events is stable in Next.js, so we can use it without including router in dependencies
@@ -67,7 +83,7 @@ export const NextFathomTrackViewPages: React.FC<
router?.events?.off('routeChangeComplete', handleRouteChangeComplete)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [trackPageview, client, disableAutoTrack])
+ }, [trackPageview, client, disableAutoTrack, transformUrl])
// Track initial pageview (routeChangeComplete doesn't fire on initial load)
useEffect(() => {
@@ -83,10 +99,12 @@ export const NextFathomTrackViewPages: React.FC<
}
hasTrackedInitialPageview.current = true
- trackPageview({
- url: window.location.href,
- })
- }, [trackPageview, client, disableAutoTrack, router])
+ const url = buildTrackingUrl(
+ window.location.pathname + window.location.search,
+ transformUrl,
+ )
+ trackPageview({ url })
+ }, [trackPageview, client, disableAutoTrack, router, transformUrl])
// This component doesn't render anything
return null
diff --git a/src/next/utils.test.ts b/src/next/utils.test.ts
new file mode 100644
index 0000000..fb23940
--- /dev/null
+++ b/src/next/utils.test.ts
@@ -0,0 +1,55 @@
+import { beforeEach, describe, expect, it } from 'vitest'
+
+import { buildTrackingUrl } from './utils'
+
+describe('buildTrackingUrl', () => {
+ beforeEach(() => {
+ delete (window as { location?: unknown }).location
+ window.location = {
+ origin: 'https://example.com',
+ } as Location
+ })
+
+ it('should build a full URL from a path', () => {
+ const url = buildTrackingUrl('/test-page')
+ expect(url).toBe('https://example.com/test-page')
+ })
+
+ it('should build a full URL from a path with query string', () => {
+ const url = buildTrackingUrl('/test-page?foo=bar')
+ expect(url).toBe('https://example.com/test-page?foo=bar')
+ })
+
+ it('should apply transformUrl when provided', () => {
+ const transformUrl = (url: string) => {
+ const u = new URL(url)
+ u.searchParams.delete('token')
+ return u.toString()
+ }
+
+ const url = buildTrackingUrl('/test-page?token=secret&page=1', transformUrl)
+ expect(url).toBe('https://example.com/test-page?page=1')
+ })
+
+ it('should not modify URL when transformUrl is not provided', () => {
+ const url = buildTrackingUrl('/test-page?token=secret')
+ expect(url).toBe('https://example.com/test-page?token=secret')
+ })
+
+ it('should handle transformUrl that returns a different URL', () => {
+ const transformUrl = () => 'https://other.com/different-page'
+
+ const url = buildTrackingUrl('/test-page', transformUrl)
+ expect(url).toBe('https://other.com/different-page')
+ })
+
+ it('should handle empty path', () => {
+ const url = buildTrackingUrl('')
+ expect(url).toBe('https://example.com')
+ })
+
+ it('should handle root path', () => {
+ const url = buildTrackingUrl('/')
+ expect(url).toBe('https://example.com/')
+ })
+})
diff --git a/src/next/utils.ts b/src/next/utils.ts
new file mode 100644
index 0000000..a65ec9a
--- /dev/null
+++ b/src/next/utils.ts
@@ -0,0 +1,17 @@
+/**
+ * Builds a full URL for tracking, applying optional transformation.
+ *
+ * @param path - The path portion of the URL (can include query string)
+ * @param transformUrl - Optional function to transform the URL before tracking
+ * @returns The full URL ready for tracking
+ */
+export function buildTrackingUrl(
+ path: string,
+ transformUrl?: (url: string) => string,
+): string {
+ let url = window.location.origin + path
+ if (transformUrl) {
+ url = transformUrl(url)
+ }
+ return url
+}
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 navigate(to)}>Navigate
+}
+
+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/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/src/types.ts b/src/types.ts
index 1dc0ce0..42c37f2 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,40 @@ 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
+ /**
+ * Callback fired when a tracking call fails.
+ * Useful for error monitoring and debugging.
+ *
+ * @example
+ * ```tsx
+ * {
+ * console.error(`Fathom ${context.method} failed:`, error)
+ * }}
+ * />
+ * ```
+ */
+ onError?: (error: unknown, context: { method: string; args?: unknown[] }) => void
}
diff --git a/yarn.lock b/yarn.lock
index e6beb43..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,56 +1417,66 @@
"@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"
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"
@@ -1515,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"
@@ -1707,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.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"
+ "@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"
@@ -1734,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"
@@ -1774,16 +1827,23 @@
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"
-"@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==
+"@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@*", "@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"
@@ -1792,175 +1852,174 @@
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:
@@ -2137,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==
@@ -2213,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"
@@ -2251,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==
@@ -2299,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"
@@ -2365,12 +2424,22 @@ 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"
+ 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"
@@ -2388,6 +2457,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"
@@ -2433,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"
@@ -2467,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==
@@ -2569,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"
@@ -2802,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"
@@ -3179,6 +3256,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"
@@ -3326,6 +3408,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"
@@ -3531,6 +3620,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"
@@ -3555,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"
@@ -3767,7 +3852,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==
@@ -3900,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:
@@ -4127,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==
@@ -4135,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"
@@ -4148,7 +4233,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==
@@ -4198,6 +4283,26 @@ 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"
+ 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"
@@ -4363,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:
@@ -4475,6 +4580,21 @@ 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"
+ 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"
@@ -4598,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"
@@ -4766,23 +4886,33 @@ 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"
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"
@@ -4930,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"
@@ -4997,6 +5127,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"
@@ -5026,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"
@@ -5058,6 +5193,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"
@@ -5068,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==
@@ -5117,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"
@@ -5180,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==