From d9d584e556327e91ee44bdcf0bd5d48c4c7547aa Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 2 Oct 2025 22:03:36 +0000
Subject: [PATCH 1/4] Initial plan
From ee01222e72464b4f4e4fff31fa63516d07772855 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 2 Oct 2025 22:12:29 +0000
Subject: [PATCH 2/4] Create shared DebugInfo component for all demos
Co-authored-by: JasonMore <383719+JasonMore@users.noreply.github.com>
---
src/Layout.tsx | 6 ++-
src/components/DebugInfo.tsx | 47 +++++++++++++++++++
.../propDrilling/PropDrillingRenderDemo.tsx | 4 +-
.../demoControls/PropDrillingDebugInfo.tsx | 32 -------------
.../PropDrillingNaiveRenderDemo.tsx | 4 +-
.../demoControls/PropDrillingDebugInfo.tsx | 32 -------------
src/examples/zustand-query/ZustandQuery.tsx | 17 ++++++-
.../zustand-query/demoControls/DebugInfo.tsx | 21 ---------
src/examples/zustand/ZustandRenderDemo.tsx | 11 ++++-
.../zustand/demoControls/DebugInfo.tsx | 17 -------
10 files changed, 79 insertions(+), 112 deletions(-)
create mode 100644 src/components/DebugInfo.tsx
delete mode 100644 src/examples/propDrilling/demoControls/PropDrillingDebugInfo.tsx
delete mode 100644 src/examples/propDrillingNaive/demoControls/PropDrillingDebugInfo.tsx
delete mode 100644 src/examples/zustand-query/demoControls/DebugInfo.tsx
delete mode 100644 src/examples/zustand/demoControls/DebugInfo.tsx
diff --git a/src/Layout.tsx b/src/Layout.tsx
index feae79e..1dfe562 100644
--- a/src/Layout.tsx
+++ b/src/Layout.tsx
@@ -12,7 +12,9 @@ export const Layout = () => {
resetTokenCounter();
// Update page title based on current route
- const currentRoute = routes.find((route) => route.path === location.pathname);
+ const currentRoute = routes.find(
+ (route) => route.path === location.pathname,
+ );
if (currentRoute) {
document.title = currentRoute.title;
} else if (location.pathname === homeRoute.path) {
@@ -26,4 +28,4 @@ export const Layout = () => {
>
);
-};
\ No newline at end of file
+};
diff --git a/src/components/DebugInfo.tsx b/src/components/DebugInfo.tsx
new file mode 100644
index 0000000..2c6d038
--- /dev/null
+++ b/src/components/DebugInfo.tsx
@@ -0,0 +1,47 @@
+import { memo } from "react";
+import css from "./css/DebugInfo.module.css";
+import sharedStyles from "./css/shared.module.css";
+import type { World } from "../types/World.ts";
+
+type Snapshot = {
+ selectedWorldId: string;
+ hello: { worlds: World[] | undefined };
+};
+
+type Props = {
+ /**
+ * Snapshot data to display.
+ */
+ snapshot: Snapshot | null;
+ /**
+ * Optional title for the debug panel. Defaults to "Store snapshot".
+ */
+ title?: string;
+};
+
+export const DebugInfo = memo(({ snapshot, title }: Props) => {
+ const displayTitle = title || "Store snapshot";
+
+ const pretty =
+ snapshot !== null && snapshot !== undefined
+ ? JSON.stringify(
+ {
+ selectedWorldId: snapshot.selectedWorldId,
+ hello: {
+ worlds: snapshot.hello.worlds?.map((world) => ({ ...world })),
+ },
+ },
+ null,
+ 2,
+ )
+ : "Snapshot pending…";
+
+ return (
+
+
{displayTitle}
+
{pretty}
+
+ );
+});
+
+DebugInfo.displayName = "DebugInfo";
diff --git a/src/examples/propDrilling/PropDrillingRenderDemo.tsx b/src/examples/propDrilling/PropDrillingRenderDemo.tsx
index e658016..794f9dd 100644
--- a/src/examples/propDrilling/PropDrillingRenderDemo.tsx
+++ b/src/examples/propDrilling/PropDrillingRenderDemo.tsx
@@ -4,7 +4,7 @@ import css from "../../components/css/DemoLayout.module.css";
import { RenderToken } from "../../components/perf/RenderToken.tsx";
import type { World } from "../../types/World.ts";
import { WorldApp } from "./exampleComponents/WorldApp.tsx";
-import { PropDrillingDebugInfo } from "./demoControls/PropDrillingDebugInfo.tsx";
+import { DebugInfo } from "../../components/DebugInfo.tsx";
import type { WorldsResponse } from "../../api/worlds.ts";
type Snapshot = {
@@ -47,7 +47,7 @@ export function PropDrillingRenderDemo() {
onSnapshotChange={setSnapshot}
revalidate={revalidator.revalidate}
/>
-
+
);
}
diff --git a/src/examples/propDrilling/demoControls/PropDrillingDebugInfo.tsx b/src/examples/propDrilling/demoControls/PropDrillingDebugInfo.tsx
deleted file mode 100644
index e98a8fe..0000000
--- a/src/examples/propDrilling/demoControls/PropDrillingDebugInfo.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import css from "../../../components/css/DebugInfo.module.css";
-import sharedStyles from "../../../components/css/shared.module.css";
-import type { World } from "../../zustand/data/types.ts";
-
-type Props = {
- snapshot: {
- selectedWorldId: string;
- hello: { worlds: World[] };
- } | null;
-};
-
-export function PropDrillingDebugInfo({ snapshot }: Props) {
- const pretty = snapshot
- ? JSON.stringify(
- {
- selectedWorldId: snapshot.selectedWorldId,
- hello: {
- worlds: snapshot.hello.worlds.map((world) => ({ ...world })),
- },
- },
- null,
- 2,
- )
- : "Snapshot pending…";
-
- return (
-
-
Top level state snapshot
-
{pretty}
-
- );
-}
diff --git a/src/examples/propDrillingNaive/PropDrillingNaiveRenderDemo.tsx b/src/examples/propDrillingNaive/PropDrillingNaiveRenderDemo.tsx
index 5103056..7430b6a 100644
--- a/src/examples/propDrillingNaive/PropDrillingNaiveRenderDemo.tsx
+++ b/src/examples/propDrillingNaive/PropDrillingNaiveRenderDemo.tsx
@@ -3,7 +3,7 @@ import css from "../../components/css/DemoLayout.module.css";
import { RenderToken } from "../../components/perf/RenderToken.tsx";
import type { World } from "../../types/World.ts";
import { PropDrillingWorldApp } from "./exampleComponents/WorldApp.tsx";
-import { PropDrillingDebugInfo } from "./demoControls/PropDrillingDebugInfo.tsx";
+import { DebugInfo } from "../../components/DebugInfo.tsx";
import { fetchWorlds } from "../../api/worlds.ts";
type Snapshot = {
@@ -50,7 +50,7 @@ export function PropDrillingNaiveRenderDemo() {
setWorlds={setWorlds}
onSnapshotChange={setSnapshot}
/>
-
+
);
}
diff --git a/src/examples/propDrillingNaive/demoControls/PropDrillingDebugInfo.tsx b/src/examples/propDrillingNaive/demoControls/PropDrillingDebugInfo.tsx
deleted file mode 100644
index e98a8fe..0000000
--- a/src/examples/propDrillingNaive/demoControls/PropDrillingDebugInfo.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import css from "../../../components/css/DebugInfo.module.css";
-import sharedStyles from "../../../components/css/shared.module.css";
-import type { World } from "../../zustand/data/types.ts";
-
-type Props = {
- snapshot: {
- selectedWorldId: string;
- hello: { worlds: World[] };
- } | null;
-};
-
-export function PropDrillingDebugInfo({ snapshot }: Props) {
- const pretty = snapshot
- ? JSON.stringify(
- {
- selectedWorldId: snapshot.selectedWorldId,
- hello: {
- worlds: snapshot.hello.worlds.map((world) => ({ ...world })),
- },
- },
- null,
- 2,
- )
- : "Snapshot pending…";
-
- return (
-
-
Top level state snapshot
-
{pretty}
-
- );
-}
diff --git a/src/examples/zustand-query/ZustandQuery.tsx b/src/examples/zustand-query/ZustandQuery.tsx
index bf36f18..03282db 100644
--- a/src/examples/zustand-query/ZustandQuery.tsx
+++ b/src/examples/zustand-query/ZustandQuery.tsx
@@ -1,9 +1,22 @@
import { QueryClientProvider } from "@tanstack/react-query";
import css from "../../components/css/DemoLayout.module.css";
-import { DebugInfo } from "./demoControls/DebugInfo";
+import { DebugInfo } from "../../components/DebugInfo.tsx";
import { WorldApp } from "./components/WorldApp";
import { memo } from "react";
import { queryClient } from "./data/WorldData.ts";
+import { useGetWorlds } from "./data/WorldData.ts";
+import { useWorldStore } from "../zustand/data/WorldStore.tsx";
+
+function ZustandQueryDebugInfo() {
+ const selectedWorldId = useWorldStore((s) => s.selectedWorldId);
+ const { data } = useGetWorlds();
+
+ return (
+
+ );
+}
export const ZustandQuery = memo(() => (
@@ -25,7 +38,7 @@ export const ZustandQuery = memo(() => (
-
+
));
diff --git a/src/examples/zustand-query/demoControls/DebugInfo.tsx b/src/examples/zustand-query/demoControls/DebugInfo.tsx
deleted file mode 100644
index aca91ab..0000000
--- a/src/examples/zustand-query/demoControls/DebugInfo.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { memo } from "react";
-import css from "../../../components/css/DebugInfo.module.css";
-import sharedStyles from "../../../components/css/shared.module.css";
-import { useGetWorlds } from "../data/WorldData.ts";
-import { useWorldStore } from "../../zustand/data/WorldStore.tsx";
-
-export const DebugInfo = memo(() => {
- const selectedWorldId = useWorldStore((s) => s.selectedWorldId);
- const { data } = useGetWorlds();
-
- const snapshot = { selectedWorldId, hello: { worlds: data?.worlds } };
-
- return (
-
-
Store snapshot
-
{JSON.stringify(snapshot, null, 2)}
-
- );
-});
-
-DebugInfo.displayName = "DebugInfo";
diff --git a/src/examples/zustand/ZustandRenderDemo.tsx b/src/examples/zustand/ZustandRenderDemo.tsx
index d392c07..644a86c 100644
--- a/src/examples/zustand/ZustandRenderDemo.tsx
+++ b/src/examples/zustand/ZustandRenderDemo.tsx
@@ -1,12 +1,19 @@
import { useEffect } from "react";
import { useLoaderData, useRevalidator } from "react-router-dom";
import css from "../../components/css/DemoLayout.module.css";
-import { DebugInfo } from "./demoControls/DebugInfo.tsx";
+import { DebugInfo } from "../../components/DebugInfo.tsx";
import { RenderToken } from "../../components/perf/RenderToken.tsx";
import { WorldApp } from "./exampleComponents/WorldApp.tsx";
import { useWorldStore } from "./data/WorldStore.tsx";
import type { WorldsResponse } from "../../api/worlds.ts";
+function ZustandDebugInfo() {
+ const selectedWorldId = useWorldStore((s) => s.selectedWorldId);
+ const worlds = useWorldStore((s) => s.hello.worlds);
+
+ return ;
+}
+
export function ZustandRenderDemo() {
const data = useLoaderData() as WorldsResponse;
const revalidator = useRevalidator();
@@ -37,7 +44,7 @@ export function ZustandRenderDemo() {
-
+
);
}
diff --git a/src/examples/zustand/demoControls/DebugInfo.tsx b/src/examples/zustand/demoControls/DebugInfo.tsx
deleted file mode 100644
index 20fd87d..0000000
--- a/src/examples/zustand/demoControls/DebugInfo.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import css from "../../../components/css/DebugInfo.module.css";
-import sharedStyles from "../../../components/css/shared.module.css";
-import { useWorldStore } from "../data/WorldStore.tsx";
-
-export function DebugInfo() {
- const selectedWorldId = useWorldStore((s) => s.selectedWorldId);
- const worlds = useWorldStore((s) => s.hello.worlds);
-
- const snapshot = { selectedWorldId, hello: { worlds } };
-
- return (
-
-
Store snapshot
-
{JSON.stringify(snapshot, null, 2)}
-
- );
-}
From b321cc5840e7470916dc9d47da4a1a87aabadf34 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 2 Oct 2025 22:13:54 +0000
Subject: [PATCH 3/4] Simplify null check in DebugInfo component
Co-authored-by: JasonMore <383719+JasonMore@users.noreply.github.com>
---
src/components/DebugInfo.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/DebugInfo.tsx b/src/components/DebugInfo.tsx
index 2c6d038..3fb1f7c 100644
--- a/src/components/DebugInfo.tsx
+++ b/src/components/DebugInfo.tsx
@@ -23,7 +23,7 @@ export const DebugInfo = memo(({ snapshot, title }: Props) => {
const displayTitle = title || "Store snapshot";
const pretty =
- snapshot !== null && snapshot !== undefined
+ snapshot != null
? JSON.stringify(
{
selectedWorldId: snapshot.selectedWorldId,
From 19796520600d99cb8d8c0fba2485d8b8d44785d1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 5 Oct 2025 16:57:41 +0000
Subject: [PATCH 4/4] Merge main branch and resolve conflicts
Co-authored-by: JasonMore <383719+JasonMore@users.noreply.github.com>
---
README.md | 2 +-
package-lock.json | 636 ++++++++++--------
package.json | 26 +-
src/Home.tsx | 20 +-
src/api/worlds.ts | 10 +-
src/components/DebugInfo.tsx | 2 +-
.../propDrilling/PropDrillingRenderDemo.tsx | 23 +-
.../propDrilling/data/solarSystemWorlds.ts | 2 +-
src/examples/propDrilling/data/types.ts | 8 -
.../exampleComponents/WorldApp.tsx | 11 +-
.../worldSelector/WorldSelector.tsx | 2 +-
.../exampleComponents/worldsViewer/World.tsx | 2 +-
.../worldsViewer/WorldInfo.tsx | 2 +-
.../worldsViewer/WorldList.tsx | 2 +-
.../worldsViewer/WorldsViewer.tsx | 2 +-
.../PropDrillingNaiveRenderDemo.tsx | 22 +-
.../data/solarSystemWorlds.ts | 2 +-
src/examples/propDrillingNaive/data/types.ts | 8 -
.../exampleComponents/WorldApp.tsx | 3 +-
src/examples/zustand-query/ZustandQuery.tsx | 16 +-
.../zustand-query/data/solarSystemWorlds.ts | 2 +-
src/examples/zustand-query/data/types.ts | 8 -
src/examples/zustand/ZustandRenderDemo.tsx | 13 +-
src/examples/zustand/data/WorldStore.tsx | 3 +-
.../zustand/data/solarSystemWorlds.ts | 2 +-
src/examples/zustand/data/types.ts | 8 -
src/routes.ts | 8 +-
src/types/World.ts | 13 -
28 files changed, 438 insertions(+), 420 deletions(-)
delete mode 100644 src/examples/propDrilling/data/types.ts
delete mode 100644 src/examples/propDrillingNaive/data/types.ts
delete mode 100644 src/examples/zustand-query/data/types.ts
delete mode 100644 src/examples/zustand/data/types.ts
delete mode 100644 src/types/World.ts
diff --git a/README.md b/README.md
index f270cfa..9e9cb6c 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# React Performance Examples
+# React Performance Examples ([Live Demo](https://stackblitz.com/~/github.com/JasonMore/react-performance-examples))
Hands-on demos that contrast common React state patterns with their
performance-minded counterparts. Each example surfaces rerender behavior through `RenderToken` counters.
diff --git a/package-lock.json b/package-lock.json
index a66fad3..9259a33 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,25 +9,25 @@
"version": "0.0.0",
"dependencies": {
"@tanstack/react-query": "^5.90.2",
- "react": "^19.1.1",
- "react-dom": "^19.1.1",
- "react-router-dom": "^7.9.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "react-router-dom": "^7.9.3",
"react-scan": "^0.4.3",
"zustand": "^5.0.8"
},
"devDependencies": {
- "@eslint/js": "^9.36.0",
- "@types/react": "^19.1.13",
- "@types/react-dom": "^19.1.9",
- "@vitejs/plugin-react": "^5.0.3",
- "eslint": "^9.36.0",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
+ "@eslint/js": "^9.37.0",
+ "@types/react": "^19.2.0",
+ "@types/react-dom": "^19.2.0",
+ "@vitejs/plugin-react": "^5.0.4",
+ "eslint": "^9.37.0",
+ "eslint-plugin-react-hooks": "^6.1.1",
+ "eslint-plugin-react-refresh": "^0.4.23",
"globals": "^16.4.0",
"prettier": "^3.6.2",
- "typescript": "~5.8.3",
- "typescript-eslint": "^8.44.0",
- "vite": "^7.1.7"
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.45.0",
+ "vite": "^7.1.9"
}
},
"node_modules/@babel/code-frame": {
@@ -742,19 +742,24 @@
}
},
"node_modules/@eslint/config-helpers": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
- "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz",
+ "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==",
"dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.16.0"
+ },
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
- "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
+ "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
@@ -798,10 +803,11 @@
}
},
"node_modules/@eslint/js": {
- "version": "9.36.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz",
- "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==",
+ "version": "9.37.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz",
+ "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -819,12 +825,13 @@
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
- "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
+ "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
"dev": true,
+ "license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.15.2",
+ "@eslint/core": "^0.16.0",
"levn": "^0.4.1"
},
"engines": {
@@ -924,6 +931,7 @@
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
"run-parallel": "^1.1.9"
@@ -937,6 +945,7 @@
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">= 8"
}
@@ -946,6 +955,7 @@
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
"fastq": "^1.6.0"
@@ -991,10 +1001,11 @@
}
},
"node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.35",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.35.tgz",
- "integrity": "sha512-slYrCpoxJUqzFDDNlvrOYRazQUNRvWPjXA17dAOISY3rDMxX6k8K4cj2H+hEYMHF81HO3uNd5rHVigAWRM5dSg==",
- "dev": true
+ "version": "1.0.0-beta.38",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.38.tgz",
+ "integrity": "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==",
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@rollup/pluginutils": {
"version": "5.3.0",
@@ -1386,7 +1397,8 @@
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@types/node": {
"version": "20.19.18",
@@ -1398,20 +1410,22 @@
}
},
"node_modules/@types/react": {
- "version": "19.1.13",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz",
- "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.0.tgz",
+ "integrity": "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA==",
+ "license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
- "version": "19.1.9",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz",
- "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.0.tgz",
+ "integrity": "sha512-brtBs0MnE9SMx7px208g39lRmC5uHZs96caOJfTjFcYSLHNamvaSMfJNagChVNkup2SdtOxKX1FDBkRSJe1ZAg==",
"dev": true,
+ "license": "MIT",
"peerDependencies": {
- "@types/react": "^19.0.0"
+ "@types/react": "^19.2.0"
}
},
"node_modules/@types/react-reconciler": {
@@ -1424,16 +1438,17 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz",
- "integrity": "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz",
+ "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/type-utils": "8.44.0",
- "@typescript-eslint/utils": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
+ "@typescript-eslint/scope-manager": "8.45.0",
+ "@typescript-eslint/type-utils": "8.45.0",
+ "@typescript-eslint/utils": "8.45.0",
+ "@typescript-eslint/visitor-keys": "8.45.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
@@ -1447,7 +1462,7 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.44.0",
+ "@typescript-eslint/parser": "^8.45.0",
"eslint": "^8.57.0 || ^9.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
@@ -1457,20 +1472,22 @@
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.0.tgz",
- "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz",
+ "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
+ "@typescript-eslint/scope-manager": "8.45.0",
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/typescript-estree": "8.45.0",
+ "@typescript-eslint/visitor-keys": "8.45.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1486,13 +1503,14 @@
}
},
"node_modules/@typescript-eslint/project-service": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.0.tgz",
- "integrity": "sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz",
+ "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.44.0",
- "@typescript-eslint/types": "^8.44.0",
+ "@typescript-eslint/tsconfig-utils": "^8.45.0",
+ "@typescript-eslint/types": "^8.45.0",
"debug": "^4.3.4"
},
"engines": {
@@ -1507,13 +1525,14 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz",
- "integrity": "sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz",
+ "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0"
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/visitor-keys": "8.45.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1524,10 +1543,11 @@
}
},
"node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz",
- "integrity": "sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz",
+ "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -1540,14 +1560,15 @@
}
},
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz",
- "integrity": "sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz",
+ "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/utils": "8.44.0",
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/typescript-estree": "8.45.0",
+ "@typescript-eslint/utils": "8.45.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
},
@@ -1564,10 +1585,11 @@
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz",
- "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz",
+ "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -1577,15 +1599,16 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz",
- "integrity": "sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz",
+ "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.44.0",
- "@typescript-eslint/tsconfig-utils": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
+ "@typescript-eslint/project-service": "8.45.0",
+ "@typescript-eslint/tsconfig-utils": "8.45.0",
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/visitor-keys": "8.45.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -1609,6 +1632,7 @@
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
@@ -1618,6 +1642,7 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
@@ -1633,6 +1658,7 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true,
+ "license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
@@ -1641,15 +1667,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.0.tgz",
- "integrity": "sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz",
+ "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0"
+ "@typescript-eslint/scope-manager": "8.45.0",
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/typescript-estree": "8.45.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1664,12 +1691,13 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz",
- "integrity": "sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz",
+ "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.44.0",
+ "@typescript-eslint/types": "8.45.0",
"eslint-visitor-keys": "^4.2.1"
},
"engines": {
@@ -1681,15 +1709,16 @@
}
},
"node_modules/@vitejs/plugin-react": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.3.tgz",
- "integrity": "sha512-PFVHhosKkofGH0Yzrw1BipSedTH68BFF8ZWy1kfUpCtJcouXXY0+racG8sExw7hw0HoX36813ga5o3LTWZ4FUg==",
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.4.tgz",
+ "integrity": "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@babel/core": "^7.28.4",
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
- "@rolldown/pluginutils": "1.0.0-beta.35",
+ "@rolldown/pluginutils": "1.0.0-beta.38",
"@types/babel__core": "^7.20.5",
"react-refresh": "^0.17.0"
},
@@ -1799,6 +1828,7 @@
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
@@ -2027,19 +2057,20 @@
}
},
"node_modules/eslint": {
- "version": "9.36.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
- "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
+ "version": "9.37.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz",
+ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.0",
- "@eslint/config-helpers": "^0.3.1",
- "@eslint/core": "^0.15.2",
+ "@eslint/config-helpers": "^0.4.0",
+ "@eslint/core": "^0.16.0",
"@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.36.0",
- "@eslint/plugin-kit": "^0.3.5",
+ "@eslint/js": "9.37.0",
+ "@eslint/plugin-kit": "^0.4.0",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
@@ -2087,22 +2118,30 @@
}
},
"node_modules/eslint-plugin-react-hooks": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
- "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-6.1.1.tgz",
+ "integrity": "sha512-St9EKZzOAQF704nt2oJvAKZHjhrpg25ClQoaAlHmPZuajFldVLqRDW4VBNAS01NzeiQF0m0qhG1ZA807K6aVaQ==",
"dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "zod": "^3.22.4 || ^4.0.0",
+ "zod-validation-error": "^3.0.3 || ^4.0.0"
+ },
"engines": {
- "node": ">=10"
+ "node": ">=18"
},
"peerDependencies": {
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
}
},
"node_modules/eslint-plugin-react-refresh": {
- "version": "0.4.20",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
- "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.23.tgz",
+ "integrity": "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA==",
"dev": true,
+ "license": "MIT",
"peerDependencies": {
"eslint": ">=8.40"
}
@@ -2214,6 +2253,7 @@
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@@ -2230,6 +2270,7 @@
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -2254,6 +2295,7 @@
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
}
@@ -2292,6 +2334,7 @@
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -2395,7 +2438,8 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/has-flag": {
"version": "4.0.0",
@@ -2466,6 +2510,7 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.12.0"
}
@@ -2598,6 +2643,7 @@
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">= 8"
}
@@ -2607,6 +2653,7 @@
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"braces": "^3.0.3",
"picomatch": "^2.3.1"
@@ -2911,25 +2958,28 @@
"type": "consulting",
"url": "https://feross.org/support"
}
- ]
+ ],
+ "license": "MIT"
},
"node_modules/react": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
- "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
+ "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
- "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
+ "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
+ "license": "MIT",
"dependencies": {
- "scheduler": "^0.26.0"
+ "scheduler": "^0.27.0"
},
"peerDependencies": {
- "react": "^19.1.1"
+ "react": "^19.2.0"
}
},
"node_modules/react-refresh": {
@@ -2942,9 +2992,9 @@
}
},
"node_modules/react-router": {
- "version": "7.9.1",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.1.tgz",
- "integrity": "sha512-pfAByjcTpX55mqSDGwGnY9vDCpxqBLASg0BMNAuMmpSGESo/TaOUG6BllhAtAkCGx8Rnohik/XtaqiYUJtgW2g==",
+ "version": "7.9.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.3.tgz",
+ "integrity": "sha512-4o2iWCFIwhI/eYAIL43+cjORXYn/aRQPgtFRRZb3VzoyQ5Uej0Bmqj7437L97N9NJW4wnicSwLOLS+yCXfAPgg==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
@@ -2964,12 +3014,12 @@
}
},
"node_modules/react-router-dom": {
- "version": "7.9.1",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.1.tgz",
- "integrity": "sha512-U9WBQssBE9B1vmRjo9qTM7YRzfZ3lUxESIZnsf4VjR/lXYz9MHjvOxHzr/aUm4efpktbVOrF09rL/y4VHa8RMw==",
+ "version": "7.9.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.3.tgz",
+ "integrity": "sha512-1QSbA0TGGFKTAc/aWjpfW/zoEukYfU4dc1dLkT/vvf54JoGMkW+fNA+3oyo2gWVW1GM7BxjJVHz5GnPJv40rvg==",
"license": "MIT",
"dependencies": {
- "react-router": "7.9.1"
+ "react-router": "7.9.3"
},
"engines": {
"node": ">=20.0.0"
@@ -3055,6 +3105,7 @@
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
"dev": true,
+ "license": "MIT",
"engines": {
"iojs": ">=1.0.0",
"node": ">=0.10.0"
@@ -3120,14 +3171,16 @@
"url": "https://feross.org/support"
}
],
+ "license": "MIT",
"dependencies": {
"queue-microtask": "^1.2.2"
}
},
"node_modules/scheduler": {
- "version": "0.26.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
- "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="
+ "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": "6.3.1",
@@ -3224,6 +3277,7 @@
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
@@ -3236,6 +3290,7 @@
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
"integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=18.12"
},
@@ -3275,10 +3330,11 @@
}
},
"node_modules/typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "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"
@@ -3288,15 +3344,16 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.0.tgz",
- "integrity": "sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz",
+ "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.44.0",
- "@typescript-eslint/parser": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/utils": "8.44.0"
+ "@typescript-eslint/eslint-plugin": "8.45.0",
+ "@typescript-eslint/parser": "8.45.0",
+ "@typescript-eslint/typescript-estree": "8.45.0",
+ "@typescript-eslint/utils": "8.45.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -3369,10 +3426,11 @@
}
},
"node_modules/vite": {
- "version": "7.1.7",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz",
- "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==",
+ "version": "7.1.9",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz",
+ "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -3490,6 +3548,29 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/zod": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.11.tgz",
+ "integrity": "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
"node_modules/zustand": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz",
@@ -3905,15 +3986,18 @@
}
},
"@eslint/config-helpers": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
- "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
- "dev": true
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz",
+ "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==",
+ "dev": true,
+ "requires": {
+ "@eslint/core": "^0.16.0"
+ }
},
"@eslint/core": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
- "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
+ "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.15"
@@ -3945,9 +4029,9 @@
}
},
"@eslint/js": {
- "version": "9.36.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz",
- "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==",
+ "version": "9.37.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz",
+ "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==",
"dev": true
},
"@eslint/object-schema": {
@@ -3957,12 +4041,12 @@
"dev": true
},
"@eslint/plugin-kit": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
- "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
+ "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
"dev": true,
"requires": {
- "@eslint/core": "^0.15.2",
+ "@eslint/core": "^0.16.0",
"levn": "^0.4.1"
}
},
@@ -4077,9 +4161,9 @@
"integrity": "sha512-BwbTXpj+9QutoZLQvbttRg5x3l5468qaV2kufh+51yha1c53ep5dY4kTuZR35+3pAZxpfQerGJiQqg34ZNZ6uA=="
},
"@rolldown/pluginutils": {
- "version": "1.0.0-beta.35",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.35.tgz",
- "integrity": "sha512-slYrCpoxJUqzFDDNlvrOYRazQUNRvWPjXA17dAOISY3rDMxX6k8K4cj2H+hEYMHF81HO3uNd5rHVigAWRM5dSg==",
+ "version": "1.0.0-beta.38",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.38.tgz",
+ "integrity": "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==",
"dev": true
},
"@rollup/pluginutils": {
@@ -4327,17 +4411,17 @@
}
},
"@types/react": {
- "version": "19.1.13",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz",
- "integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.0.tgz",
+ "integrity": "sha512-1LOH8xovvsKsCBq1wnT4ntDUdCJKmnEakhsuoUSy6ExlHCkGP2hqnatagYTgFk6oeL0VU31u7SNjunPN+GchtA==",
"requires": {
"csstype": "^3.0.2"
}
},
"@types/react-dom": {
- "version": "19.1.9",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.9.tgz",
- "integrity": "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.0.tgz",
+ "integrity": "sha512-brtBs0MnE9SMx7px208g39lRmC5uHZs96caOJfTjFcYSLHNamvaSMfJNagChVNkup2SdtOxKX1FDBkRSJe1ZAg==",
"dev": true,
"requires": {}
},
@@ -4348,16 +4432,16 @@
"requires": {}
},
"@typescript-eslint/eslint-plugin": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.0.tgz",
- "integrity": "sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz",
+ "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==",
"dev": true,
"requires": {
"@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/type-utils": "8.44.0",
- "@typescript-eslint/utils": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
+ "@typescript-eslint/scope-manager": "8.45.0",
+ "@typescript-eslint/type-utils": "8.45.0",
+ "@typescript-eslint/utils": "8.45.0",
+ "@typescript-eslint/visitor-keys": "8.45.0",
"graphemer": "^1.4.0",
"ignore": "^7.0.0",
"natural-compare": "^1.4.0",
@@ -4373,75 +4457,75 @@
}
},
"@typescript-eslint/parser": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.0.tgz",
- "integrity": "sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz",
+ "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==",
"dev": true,
"requires": {
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
+ "@typescript-eslint/scope-manager": "8.45.0",
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/typescript-estree": "8.45.0",
+ "@typescript-eslint/visitor-keys": "8.45.0",
"debug": "^4.3.4"
}
},
"@typescript-eslint/project-service": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.0.tgz",
- "integrity": "sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz",
+ "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==",
"dev": true,
"requires": {
- "@typescript-eslint/tsconfig-utils": "^8.44.0",
- "@typescript-eslint/types": "^8.44.0",
+ "@typescript-eslint/tsconfig-utils": "^8.45.0",
+ "@typescript-eslint/types": "^8.45.0",
"debug": "^4.3.4"
}
},
"@typescript-eslint/scope-manager": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.0.tgz",
- "integrity": "sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz",
+ "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==",
"dev": true,
"requires": {
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0"
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/visitor-keys": "8.45.0"
}
},
"@typescript-eslint/tsconfig-utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.0.tgz",
- "integrity": "sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz",
+ "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==",
"dev": true,
"requires": {}
},
"@typescript-eslint/type-utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.0.tgz",
- "integrity": "sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz",
+ "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==",
"dev": true,
"requires": {
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/utils": "8.44.0",
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/typescript-estree": "8.45.0",
+ "@typescript-eslint/utils": "8.45.0",
"debug": "^4.3.4",
"ts-api-utils": "^2.1.0"
}
},
"@typescript-eslint/types": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.0.tgz",
- "integrity": "sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz",
+ "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.0.tgz",
- "integrity": "sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz",
+ "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==",
"dev": true,
"requires": {
- "@typescript-eslint/project-service": "8.44.0",
- "@typescript-eslint/tsconfig-utils": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/visitor-keys": "8.44.0",
+ "@typescript-eslint/project-service": "8.45.0",
+ "@typescript-eslint/tsconfig-utils": "8.45.0",
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/visitor-keys": "8.45.0",
"debug": "^4.3.4",
"fast-glob": "^3.3.2",
"is-glob": "^4.0.3",
@@ -4477,37 +4561,37 @@
}
},
"@typescript-eslint/utils": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.0.tgz",
- "integrity": "sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz",
+ "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.44.0",
- "@typescript-eslint/types": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0"
+ "@typescript-eslint/scope-manager": "8.45.0",
+ "@typescript-eslint/types": "8.45.0",
+ "@typescript-eslint/typescript-estree": "8.45.0"
}
},
"@typescript-eslint/visitor-keys": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.0.tgz",
- "integrity": "sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz",
+ "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==",
"dev": true,
"requires": {
- "@typescript-eslint/types": "8.44.0",
+ "@typescript-eslint/types": "8.45.0",
"eslint-visitor-keys": "^4.2.1"
}
},
"@vitejs/plugin-react": {
- "version": "5.0.3",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.3.tgz",
- "integrity": "sha512-PFVHhosKkofGH0Yzrw1BipSedTH68BFF8ZWy1kfUpCtJcouXXY0+racG8sExw7hw0HoX36813ga5o3LTWZ4FUg==",
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.4.tgz",
+ "integrity": "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA==",
"dev": true,
"requires": {
"@babel/core": "^7.28.4",
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
- "@rolldown/pluginutils": "1.0.0-beta.35",
+ "@rolldown/pluginutils": "1.0.0-beta.38",
"@types/babel__core": "^7.20.5",
"react-refresh": "^0.17.0"
}
@@ -4734,19 +4818,19 @@
"dev": true
},
"eslint": {
- "version": "9.36.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz",
- "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==",
+ "version": "9.37.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz",
+ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
"dev": true,
"requires": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.21.0",
- "@eslint/config-helpers": "^0.3.1",
- "@eslint/core": "^0.15.2",
+ "@eslint/config-helpers": "^0.4.0",
+ "@eslint/core": "^0.16.0",
"@eslint/eslintrc": "^3.3.1",
- "@eslint/js": "9.36.0",
- "@eslint/plugin-kit": "^0.3.5",
+ "@eslint/js": "9.37.0",
+ "@eslint/plugin-kit": "^0.4.0",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
@@ -4777,16 +4861,21 @@
}
},
"eslint-plugin-react-hooks": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
- "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-6.1.1.tgz",
+ "integrity": "sha512-St9EKZzOAQF704nt2oJvAKZHjhrpg25ClQoaAlHmPZuajFldVLqRDW4VBNAS01NzeiQF0m0qhG1ZA807K6aVaQ==",
"dev": true,
- "requires": {}
+ "requires": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "zod": "^3.22.4 || ^4.0.0",
+ "zod-validation-error": "^3.0.3 || ^4.0.0"
+ }
},
"eslint-plugin-react-refresh": {
- "version": "0.4.20",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz",
- "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==",
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.23.tgz",
+ "integrity": "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA==",
"dev": true,
"requires": {}
},
@@ -5327,16 +5416,16 @@
"dev": true
},
"react": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz",
- "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
+ "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="
},
"react-dom": {
- "version": "19.1.1",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz",
- "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==",
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
+ "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
"requires": {
- "scheduler": "^0.26.0"
+ "scheduler": "^0.27.0"
}
},
"react-refresh": {
@@ -5346,20 +5435,20 @@
"dev": true
},
"react-router": {
- "version": "7.9.1",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.1.tgz",
- "integrity": "sha512-pfAByjcTpX55mqSDGwGnY9vDCpxqBLASg0BMNAuMmpSGESo/TaOUG6BllhAtAkCGx8Rnohik/XtaqiYUJtgW2g==",
+ "version": "7.9.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.3.tgz",
+ "integrity": "sha512-4o2iWCFIwhI/eYAIL43+cjORXYn/aRQPgtFRRZb3VzoyQ5Uej0Bmqj7437L97N9NJW4wnicSwLOLS+yCXfAPgg==",
"requires": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0"
}
},
"react-router-dom": {
- "version": "7.9.1",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.1.tgz",
- "integrity": "sha512-U9WBQssBE9B1vmRjo9qTM7YRzfZ3lUxESIZnsf4VjR/lXYz9MHjvOxHzr/aUm4efpktbVOrF09rL/y4VHa8RMw==",
+ "version": "7.9.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.3.tgz",
+ "integrity": "sha512-1QSbA0TGGFKTAc/aWjpfW/zoEukYfU4dc1dLkT/vvf54JoGMkW+fNA+3oyo2gWVW1GM7BxjJVHz5GnPJv40rvg==",
"requires": {
- "react-router": "7.9.1"
+ "react-router": "7.9.3"
}
},
"react-scan": {
@@ -5446,9 +5535,9 @@
}
},
"scheduler": {
- "version": "0.26.0",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
- "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="
+ "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=="
},
"semver": {
"version": "6.3.1",
@@ -5547,21 +5636,21 @@
}
},
"typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true
},
"typescript-eslint": {
- "version": "8.44.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.0.tgz",
- "integrity": "sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==",
+ "version": "8.45.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz",
+ "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==",
"dev": true,
"requires": {
- "@typescript-eslint/eslint-plugin": "8.44.0",
- "@typescript-eslint/parser": "8.44.0",
- "@typescript-eslint/typescript-estree": "8.44.0",
- "@typescript-eslint/utils": "8.44.0"
+ "@typescript-eslint/eslint-plugin": "8.45.0",
+ "@typescript-eslint/parser": "8.45.0",
+ "@typescript-eslint/typescript-estree": "8.45.0",
+ "@typescript-eslint/utils": "8.45.0"
}
},
"undici-types": {
@@ -5598,9 +5687,9 @@
}
},
"vite": {
- "version": "7.1.7",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz",
- "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==",
+ "version": "7.1.9",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz",
+ "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==",
"dev": true,
"requires": {
"esbuild": "^0.25.0",
@@ -5644,6 +5733,19 @@
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true
},
+ "zod": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.11.tgz",
+ "integrity": "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==",
+ "dev": true
+ },
+ "zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "requires": {}
+ },
"zustand": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz",
diff --git a/package.json b/package.json
index 46ad7db..ec92a4d 100644
--- a/package.json
+++ b/package.json
@@ -13,24 +13,24 @@
},
"dependencies": {
"@tanstack/react-query": "^5.90.2",
- "react": "^19.1.1",
- "react-dom": "^19.1.1",
- "react-router-dom": "^7.9.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "react-router-dom": "^7.9.3",
"react-scan": "^0.4.3",
"zustand": "^5.0.8"
},
"devDependencies": {
- "@eslint/js": "^9.36.0",
- "@types/react": "^19.1.13",
- "@types/react-dom": "^19.1.9",
- "@vitejs/plugin-react": "^5.0.3",
- "eslint": "^9.36.0",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
+ "@eslint/js": "^9.37.0",
+ "@types/react": "^19.2.0",
+ "@types/react-dom": "^19.2.0",
+ "@vitejs/plugin-react": "^5.0.4",
+ "eslint": "^9.37.0",
+ "eslint-plugin-react-hooks": "^6.1.1",
+ "eslint-plugin-react-refresh": "^0.4.23",
"globals": "^16.4.0",
"prettier": "^3.6.2",
- "typescript": "~5.8.3",
- "typescript-eslint": "^8.44.0",
- "vite": "^7.1.7"
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.45.0",
+ "vite": "^7.1.9"
}
}
diff --git a/src/Home.tsx b/src/Home.tsx
index 1400cdf..5aa8d15 100644
--- a/src/Home.tsx
+++ b/src/Home.tsx
@@ -12,16 +12,6 @@ export function Home() {
A collection of React performance examples and demos to help you
understand various optimization techniques and patterns.
-
- The RenderToken
shows render counts. When
- that value increases, the component just re-rendered. It will be much
- higher than a well implemented example.
-
-
- Enable react-scan
to see performance differences with DOM
- render times. It will disable RenderToken
. Refresh the page
- to turn off react-scan
-
{routes.map(({ id, path, title, description }) => (
@@ -34,7 +24,15 @@ export function Home() {
Demo App layout
Here is what the app looks like in each example. Each component is named
- below
+ below.
+
+
+ The RenderToken
shows render counts. When
+ that value increases, the component just re-rendered.
+
+
+ Enable react-scan
to see performance differences with DOM
+ render times. It will disable RenderToken
.
diff --git a/src/api/worlds.ts b/src/api/worlds.ts
index 4f8b9eb..e2a7ed4 100644
--- a/src/api/worlds.ts
+++ b/src/api/worlds.ts
@@ -1,6 +1,14 @@
-import type { World } from "../types/World";
import { getNextWorld } from "../examples/zustand/data/solarSystemWorlds";
+export type World = {
+ id: string;
+ name: string;
+ distanceFromSun: string;
+ diameter: string;
+ orbitalPeriod: string;
+ type: string;
+};
+
export type WorldsResponse = {
worlds: World[];
};
diff --git a/src/components/DebugInfo.tsx b/src/components/DebugInfo.tsx
index 3fb1f7c..4774ea1 100644
--- a/src/components/DebugInfo.tsx
+++ b/src/components/DebugInfo.tsx
@@ -1,7 +1,7 @@
import { memo } from "react";
import css from "./css/DebugInfo.module.css";
import sharedStyles from "./css/shared.module.css";
-import type { World } from "../types/World.ts";
+import type { World } from "../api/worlds.ts";
type Snapshot = {
selectedWorldId: string;
diff --git a/src/examples/propDrilling/PropDrillingRenderDemo.tsx b/src/examples/propDrilling/PropDrillingRenderDemo.tsx
index 794f9dd..285c0f0 100644
--- a/src/examples/propDrilling/PropDrillingRenderDemo.tsx
+++ b/src/examples/propDrilling/PropDrillingRenderDemo.tsx
@@ -1,11 +1,9 @@
import { useState } from "react";
import { useLoaderData, useRevalidator } from "react-router-dom";
import css from "../../components/css/DemoLayout.module.css";
-import { RenderToken } from "../../components/perf/RenderToken.tsx";
-import type { World } from "../../types/World.ts";
import { WorldApp } from "./exampleComponents/WorldApp.tsx";
import { DebugInfo } from "../../components/DebugInfo.tsx";
-import type { WorldsResponse } from "../../api/worlds.ts";
+import type { World, WorldsResponse } from "../../api/worlds.ts";
type Snapshot = {
selectedWorldId: string;
@@ -21,25 +19,12 @@ export function PropDrillingRenderDemo() {
Optimized Prop Drilling Demo
- This demo highlights an optimized prop drilling flow: state still lives
+ This demo highlights an optimized prop drilling flow. State still lives
at the top, but children receive stable data and callbacks via
- React.memo
, useMemo
, and{" "}
+ React.memo
, useMemo
, and
useCallback
. Keeping shapes consistent across layers
prevents prop churn and keeps memoized children from re-rendering
- unnecessarily—even if a dedicated state store would still be leaner.
-
-
- The RenderToken
shows render counts. When
- that value increases, the component just re-rendered. This demo shows
- noticeably lower counts across the tree than the naive version, making
- it easier to see how the optimizations pay off in practice.
-
-
-
Instructions:
-
- Click Add world to simulate changing data. Select a
- world from the World Selector to change the highlighted
- item.
+ unnecessarily.
{
+ onSnapshotChangeRef.current = onSnapshotChange;
+ }, [onSnapshotChange]);
const stableOnSnapshotChange = useCallback(
(snapshot: { selectedWorldId: string; hello: { worlds: World[] } }) => {
diff --git a/src/examples/propDrilling/exampleComponents/worldSelector/WorldSelector.tsx b/src/examples/propDrilling/exampleComponents/worldSelector/WorldSelector.tsx
index e3dfbbb..2187d34 100644
--- a/src/examples/propDrilling/exampleComponents/worldSelector/WorldSelector.tsx
+++ b/src/examples/propDrilling/exampleComponents/worldSelector/WorldSelector.tsx
@@ -4,7 +4,7 @@ import sharedStyles from "../../../../components/css/shared.module.css";
import css from "../../../../components/css/worldSelector/WorldSelector.module.css";
import { AddWorldButton } from "./AddWorld.tsx";
import { WorldIdButton } from "./WorldIdButton.tsx";
-import type { World } from "../../data/types.ts";
+import type { World } from "../../../../api/worlds";
type Props = {
activeWorld: string;
diff --git a/src/examples/propDrilling/exampleComponents/worldsViewer/World.tsx b/src/examples/propDrilling/exampleComponents/worldsViewer/World.tsx
index a564b97..77cd23f 100644
--- a/src/examples/propDrilling/exampleComponents/worldsViewer/World.tsx
+++ b/src/examples/propDrilling/exampleComponents/worldsViewer/World.tsx
@@ -2,7 +2,7 @@ import { memo, useEffect, useRef } from "react";
import { RenderToken } from "../../../../components/perf/RenderToken.tsx";
import css from "../../../../components/css/worldsViewer/World.module.css";
import { WorldInfo } from "./WorldInfo.tsx";
-import type { World as WorldType } from "../../data/types.ts";
+import type { World as WorldType } from "../../../../api/worlds";
type Props = {
world: WorldType;
diff --git a/src/examples/propDrilling/exampleComponents/worldsViewer/WorldInfo.tsx b/src/examples/propDrilling/exampleComponents/worldsViewer/WorldInfo.tsx
index 8e8a292..4e44f0c 100644
--- a/src/examples/propDrilling/exampleComponents/worldsViewer/WorldInfo.tsx
+++ b/src/examples/propDrilling/exampleComponents/worldsViewer/WorldInfo.tsx
@@ -1,7 +1,7 @@
import { memo, useMemo } from "react";
import { RenderToken } from "../../../../components/perf/RenderToken.tsx";
import css from "../../../../components/css/worldsViewer/WorldInfo.module.css";
-import type { World } from "../../data/types.ts";
+import type { World } from "../../../../api/worlds";
type Props = {
world: World;
diff --git a/src/examples/propDrilling/exampleComponents/worldsViewer/WorldList.tsx b/src/examples/propDrilling/exampleComponents/worldsViewer/WorldList.tsx
index ca0e961..0d87bba 100644
--- a/src/examples/propDrilling/exampleComponents/worldsViewer/WorldList.tsx
+++ b/src/examples/propDrilling/exampleComponents/worldsViewer/WorldList.tsx
@@ -2,7 +2,7 @@ import { memo } from "react";
import { RenderToken } from "../../../../components/perf/RenderToken.tsx";
import css from "../../../../components/css/worldsViewer/WorldList.module.css";
import { World } from "./World.tsx";
-import type { World as WorldType } from "../../data/types.ts";
+import type { World as WorldType } from "../../../../api/worlds";
type Props = {
worlds: WorldType[];
diff --git a/src/examples/propDrilling/exampleComponents/worldsViewer/WorldsViewer.tsx b/src/examples/propDrilling/exampleComponents/worldsViewer/WorldsViewer.tsx
index c7310d9..0def90a 100644
--- a/src/examples/propDrilling/exampleComponents/worldsViewer/WorldsViewer.tsx
+++ b/src/examples/propDrilling/exampleComponents/worldsViewer/WorldsViewer.tsx
@@ -2,7 +2,7 @@ import { memo } from "react";
import { RenderToken } from "../../../../components/perf/RenderToken.tsx";
import sharedStyles from "../../../../components/css/shared.module.css";
import { WorldList } from "./WorldList.tsx";
-import type { World } from "../../data/types.ts";
+import type { World } from "../../../../api/worlds";
type Props = {
worlds: World[];
diff --git a/src/examples/propDrillingNaive/PropDrillingNaiveRenderDemo.tsx b/src/examples/propDrillingNaive/PropDrillingNaiveRenderDemo.tsx
index 7430b6a..1e7ca7e 100644
--- a/src/examples/propDrillingNaive/PropDrillingNaiveRenderDemo.tsx
+++ b/src/examples/propDrillingNaive/PropDrillingNaiveRenderDemo.tsx
@@ -1,10 +1,8 @@
import { useState, useEffect } from "react";
import css from "../../components/css/DemoLayout.module.css";
-import { RenderToken } from "../../components/perf/RenderToken.tsx";
-import type { World } from "../../types/World.ts";
import { PropDrillingWorldApp } from "./exampleComponents/WorldApp.tsx";
import { DebugInfo } from "../../components/DebugInfo.tsx";
-import { fetchWorlds } from "../../api/worlds.ts";
+import { fetchWorlds, type World } from "../../api/worlds.ts";
type Snapshot = {
selectedWorldId: string;
@@ -27,22 +25,8 @@ export function PropDrillingNaiveRenderDemo() {
Prop Drilling Render Demo
This intentionally clumsy demo uses anti-patterns commonly seen with
- prop drilling. All state sits at the top of the tree, forcing every
- child to receive new prop objects each render—causing relentless prop
- churn, broken memoization, and a cascade of unnecessary updates.
-
-
- The RenderToken
shows render counts. When
- that value increases, the component just re-rendered. Expect the counter
- to spike across multiple components compared to healthier patterns, so
- use it to spot how far the churn propagates.
-
-
- Instructions:
-
- Click Add world to simulate changing data. Select a
- world from the World Selector to change the highlighted
- item.
+ prop drilling. Prop churn, broken memoization, and a cascade of
+ unnecessary updates.
(
🌤 Zustand + tanstack-query/react
-
Puts API state in tanstack/react-query and client state in Zustand.
- The WorldsSelector
and WorldsViewer
components
- are siblings. The WorldsViewer
component has a child
- component WorldList
.
+ Puts API state in tanstack-query/react and client state in Zustand.
+ Since tanstack-query is a specialized tool for managing api data, its
+ more fully featured for handling data specific needs.
-
-
Instructions:
-
- Simulate API data changing by clicking Add world{" "}
- (notice the loading state and error handling). Simulate client state
- changing by clicking a different world in the{" "}
- World Selector.
-
-
diff --git a/src/examples/zustand-query/data/solarSystemWorlds.ts b/src/examples/zustand-query/data/solarSystemWorlds.ts
index 4eed5c5..76a86c5 100644
--- a/src/examples/zustand-query/data/solarSystemWorlds.ts
+++ b/src/examples/zustand-query/data/solarSystemWorlds.ts
@@ -1,4 +1,4 @@
-import type { World } from "./types";
+import type { World } from "../../../api/worlds";
export const SOLAR_SYSTEM_WORLDS: World[] = [
{
diff --git a/src/examples/zustand-query/data/types.ts b/src/examples/zustand-query/data/types.ts
deleted file mode 100644
index bcfa475..0000000
--- a/src/examples/zustand-query/data/types.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export type World = {
- id: string;
- name: string;
- distanceFromSun: string;
- diameter: string;
- orbitalPeriod: string;
- type: string;
-};
diff --git a/src/examples/zustand/ZustandRenderDemo.tsx b/src/examples/zustand/ZustandRenderDemo.tsx
index 644a86c..cf7ac7a 100644
--- a/src/examples/zustand/ZustandRenderDemo.tsx
+++ b/src/examples/zustand/ZustandRenderDemo.tsx
@@ -2,7 +2,6 @@ import { useEffect } from "react";
import { useLoaderData, useRevalidator } from "react-router-dom";
import css from "../../components/css/DemoLayout.module.css";
import { DebugInfo } from "../../components/DebugInfo.tsx";
-import { RenderToken } from "../../components/perf/RenderToken.tsx";
import { WorldApp } from "./exampleComponents/WorldApp.tsx";
import { useWorldStore } from "./data/WorldStore.tsx";
import type { WorldsResponse } from "../../api/worlds.ts";
@@ -31,16 +30,8 @@ export function ZustandRenderDemo() {
Zustand Render Demo
This demo shows you can prevent re-rendering without using prop
- drilling. The RenderToken
is a counter
- which counts each render. When that value increases, the component just
- re-rendered.
-
-
- Instructions:
-
- Simulate API data changing by clicking Add world.
- Simulate client state changing by clicking a different world in the{" "}
- World Selector.
+ drilling. Because a zustand is used, its internal data pub/sub is more
+ efficient than React memoization.
diff --git a/src/examples/zustand/data/WorldStore.tsx b/src/examples/zustand/data/WorldStore.tsx
index 0e71735..70ffb9d 100644
--- a/src/examples/zustand/data/WorldStore.tsx
+++ b/src/examples/zustand/data/WorldStore.tsx
@@ -1,5 +1,6 @@
import { create } from "zustand";
-import type { World } from "../../../types/World.ts";
+
+import type { World } from "../../../api/worlds.ts";
interface Store {
selectedWorldId: string;
diff --git a/src/examples/zustand/data/solarSystemWorlds.ts b/src/examples/zustand/data/solarSystemWorlds.ts
index b9a3d80..76a86c5 100644
--- a/src/examples/zustand/data/solarSystemWorlds.ts
+++ b/src/examples/zustand/data/solarSystemWorlds.ts
@@ -1,4 +1,4 @@
-import type { World } from "./types.ts";
+import type { World } from "../../../api/worlds";
export const SOLAR_SYSTEM_WORLDS: World[] = [
{
diff --git a/src/examples/zustand/data/types.ts b/src/examples/zustand/data/types.ts
deleted file mode 100644
index bcfa475..0000000
--- a/src/examples/zustand/data/types.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export type World = {
- id: string;
- name: string;
- distanceFromSun: string;
- diameter: string;
- orbitalPeriod: string;
- type: string;
-};
diff --git a/src/routes.ts b/src/routes.ts
index c39e146..463f65e 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -36,14 +36,14 @@ export const routes: Route[] = [
navLabel: "🌤️ Zustand",
title: "🌤️ Zustand",
description:
- "Both API and client state are in zustand. Optimized to prevent re-rendering. Props are passed only as lookup keys",
+ "Both API and client state are in zustand. Optimized to prevent re-rendering. Props are passed only as lookup keys.",
},
{
id: "zustand-query",
path: "/zustand-query",
- navLabel: "🌤 RQ+Zustand",
- title: "🌤 tanstack-query/react + Zustand",
+ navLabel: "🌤 Zustand+tanstack",
+ title: "🌤 Zustand + tanstack-query/react",
description:
- "API state in tanstack-query/react, client state in zustand. Shows how you can combine state management",
+ "API state in tanstack-query/react, client state in zustand. Shows how you can combine state from multiple libraries.",
},
];
diff --git a/src/types/World.ts b/src/types/World.ts
deleted file mode 100644
index fb48961..0000000
--- a/src/types/World.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * Shared type definitions used across multiple examples.
- * This ensures consistency and reduces coupling between examples.
- */
-
-export type World = {
- id: string;
- name: string;
- distanceFromSun: string;
- diameter: string;
- orbitalPeriod: string;
- type: string;
-};