Your calm window into the Atmosphere. morgen.blue
rss atproto
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #1 from hfrdmnk/feat/react-tanstack-start

feat: migrate from SvelteKit to React 19 + TanStack Start

authored by

Dominik Hofer and committed by
GitHub
60efe9fe 081e965b

+6810 -1004
+2934
.claude/skills/react-best-practices/AGENTS.md
··· 1 + # React Best Practices 2 + 3 + **Version 1.0.0** 4 + Vercel Engineering 5 + January 2026 6 + 7 + > **Note:** 8 + > This document is mainly for agents and LLMs to follow when maintaining, 9 + > generating, or refactoring React and Next.js codebases. Humans 10 + > may also find it useful, but guidance here is optimized for automation 11 + > and consistency by AI-assisted workflows. 12 + 13 + --- 14 + 15 + ## Abstract 16 + 17 + Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation. 18 + 19 + --- 20 + 21 + ## Table of Contents 22 + 23 + 1. [Eliminating Waterfalls](#1-eliminating-waterfalls) — **CRITICAL** 24 + - 1.1 [Defer Await Until Needed](#11-defer-await-until-needed) 25 + - 1.2 [Dependency-Based Parallelization](#12-dependency-based-parallelization) 26 + - 1.3 [Prevent Waterfall Chains in API Routes](#13-prevent-waterfall-chains-in-api-routes) 27 + - 1.4 [Promise.all() for Independent Operations](#14-promiseall-for-independent-operations) 28 + - 1.5 [Strategic Suspense Boundaries](#15-strategic-suspense-boundaries) 29 + 2. [Bundle Size Optimization](#2-bundle-size-optimization) — **CRITICAL** 30 + - 2.1 [Avoid Barrel File Imports](#21-avoid-barrel-file-imports) 31 + - 2.2 [Conditional Module Loading](#22-conditional-module-loading) 32 + - 2.3 [Defer Non-Critical Third-Party Libraries](#23-defer-non-critical-third-party-libraries) 33 + - 2.4 [Dynamic Imports for Heavy Components](#24-dynamic-imports-for-heavy-components) 34 + - 2.5 [Preload Based on User Intent](#25-preload-based-on-user-intent) 35 + 3. [Server-Side Performance](#3-server-side-performance) — **HIGH** 36 + - 3.1 [Authenticate Server Actions Like API Routes](#31-authenticate-server-actions-like-api-routes) 37 + - 3.2 [Avoid Duplicate Serialization in RSC Props](#32-avoid-duplicate-serialization-in-rsc-props) 38 + - 3.3 [Cross-Request LRU Caching](#33-cross-request-lru-caching) 39 + - 3.4 [Minimize Serialization at RSC Boundaries](#34-minimize-serialization-at-rsc-boundaries) 40 + - 3.5 [Parallel Data Fetching with Component Composition](#35-parallel-data-fetching-with-component-composition) 41 + - 3.6 [Per-Request Deduplication with React.cache()](#36-per-request-deduplication-with-reactcache) 42 + - 3.7 [Use after() for Non-Blocking Operations](#37-use-after-for-non-blocking-operations) 43 + 4. [Client-Side Data Fetching](#4-client-side-data-fetching) — **MEDIUM-HIGH** 44 + - 4.1 [Deduplicate Global Event Listeners](#41-deduplicate-global-event-listeners) 45 + - 4.2 [Use Passive Event Listeners for Scrolling Performance](#42-use-passive-event-listeners-for-scrolling-performance) 46 + - 4.3 [Use SWR for Automatic Deduplication](#43-use-swr-for-automatic-deduplication) 47 + - 4.4 [Version and Minimize localStorage Data](#44-version-and-minimize-localstorage-data) 48 + 5. [Re-render Optimization](#5-re-render-optimization) — **MEDIUM** 49 + - 5.1 [Calculate Derived State During Rendering](#51-calculate-derived-state-during-rendering) 50 + - 5.2 [Defer State Reads to Usage Point](#52-defer-state-reads-to-usage-point) 51 + - 5.3 [Do not wrap a simple expression with a primitive result type in useMemo](#53-do-not-wrap-a-simple-expression-with-a-primitive-result-type-in-usememo) 52 + - 5.4 [Extract Default Non-primitive Parameter Value from Memoized Component to Constant](#54-extract-default-non-primitive-parameter-value-from-memoized-component-to-constant) 53 + - 5.5 [Extract to Memoized Components](#55-extract-to-memoized-components) 54 + - 5.6 [Narrow Effect Dependencies](#56-narrow-effect-dependencies) 55 + - 5.7 [Put Interaction Logic in Event Handlers](#57-put-interaction-logic-in-event-handlers) 56 + - 5.8 [Subscribe to Derived State](#58-subscribe-to-derived-state) 57 + - 5.9 [Use Functional setState Updates](#59-use-functional-setstate-updates) 58 + - 5.10 [Use Lazy State Initialization](#510-use-lazy-state-initialization) 59 + - 5.11 [Use Transitions for Non-Urgent Updates](#511-use-transitions-for-non-urgent-updates) 60 + - 5.12 [Use useRef for Transient Values](#512-use-useref-for-transient-values) 61 + 6. [Rendering Performance](#6-rendering-performance) — **MEDIUM** 62 + - 6.1 [Animate SVG Wrapper Instead of SVG Element](#61-animate-svg-wrapper-instead-of-svg-element) 63 + - 6.2 [CSS content-visibility for Long Lists](#62-css-content-visibility-for-long-lists) 64 + - 6.3 [Hoist Static JSX Elements](#63-hoist-static-jsx-elements) 65 + - 6.4 [Optimize SVG Precision](#64-optimize-svg-precision) 66 + - 6.5 [Prevent Hydration Mismatch Without Flickering](#65-prevent-hydration-mismatch-without-flickering) 67 + - 6.6 [Suppress Expected Hydration Mismatches](#66-suppress-expected-hydration-mismatches) 68 + - 6.7 [Use Activity Component for Show/Hide](#67-use-activity-component-for-showhide) 69 + - 6.8 [Use Explicit Conditional Rendering](#68-use-explicit-conditional-rendering) 70 + - 6.9 [Use useTransition Over Manual Loading States](#69-use-usetransition-over-manual-loading-states) 71 + 7. [JavaScript Performance](#7-javascript-performance) — **LOW-MEDIUM** 72 + - 7.1 [Avoid Layout Thrashing](#71-avoid-layout-thrashing) 73 + - 7.2 [Build Index Maps for Repeated Lookups](#72-build-index-maps-for-repeated-lookups) 74 + - 7.3 [Cache Property Access in Loops](#73-cache-property-access-in-loops) 75 + - 7.4 [Cache Repeated Function Calls](#74-cache-repeated-function-calls) 76 + - 7.5 [Cache Storage API Calls](#75-cache-storage-api-calls) 77 + - 7.6 [Combine Multiple Array Iterations](#76-combine-multiple-array-iterations) 78 + - 7.7 [Early Length Check for Array Comparisons](#77-early-length-check-for-array-comparisons) 79 + - 7.8 [Early Return from Functions](#78-early-return-from-functions) 80 + - 7.9 [Hoist RegExp Creation](#79-hoist-regexp-creation) 81 + - 7.10 [Use Loop for Min/Max Instead of Sort](#710-use-loop-for-minmax-instead-of-sort) 82 + - 7.11 [Use Set/Map for O(1) Lookups](#711-use-setmap-for-o1-lookups) 83 + - 7.12 [Use toSorted() Instead of sort() for Immutability](#712-use-tosorted-instead-of-sort-for-immutability) 84 + 8. [Advanced Patterns](#8-advanced-patterns) — **LOW** 85 + - 8.1 [Initialize App Once, Not Per Mount](#81-initialize-app-once-not-per-mount) 86 + - 8.2 [Store Event Handlers in Refs](#82-store-event-handlers-in-refs) 87 + - 8.3 [useEffectEvent for Stable Callback Refs](#83-useeffectevent-for-stable-callback-refs) 88 + 89 + --- 90 + 91 + ## 1. Eliminating Waterfalls 92 + 93 + **Impact: CRITICAL** 94 + 95 + Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains. 96 + 97 + ### 1.1 Defer Await Until Needed 98 + 99 + **Impact: HIGH (avoids blocking unused code paths)** 100 + 101 + Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them. 102 + 103 + **Incorrect: blocks both branches** 104 + 105 + ```typescript 106 + async function handleRequest(userId: string, skipProcessing: boolean) { 107 + const userData = await fetchUserData(userId) 108 + 109 + if (skipProcessing) { 110 + // Returns immediately but still waited for userData 111 + return { skipped: true } 112 + } 113 + 114 + // Only this branch uses userData 115 + return processUserData(userData) 116 + } 117 + ``` 118 + 119 + **Correct: only blocks when needed** 120 + 121 + ```typescript 122 + async function handleRequest(userId: string, skipProcessing: boolean) { 123 + if (skipProcessing) { 124 + // Returns immediately without waiting 125 + return { skipped: true } 126 + } 127 + 128 + // Fetch only when needed 129 + const userData = await fetchUserData(userId) 130 + return processUserData(userData) 131 + } 132 + ``` 133 + 134 + **Another example: early return optimization** 135 + 136 + ```typescript 137 + // Incorrect: always fetches permissions 138 + async function updateResource(resourceId: string, userId: string) { 139 + const permissions = await fetchPermissions(userId) 140 + const resource = await getResource(resourceId) 141 + 142 + if (!resource) { 143 + return { error: 'Not found' } 144 + } 145 + 146 + if (!permissions.canEdit) { 147 + return { error: 'Forbidden' } 148 + } 149 + 150 + return await updateResourceData(resource, permissions) 151 + } 152 + 153 + // Correct: fetches only when needed 154 + async function updateResource(resourceId: string, userId: string) { 155 + const resource = await getResource(resourceId) 156 + 157 + if (!resource) { 158 + return { error: 'Not found' } 159 + } 160 + 161 + const permissions = await fetchPermissions(userId) 162 + 163 + if (!permissions.canEdit) { 164 + return { error: 'Forbidden' } 165 + } 166 + 167 + return await updateResourceData(resource, permissions) 168 + } 169 + ``` 170 + 171 + This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive. 172 + 173 + ### 1.2 Dependency-Based Parallelization 174 + 175 + **Impact: CRITICAL (2-10× improvement)** 176 + 177 + For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment. 178 + 179 + **Incorrect: profile waits for config unnecessarily** 180 + 181 + ```typescript 182 + const [user, config] = await Promise.all([ 183 + fetchUser(), 184 + fetchConfig() 185 + ]) 186 + const profile = await fetchProfile(user.id) 187 + ``` 188 + 189 + **Correct: config and profile run in parallel** 190 + 191 + ```typescript 192 + import { all } from 'better-all' 193 + 194 + const { user, config, profile } = await all({ 195 + async user() { return fetchUser() }, 196 + async config() { return fetchConfig() }, 197 + async profile() { 198 + return fetchProfile((await this.$.user).id) 199 + } 200 + }) 201 + ``` 202 + 203 + **Alternative without extra dependencies:** 204 + 205 + ```typescript 206 + const userPromise = fetchUser() 207 + const profilePromise = userPromise.then(user => fetchProfile(user.id)) 208 + 209 + const [user, config, profile] = await Promise.all([ 210 + userPromise, 211 + fetchConfig(), 212 + profilePromise 213 + ]) 214 + ``` 215 + 216 + We can also create all the promises first, and do `Promise.all()` at the end. 217 + 218 + Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all) 219 + 220 + ### 1.3 Prevent Waterfall Chains in API Routes 221 + 222 + **Impact: CRITICAL (2-10× improvement)** 223 + 224 + In API routes and Server Actions, start independent operations immediately, even if you don't await them yet. 225 + 226 + **Incorrect: config waits for auth, data waits for both** 227 + 228 + ```typescript 229 + export async function GET(request: Request) { 230 + const session = await auth() 231 + const config = await fetchConfig() 232 + const data = await fetchData(session.user.id) 233 + return Response.json({ data, config }) 234 + } 235 + ``` 236 + 237 + **Correct: auth and config start immediately** 238 + 239 + ```typescript 240 + export async function GET(request: Request) { 241 + const sessionPromise = auth() 242 + const configPromise = fetchConfig() 243 + const session = await sessionPromise 244 + const [config, data] = await Promise.all([ 245 + configPromise, 246 + fetchData(session.user.id) 247 + ]) 248 + return Response.json({ data, config }) 249 + } 250 + ``` 251 + 252 + For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization). 253 + 254 + ### 1.4 Promise.all() for Independent Operations 255 + 256 + **Impact: CRITICAL (2-10× improvement)** 257 + 258 + When async operations have no interdependencies, execute them concurrently using `Promise.all()`. 259 + 260 + **Incorrect: sequential execution, 3 round trips** 261 + 262 + ```typescript 263 + const user = await fetchUser() 264 + const posts = await fetchPosts() 265 + const comments = await fetchComments() 266 + ``` 267 + 268 + **Correct: parallel execution, 1 round trip** 269 + 270 + ```typescript 271 + const [user, posts, comments] = await Promise.all([ 272 + fetchUser(), 273 + fetchPosts(), 274 + fetchComments() 275 + ]) 276 + ``` 277 + 278 + ### 1.5 Strategic Suspense Boundaries 279 + 280 + **Impact: HIGH (faster initial paint)** 281 + 282 + Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads. 283 + 284 + **Incorrect: wrapper blocked by data fetching** 285 + 286 + ```tsx 287 + async function Page() { 288 + const data = await fetchData() // Blocks entire page 289 + 290 + return ( 291 + <div> 292 + <div>Sidebar</div> 293 + <div>Header</div> 294 + <div> 295 + <DataDisplay data={data} /> 296 + </div> 297 + <div>Footer</div> 298 + </div> 299 + ) 300 + } 301 + ``` 302 + 303 + The entire layout waits for data even though only the middle section needs it. 304 + 305 + **Correct: wrapper shows immediately, data streams in** 306 + 307 + ```tsx 308 + function Page() { 309 + return ( 310 + <div> 311 + <div>Sidebar</div> 312 + <div>Header</div> 313 + <div> 314 + <Suspense fallback={<Skeleton />}> 315 + <DataDisplay /> 316 + </Suspense> 317 + </div> 318 + <div>Footer</div> 319 + </div> 320 + ) 321 + } 322 + 323 + async function DataDisplay() { 324 + const data = await fetchData() // Only blocks this component 325 + return <div>{data.content}</div> 326 + } 327 + ``` 328 + 329 + Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data. 330 + 331 + **Alternative: share promise across components** 332 + 333 + ```tsx 334 + function Page() { 335 + // Start fetch immediately, but don't await 336 + const dataPromise = fetchData() 337 + 338 + return ( 339 + <div> 340 + <div>Sidebar</div> 341 + <div>Header</div> 342 + <Suspense fallback={<Skeleton />}> 343 + <DataDisplay dataPromise={dataPromise} /> 344 + <DataSummary dataPromise={dataPromise} /> 345 + </Suspense> 346 + <div>Footer</div> 347 + </div> 348 + ) 349 + } 350 + 351 + function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) { 352 + const data = use(dataPromise) // Unwraps the promise 353 + return <div>{data.content}</div> 354 + } 355 + 356 + function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) { 357 + const data = use(dataPromise) // Reuses the same promise 358 + return <div>{data.summary}</div> 359 + } 360 + ``` 361 + 362 + Both components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together. 363 + 364 + **When NOT to use this pattern:** 365 + 366 + - Critical data needed for layout decisions (affects positioning) 367 + 368 + - SEO-critical content above the fold 369 + 370 + - Small, fast queries where suspense overhead isn't worth it 371 + 372 + - When you want to avoid layout shift (loading → content jump) 373 + 374 + **Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities. 375 + 376 + --- 377 + 378 + ## 2. Bundle Size Optimization 379 + 380 + **Impact: CRITICAL** 381 + 382 + Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint. 383 + 384 + ### 2.1 Avoid Barrel File Imports 385 + 386 + **Impact: CRITICAL (200-800ms import cost, slow builds)** 387 + 388 + Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`). 389 + 390 + Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts. 391 + 392 + **Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph. 393 + 394 + **Incorrect: imports entire library** 395 + 396 + ```tsx 397 + import { Check, X, Menu } from 'lucide-react' 398 + // Loads 1,583 modules, takes ~2.8s extra in dev 399 + // Runtime cost: 200-800ms on every cold start 400 + 401 + import { Button, TextField } from '@mui/material' 402 + // Loads 2,225 modules, takes ~4.2s extra in dev 403 + ``` 404 + 405 + **Correct: imports only what you need** 406 + 407 + ```tsx 408 + import Check from 'lucide-react/dist/esm/icons/check' 409 + import X from 'lucide-react/dist/esm/icons/x' 410 + import Menu from 'lucide-react/dist/esm/icons/menu' 411 + // Loads only 3 modules (~2KB vs ~1MB) 412 + 413 + import Button from '@mui/material/Button' 414 + import TextField from '@mui/material/TextField' 415 + // Loads only what you use 416 + ``` 417 + 418 + **Alternative: Next.js 13.5+** 419 + 420 + ```js 421 + // next.config.js - use optimizePackageImports 422 + module.exports = { 423 + experimental: { 424 + optimizePackageImports: ['lucide-react', '@mui/material'] 425 + } 426 + } 427 + 428 + // Then you can keep the ergonomic barrel imports: 429 + import { Check, X, Menu } from 'lucide-react' 430 + // Automatically transformed to direct imports at build time 431 + ``` 432 + 433 + Direct imports provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR. 434 + 435 + Libraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`. 436 + 437 + Reference: [https://vercel.com/blog/how-we-optimized-package-imports-in-next-js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js) 438 + 439 + ### 2.2 Conditional Module Loading 440 + 441 + **Impact: HIGH (loads large data only when needed)** 442 + 443 + Load large data or modules only when a feature is activated. 444 + 445 + **Example: lazy-load animation frames** 446 + 447 + ```tsx 448 + function AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) { 449 + const [frames, setFrames] = useState<Frame[] | null>(null) 450 + 451 + useEffect(() => { 452 + if (enabled && !frames && typeof window !== 'undefined') { 453 + import('./animation-frames.js') 454 + .then(mod => setFrames(mod.frames)) 455 + .catch(() => setEnabled(false)) 456 + } 457 + }, [enabled, frames, setEnabled]) 458 + 459 + if (!frames) return <Skeleton /> 460 + return <Canvas frames={frames} /> 461 + } 462 + ``` 463 + 464 + The `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed. 465 + 466 + ### 2.3 Defer Non-Critical Third-Party Libraries 467 + 468 + **Impact: MEDIUM (loads after hydration)** 469 + 470 + Analytics, logging, and error tracking don't block user interaction. Load them after hydration. 471 + 472 + **Incorrect: blocks initial bundle** 473 + 474 + ```tsx 475 + import { Analytics } from '@vercel/analytics/react' 476 + 477 + export default function RootLayout({ children }) { 478 + return ( 479 + <html> 480 + <body> 481 + {children} 482 + <Analytics /> 483 + </body> 484 + </html> 485 + ) 486 + } 487 + ``` 488 + 489 + **Correct: loads after hydration** 490 + 491 + ```tsx 492 + import dynamic from 'next/dynamic' 493 + 494 + const Analytics = dynamic( 495 + () => import('@vercel/analytics/react').then(m => m.Analytics), 496 + { ssr: false } 497 + ) 498 + 499 + export default function RootLayout({ children }) { 500 + return ( 501 + <html> 502 + <body> 503 + {children} 504 + <Analytics /> 505 + </body> 506 + </html> 507 + ) 508 + } 509 + ``` 510 + 511 + ### 2.4 Dynamic Imports for Heavy Components 512 + 513 + **Impact: CRITICAL (directly affects TTI and LCP)** 514 + 515 + Use `next/dynamic` to lazy-load large components not needed on initial render. 516 + 517 + **Incorrect: Monaco bundles with main chunk ~300KB** 518 + 519 + ```tsx 520 + import { MonacoEditor } from './monaco-editor' 521 + 522 + function CodePanel({ code }: { code: string }) { 523 + return <MonacoEditor value={code} /> 524 + } 525 + ``` 526 + 527 + **Correct: Monaco loads on demand** 528 + 529 + ```tsx 530 + import dynamic from 'next/dynamic' 531 + 532 + const MonacoEditor = dynamic( 533 + () => import('./monaco-editor').then(m => m.MonacoEditor), 534 + { ssr: false } 535 + ) 536 + 537 + function CodePanel({ code }: { code: string }) { 538 + return <MonacoEditor value={code} /> 539 + } 540 + ``` 541 + 542 + ### 2.5 Preload Based on User Intent 543 + 544 + **Impact: MEDIUM (reduces perceived latency)** 545 + 546 + Preload heavy bundles before they're needed to reduce perceived latency. 547 + 548 + **Example: preload on hover/focus** 549 + 550 + ```tsx 551 + function EditorButton({ onClick }: { onClick: () => void }) { 552 + const preload = () => { 553 + if (typeof window !== 'undefined') { 554 + void import('./monaco-editor') 555 + } 556 + } 557 + 558 + return ( 559 + <button 560 + onMouseEnter={preload} 561 + onFocus={preload} 562 + onClick={onClick} 563 + > 564 + Open Editor 565 + </button> 566 + ) 567 + } 568 + ``` 569 + 570 + **Example: preload when feature flag is enabled** 571 + 572 + ```tsx 573 + function FlagsProvider({ children, flags }: Props) { 574 + useEffect(() => { 575 + if (flags.editorEnabled && typeof window !== 'undefined') { 576 + void import('./monaco-editor').then(mod => mod.init()) 577 + } 578 + }, [flags.editorEnabled]) 579 + 580 + return <FlagsContext.Provider value={flags}> 581 + {children} 582 + </FlagsContext.Provider> 583 + } 584 + ``` 585 + 586 + The `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed. 587 + 588 + --- 589 + 590 + ## 3. Server-Side Performance 591 + 592 + **Impact: HIGH** 593 + 594 + Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times. 595 + 596 + ### 3.1 Authenticate Server Actions Like API Routes 597 + 598 + **Impact: CRITICAL (prevents unauthorized access to server mutations)** 599 + 600 + Server Actions (functions with `"use server"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly. 601 + 602 + Next.js documentation explicitly states: "Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation." 603 + 604 + **Incorrect: no authentication check** 605 + 606 + ```typescript 607 + 'use server' 608 + 609 + export async function deleteUser(userId: string) { 610 + // Anyone can call this! No auth check 611 + await db.user.delete({ where: { id: userId } }) 612 + return { success: true } 613 + } 614 + ``` 615 + 616 + **Correct: authentication inside the action** 617 + 618 + ```typescript 619 + 'use server' 620 + 621 + import { verifySession } from '@/lib/auth' 622 + import { unauthorized } from '@/lib/errors' 623 + 624 + export async function deleteUser(userId: string) { 625 + // Always check auth inside the action 626 + const session = await verifySession() 627 + 628 + if (!session) { 629 + throw unauthorized('Must be logged in') 630 + } 631 + 632 + // Check authorization too 633 + if (session.user.role !== 'admin' && session.user.id !== userId) { 634 + throw unauthorized('Cannot delete other users') 635 + } 636 + 637 + await db.user.delete({ where: { id: userId } }) 638 + return { success: true } 639 + } 640 + ``` 641 + 642 + **With input validation:** 643 + 644 + ```typescript 645 + 'use server' 646 + 647 + import { verifySession } from '@/lib/auth' 648 + import { z } from 'zod' 649 + 650 + const updateProfileSchema = z.object({ 651 + userId: z.string().uuid(), 652 + name: z.string().min(1).max(100), 653 + email: z.string().email() 654 + }) 655 + 656 + export async function updateProfile(data: unknown) { 657 + // Validate input first 658 + const validated = updateProfileSchema.parse(data) 659 + 660 + // Then authenticate 661 + const session = await verifySession() 662 + if (!session) { 663 + throw new Error('Unauthorized') 664 + } 665 + 666 + // Then authorize 667 + if (session.user.id !== validated.userId) { 668 + throw new Error('Can only update own profile') 669 + } 670 + 671 + // Finally perform the mutation 672 + await db.user.update({ 673 + where: { id: validated.userId }, 674 + data: { 675 + name: validated.name, 676 + email: validated.email 677 + } 678 + }) 679 + 680 + return { success: true } 681 + } 682 + ``` 683 + 684 + Reference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication) 685 + 686 + ### 3.2 Avoid Duplicate Serialization in RSC Props 687 + 688 + **Impact: LOW (reduces network payload by avoiding duplicate serialization)** 689 + 690 + RSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again. Do transformations (`.toSorted()`, `.filter()`, `.map()`) in client, not server. 691 + 692 + **Incorrect: duplicates array** 693 + 694 + ```tsx 695 + // RSC: sends 6 strings (2 arrays × 3 items) 696 + <ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} /> 697 + ``` 698 + 699 + **Correct: sends 3 strings** 700 + 701 + ```tsx 702 + // RSC: send once 703 + <ClientList usernames={usernames} /> 704 + 705 + // Client: transform there 706 + 'use client' 707 + const sorted = useMemo(() => [...usernames].sort(), [usernames]) 708 + ``` 709 + 710 + **Nested deduplication behavior:** 711 + 712 + ```tsx 713 + // string[] - duplicates everything 714 + usernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings 715 + 716 + // object[] - duplicates array structure only 717 + users={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4) 718 + ``` 719 + 720 + Deduplication works recursively. Impact varies by data type: 721 + 722 + - `string[]`, `number[]`, `boolean[]`: **HIGH impact** - array + all primitives fully duplicated 723 + 724 + - `object[]`: **LOW impact** - array duplicated, but nested objects deduplicated by reference 725 + 726 + **Operations breaking deduplication: create new references** 727 + 728 + - Arrays: `.toSorted()`, `.filter()`, `.map()`, `.slice()`, `[...arr]` 729 + 730 + - Objects: `{...obj}`, `Object.assign()`, `structuredClone()`, `JSON.parse(JSON.stringify())` 731 + 732 + **More examples:** 733 + 734 + ```tsx 735 + // ❌ Bad 736 + <C users={users} active={users.filter(u => u.active)} /> 737 + <C product={product} productName={product.name} /> 738 + 739 + // ✅ Good 740 + <C users={users} /> 741 + <C product={product} /> 742 + // Do filtering/destructuring in client 743 + ``` 744 + 745 + **Exception:** Pass derived data when transformation is expensive or client doesn't need original. 746 + 747 + ### 3.3 Cross-Request LRU Caching 748 + 749 + **Impact: HIGH (caches across requests)** 750 + 751 + `React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache. 752 + 753 + **Implementation:** 754 + 755 + ```typescript 756 + import { LRUCache } from 'lru-cache' 757 + 758 + const cache = new LRUCache<string, any>({ 759 + max: 1000, 760 + ttl: 5 * 60 * 1000 // 5 minutes 761 + }) 762 + 763 + export async function getUser(id: string) { 764 + const cached = cache.get(id) 765 + if (cached) return cached 766 + 767 + const user = await db.user.findUnique({ where: { id } }) 768 + cache.set(id, user) 769 + return user 770 + } 771 + 772 + // Request 1: DB query, result cached 773 + // Request 2: cache hit, no DB query 774 + ``` 775 + 776 + Use when sequential user actions hit multiple endpoints needing the same data within seconds. 777 + 778 + **With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis. 779 + 780 + **In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching. 781 + 782 + Reference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache) 783 + 784 + ### 3.4 Minimize Serialization at RSC Boundaries 785 + 786 + **Impact: HIGH (reduces data transfer size)** 787 + 788 + The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses. 789 + 790 + **Incorrect: serializes all 50 fields** 791 + 792 + ```tsx 793 + async function Page() { 794 + const user = await fetchUser() // 50 fields 795 + return <Profile user={user} /> 796 + } 797 + 798 + 'use client' 799 + function Profile({ user }: { user: User }) { 800 + return <div>{user.name}</div> // uses 1 field 801 + } 802 + ``` 803 + 804 + **Correct: serializes only 1 field** 805 + 806 + ```tsx 807 + async function Page() { 808 + const user = await fetchUser() 809 + return <Profile name={user.name} /> 810 + } 811 + 812 + 'use client' 813 + function Profile({ name }: { name: string }) { 814 + return <div>{name}</div> 815 + } 816 + ``` 817 + 818 + ### 3.5 Parallel Data Fetching with Component Composition 819 + 820 + **Impact: CRITICAL (eliminates server-side waterfalls)** 821 + 822 + React Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching. 823 + 824 + **Incorrect: Sidebar waits for Page's fetch to complete** 825 + 826 + ```tsx 827 + export default async function Page() { 828 + const header = await fetchHeader() 829 + return ( 830 + <div> 831 + <div>{header}</div> 832 + <Sidebar /> 833 + </div> 834 + ) 835 + } 836 + 837 + async function Sidebar() { 838 + const items = await fetchSidebarItems() 839 + return <nav>{items.map(renderItem)}</nav> 840 + } 841 + ``` 842 + 843 + **Correct: both fetch simultaneously** 844 + 845 + ```tsx 846 + async function Header() { 847 + const data = await fetchHeader() 848 + return <div>{data}</div> 849 + } 850 + 851 + async function Sidebar() { 852 + const items = await fetchSidebarItems() 853 + return <nav>{items.map(renderItem)}</nav> 854 + } 855 + 856 + export default function Page() { 857 + return ( 858 + <div> 859 + <Header /> 860 + <Sidebar /> 861 + </div> 862 + ) 863 + } 864 + ``` 865 + 866 + **Alternative with children prop:** 867 + 868 + ```tsx 869 + async function Header() { 870 + const data = await fetchHeader() 871 + return <div>{data}</div> 872 + } 873 + 874 + async function Sidebar() { 875 + const items = await fetchSidebarItems() 876 + return <nav>{items.map(renderItem)}</nav> 877 + } 878 + 879 + function Layout({ children }: { children: ReactNode }) { 880 + return ( 881 + <div> 882 + <Header /> 883 + {children} 884 + </div> 885 + ) 886 + } 887 + 888 + export default function Page() { 889 + return ( 890 + <Layout> 891 + <Sidebar /> 892 + </Layout> 893 + ) 894 + } 895 + ``` 896 + 897 + ### 3.6 Per-Request Deduplication with React.cache() 898 + 899 + **Impact: MEDIUM (deduplicates within request)** 900 + 901 + Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most. 902 + 903 + **Usage:** 904 + 905 + ```typescript 906 + import { cache } from 'react' 907 + 908 + export const getCurrentUser = cache(async () => { 909 + const session = await auth() 910 + if (!session?.user?.id) return null 911 + return await db.user.findUnique({ 912 + where: { id: session.user.id } 913 + }) 914 + }) 915 + ``` 916 + 917 + Within a single request, multiple calls to `getCurrentUser()` execute the query only once. 918 + 919 + **Avoid inline objects as arguments:** 920 + 921 + `React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits. 922 + 923 + **Incorrect: always cache miss** 924 + 925 + ```typescript 926 + const getUser = cache(async (params: { uid: number }) => { 927 + return await db.user.findUnique({ where: { id: params.uid } }) 928 + }) 929 + 930 + // Each call creates new object, never hits cache 931 + getUser({ uid: 1 }) 932 + getUser({ uid: 1 }) // Cache miss, runs query again 933 + ``` 934 + 935 + **Correct: cache hit** 936 + 937 + ```typescript 938 + const params = { uid: 1 } 939 + getUser(params) // Query runs 940 + getUser(params) // Cache hit (same reference) 941 + ``` 942 + 943 + If you must pass objects, pass the same reference: 944 + 945 + **Next.js-Specific Note:** 946 + 947 + In Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks: 948 + 949 + - Database queries (Prisma, Drizzle, etc.) 950 + 951 + - Heavy computations 952 + 953 + - Authentication checks 954 + 955 + - File system operations 956 + 957 + - Any non-fetch async work 958 + 959 + Use `React.cache()` to deduplicate these operations across your component tree. 960 + 961 + Reference: [https://react.dev/reference/react/cache](https://react.dev/reference/react/cache) 962 + 963 + ### 3.7 Use after() for Non-Blocking Operations 964 + 965 + **Impact: MEDIUM (faster response times)** 966 + 967 + Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response. 968 + 969 + **Incorrect: blocks response** 970 + 971 + ```tsx 972 + import { logUserAction } from '@/app/utils' 973 + 974 + export async function POST(request: Request) { 975 + // Perform mutation 976 + await updateDatabase(request) 977 + 978 + // Logging blocks the response 979 + const userAgent = request.headers.get('user-agent') || 'unknown' 980 + await logUserAction({ userAgent }) 981 + 982 + return new Response(JSON.stringify({ status: 'success' }), { 983 + status: 200, 984 + headers: { 'Content-Type': 'application/json' } 985 + }) 986 + } 987 + ``` 988 + 989 + **Correct: non-blocking** 990 + 991 + ```tsx 992 + import { after } from 'next/server' 993 + import { headers, cookies } from 'next/headers' 994 + import { logUserAction } from '@/app/utils' 995 + 996 + export async function POST(request: Request) { 997 + // Perform mutation 998 + await updateDatabase(request) 999 + 1000 + // Log after response is sent 1001 + after(async () => { 1002 + const userAgent = (await headers()).get('user-agent') || 'unknown' 1003 + const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous' 1004 + 1005 + logUserAction({ sessionCookie, userAgent }) 1006 + }) 1007 + 1008 + return new Response(JSON.stringify({ status: 'success' }), { 1009 + status: 200, 1010 + headers: { 'Content-Type': 'application/json' } 1011 + }) 1012 + } 1013 + ``` 1014 + 1015 + The response is sent immediately while logging happens in the background. 1016 + 1017 + **Common use cases:** 1018 + 1019 + - Analytics tracking 1020 + 1021 + - Audit logging 1022 + 1023 + - Sending notifications 1024 + 1025 + - Cache invalidation 1026 + 1027 + - Cleanup tasks 1028 + 1029 + **Important notes:** 1030 + 1031 + - `after()` runs even if the response fails or redirects 1032 + 1033 + - Works in Server Actions, Route Handlers, and Server Components 1034 + 1035 + Reference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after) 1036 + 1037 + --- 1038 + 1039 + ## 4. Client-Side Data Fetching 1040 + 1041 + **Impact: MEDIUM-HIGH** 1042 + 1043 + Automatic deduplication and efficient data fetching patterns reduce redundant network requests. 1044 + 1045 + ### 4.1 Deduplicate Global Event Listeners 1046 + 1047 + **Impact: LOW (single listener for N components)** 1048 + 1049 + Use `useSWRSubscription()` to share global event listeners across component instances. 1050 + 1051 + **Incorrect: N instances = N listeners** 1052 + 1053 + ```tsx 1054 + function useKeyboardShortcut(key: string, callback: () => void) { 1055 + useEffect(() => { 1056 + const handler = (e: KeyboardEvent) => { 1057 + if (e.metaKey && e.key === key) { 1058 + callback() 1059 + } 1060 + } 1061 + window.addEventListener('keydown', handler) 1062 + return () => window.removeEventListener('keydown', handler) 1063 + }, [key, callback]) 1064 + } 1065 + ``` 1066 + 1067 + When using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener. 1068 + 1069 + **Correct: N instances = 1 listener** 1070 + 1071 + ```tsx 1072 + import useSWRSubscription from 'swr/subscription' 1073 + 1074 + // Module-level Map to track callbacks per key 1075 + const keyCallbacks = new Map<string, Set<() => void>>() 1076 + 1077 + function useKeyboardShortcut(key: string, callback: () => void) { 1078 + // Register this callback in the Map 1079 + useEffect(() => { 1080 + if (!keyCallbacks.has(key)) { 1081 + keyCallbacks.set(key, new Set()) 1082 + } 1083 + keyCallbacks.get(key)!.add(callback) 1084 + 1085 + return () => { 1086 + const set = keyCallbacks.get(key) 1087 + if (set) { 1088 + set.delete(callback) 1089 + if (set.size === 0) { 1090 + keyCallbacks.delete(key) 1091 + } 1092 + } 1093 + } 1094 + }, [key, callback]) 1095 + 1096 + useSWRSubscription('global-keydown', () => { 1097 + const handler = (e: KeyboardEvent) => { 1098 + if (e.metaKey && keyCallbacks.has(e.key)) { 1099 + keyCallbacks.get(e.key)!.forEach(cb => cb()) 1100 + } 1101 + } 1102 + window.addEventListener('keydown', handler) 1103 + return () => window.removeEventListener('keydown', handler) 1104 + }) 1105 + } 1106 + 1107 + function Profile() { 1108 + // Multiple shortcuts will share the same listener 1109 + useKeyboardShortcut('p', () => { /* ... */ }) 1110 + useKeyboardShortcut('k', () => { /* ... */ }) 1111 + // ... 1112 + } 1113 + ``` 1114 + 1115 + ### 4.2 Use Passive Event Listeners for Scrolling Performance 1116 + 1117 + **Impact: MEDIUM (eliminates scroll delay caused by event listeners)** 1118 + 1119 + Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay. 1120 + 1121 + **Incorrect:** 1122 + 1123 + ```typescript 1124 + useEffect(() => { 1125 + const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX) 1126 + const handleWheel = (e: WheelEvent) => console.log(e.deltaY) 1127 + 1128 + document.addEventListener('touchstart', handleTouch) 1129 + document.addEventListener('wheel', handleWheel) 1130 + 1131 + return () => { 1132 + document.removeEventListener('touchstart', handleTouch) 1133 + document.removeEventListener('wheel', handleWheel) 1134 + } 1135 + }, []) 1136 + ``` 1137 + 1138 + **Correct:** 1139 + 1140 + ```typescript 1141 + useEffect(() => { 1142 + const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX) 1143 + const handleWheel = (e: WheelEvent) => console.log(e.deltaY) 1144 + 1145 + document.addEventListener('touchstart', handleTouch, { passive: true }) 1146 + document.addEventListener('wheel', handleWheel, { passive: true }) 1147 + 1148 + return () => { 1149 + document.removeEventListener('touchstart', handleTouch) 1150 + document.removeEventListener('wheel', handleWheel) 1151 + } 1152 + }, []) 1153 + ``` 1154 + 1155 + **Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`. 1156 + 1157 + **Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`. 1158 + 1159 + ### 4.3 Use SWR for Automatic Deduplication 1160 + 1161 + **Impact: MEDIUM-HIGH (automatic deduplication)** 1162 + 1163 + SWR enables request deduplication, caching, and revalidation across component instances. 1164 + 1165 + **Incorrect: no deduplication, each instance fetches** 1166 + 1167 + ```tsx 1168 + function UserList() { 1169 + const [users, setUsers] = useState([]) 1170 + useEffect(() => { 1171 + fetch('/api/users') 1172 + .then(r => r.json()) 1173 + .then(setUsers) 1174 + }, []) 1175 + } 1176 + ``` 1177 + 1178 + **Correct: multiple instances share one request** 1179 + 1180 + ```tsx 1181 + import useSWR from 'swr' 1182 + 1183 + function UserList() { 1184 + const { data: users } = useSWR('/api/users', fetcher) 1185 + } 1186 + ``` 1187 + 1188 + **For immutable data:** 1189 + 1190 + ```tsx 1191 + import { useImmutableSWR } from '@/lib/swr' 1192 + 1193 + function StaticContent() { 1194 + const { data } = useImmutableSWR('/api/config', fetcher) 1195 + } 1196 + ``` 1197 + 1198 + **For mutations:** 1199 + 1200 + ```tsx 1201 + import { useSWRMutation } from 'swr/mutation' 1202 + 1203 + function UpdateButton() { 1204 + const { trigger } = useSWRMutation('/api/user', updateUser) 1205 + return <button onClick={() => trigger()}>Update</button> 1206 + } 1207 + ``` 1208 + 1209 + Reference: [https://swr.vercel.app](https://swr.vercel.app) 1210 + 1211 + ### 4.4 Version and Minimize localStorage Data 1212 + 1213 + **Impact: MEDIUM (prevents schema conflicts, reduces storage size)** 1214 + 1215 + Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data. 1216 + 1217 + **Incorrect:** 1218 + 1219 + ```typescript 1220 + // No version, stores everything, no error handling 1221 + localStorage.setItem('userConfig', JSON.stringify(fullUserObject)) 1222 + const data = localStorage.getItem('userConfig') 1223 + ``` 1224 + 1225 + **Correct:** 1226 + 1227 + ```typescript 1228 + const VERSION = 'v2' 1229 + 1230 + function saveConfig(config: { theme: string; language: string }) { 1231 + try { 1232 + localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config)) 1233 + } catch { 1234 + // Throws in incognito/private browsing, quota exceeded, or disabled 1235 + } 1236 + } 1237 + 1238 + function loadConfig() { 1239 + try { 1240 + const data = localStorage.getItem(`userConfig:${VERSION}`) 1241 + return data ? JSON.parse(data) : null 1242 + } catch { 1243 + return null 1244 + } 1245 + } 1246 + 1247 + // Migration from v1 to v2 1248 + function migrate() { 1249 + try { 1250 + const v1 = localStorage.getItem('userConfig:v1') 1251 + if (v1) { 1252 + const old = JSON.parse(v1) 1253 + saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang }) 1254 + localStorage.removeItem('userConfig:v1') 1255 + } 1256 + } catch {} 1257 + } 1258 + ``` 1259 + 1260 + **Store minimal fields from server responses:** 1261 + 1262 + ```typescript 1263 + // User object has 20+ fields, only store what UI needs 1264 + function cachePrefs(user: FullUser) { 1265 + try { 1266 + localStorage.setItem('prefs:v1', JSON.stringify({ 1267 + theme: user.preferences.theme, 1268 + notifications: user.preferences.notifications 1269 + })) 1270 + } catch {} 1271 + } 1272 + ``` 1273 + 1274 + **Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled. 1275 + 1276 + **Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags. 1277 + 1278 + --- 1279 + 1280 + ## 5. Re-render Optimization 1281 + 1282 + **Impact: MEDIUM** 1283 + 1284 + Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness. 1285 + 1286 + ### 5.1 Calculate Derived State During Rendering 1287 + 1288 + **Impact: MEDIUM (avoids redundant renders and state drift)** 1289 + 1290 + If a value can be computed from current props/state, do not store it in state or update it in an effect. Derive it during render to avoid extra renders and state drift. Do not set state in effects solely in response to prop changes; prefer derived values or keyed resets instead. 1291 + 1292 + **Incorrect: redundant state and effect** 1293 + 1294 + ```tsx 1295 + function Form() { 1296 + const [firstName, setFirstName] = useState('First') 1297 + const [lastName, setLastName] = useState('Last') 1298 + const [fullName, setFullName] = useState('') 1299 + 1300 + useEffect(() => { 1301 + setFullName(firstName + ' ' + lastName) 1302 + }, [firstName, lastName]) 1303 + 1304 + return <p>{fullName}</p> 1305 + } 1306 + ``` 1307 + 1308 + **Correct: derive during render** 1309 + 1310 + ```tsx 1311 + function Form() { 1312 + const [firstName, setFirstName] = useState('First') 1313 + const [lastName, setLastName] = useState('Last') 1314 + const fullName = firstName + ' ' + lastName 1315 + 1316 + return <p>{fullName}</p> 1317 + } 1318 + ``` 1319 + 1320 + Reference: [https://react.dev/learn/you-might-not-need-an-effect](https://react.dev/learn/you-might-not-need-an-effect) 1321 + 1322 + ### 5.2 Defer State Reads to Usage Point 1323 + 1324 + **Impact: MEDIUM (avoids unnecessary subscriptions)** 1325 + 1326 + Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks. 1327 + 1328 + **Incorrect: subscribes to all searchParams changes** 1329 + 1330 + ```tsx 1331 + function ShareButton({ chatId }: { chatId: string }) { 1332 + const searchParams = useSearchParams() 1333 + 1334 + const handleShare = () => { 1335 + const ref = searchParams.get('ref') 1336 + shareChat(chatId, { ref }) 1337 + } 1338 + 1339 + return <button onClick={handleShare}>Share</button> 1340 + } 1341 + ``` 1342 + 1343 + **Correct: reads on demand, no subscription** 1344 + 1345 + ```tsx 1346 + function ShareButton({ chatId }: { chatId: string }) { 1347 + const handleShare = () => { 1348 + const params = new URLSearchParams(window.location.search) 1349 + const ref = params.get('ref') 1350 + shareChat(chatId, { ref }) 1351 + } 1352 + 1353 + return <button onClick={handleShare}>Share</button> 1354 + } 1355 + ``` 1356 + 1357 + ### 5.3 Do not wrap a simple expression with a primitive result type in useMemo 1358 + 1359 + **Impact: LOW-MEDIUM (wasted computation on every render)** 1360 + 1361 + When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`. 1362 + 1363 + Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself. 1364 + 1365 + **Incorrect:** 1366 + 1367 + ```tsx 1368 + function Header({ user, notifications }: Props) { 1369 + const isLoading = useMemo(() => { 1370 + return user.isLoading || notifications.isLoading 1371 + }, [user.isLoading, notifications.isLoading]) 1372 + 1373 + if (isLoading) return <Skeleton /> 1374 + // return some markup 1375 + } 1376 + ``` 1377 + 1378 + **Correct:** 1379 + 1380 + ```tsx 1381 + function Header({ user, notifications }: Props) { 1382 + const isLoading = user.isLoading || notifications.isLoading 1383 + 1384 + if (isLoading) return <Skeleton /> 1385 + // return some markup 1386 + } 1387 + ``` 1388 + 1389 + ### 5.4 Extract Default Non-primitive Parameter Value from Memoized Component to Constant 1390 + 1391 + **Impact: MEDIUM (restores memoization by using a constant for default value)** 1392 + 1393 + When memoized component has a default value for some non-primitive optional parameter, such as an array, function, or object, calling the component without that parameter results in broken memoization. This is because new value instances are created on every rerender, and they do not pass strict equality comparison in `memo()`. 1394 + 1395 + To address this issue, extract the default value into a constant. 1396 + 1397 + **Incorrect: `onClick` has different values on every rerender** 1398 + 1399 + ```tsx 1400 + const UserAvatar = memo(function UserAvatar({ onClick = () => {} }: { onClick?: () => void }) { 1401 + // ... 1402 + }) 1403 + 1404 + // Used without optional onClick 1405 + <UserAvatar /> 1406 + ``` 1407 + 1408 + **Correct: stable default value** 1409 + 1410 + ```tsx 1411 + const NOOP = () => {}; 1412 + 1413 + const UserAvatar = memo(function UserAvatar({ onClick = NOOP }: { onClick?: () => void }) { 1414 + // ... 1415 + }) 1416 + 1417 + // Used without optional onClick 1418 + <UserAvatar /> 1419 + ``` 1420 + 1421 + ### 5.5 Extract to Memoized Components 1422 + 1423 + **Impact: MEDIUM (enables early returns)** 1424 + 1425 + Extract expensive work into memoized components to enable early returns before computation. 1426 + 1427 + **Incorrect: computes avatar even when loading** 1428 + 1429 + ```tsx 1430 + function Profile({ user, loading }: Props) { 1431 + const avatar = useMemo(() => { 1432 + const id = computeAvatarId(user) 1433 + return <Avatar id={id} /> 1434 + }, [user]) 1435 + 1436 + if (loading) return <Skeleton /> 1437 + return <div>{avatar}</div> 1438 + } 1439 + ``` 1440 + 1441 + **Correct: skips computation when loading** 1442 + 1443 + ```tsx 1444 + const UserAvatar = memo(function UserAvatar({ user }: { user: User }) { 1445 + const id = useMemo(() => computeAvatarId(user), [user]) 1446 + return <Avatar id={id} /> 1447 + }) 1448 + 1449 + function Profile({ user, loading }: Props) { 1450 + if (loading) return <Skeleton /> 1451 + return ( 1452 + <div> 1453 + <UserAvatar user={user} /> 1454 + </div> 1455 + ) 1456 + } 1457 + ``` 1458 + 1459 + **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, manual memoization with `memo()` and `useMemo()` is not necessary. The compiler automatically optimizes re-renders. 1460 + 1461 + ### 5.6 Narrow Effect Dependencies 1462 + 1463 + **Impact: LOW (minimizes effect re-runs)** 1464 + 1465 + Specify primitive dependencies instead of objects to minimize effect re-runs. 1466 + 1467 + **Incorrect: re-runs on any user field change** 1468 + 1469 + ```tsx 1470 + useEffect(() => { 1471 + console.log(user.id) 1472 + }, [user]) 1473 + ``` 1474 + 1475 + **Correct: re-runs only when id changes** 1476 + 1477 + ```tsx 1478 + useEffect(() => { 1479 + console.log(user.id) 1480 + }, [user.id]) 1481 + ``` 1482 + 1483 + **For derived state, compute outside effect:** 1484 + 1485 + ```tsx 1486 + // Incorrect: runs on width=767, 766, 765... 1487 + useEffect(() => { 1488 + if (width < 768) { 1489 + enableMobileMode() 1490 + } 1491 + }, [width]) 1492 + 1493 + // Correct: runs only on boolean transition 1494 + const isMobile = width < 768 1495 + useEffect(() => { 1496 + if (isMobile) { 1497 + enableMobileMode() 1498 + } 1499 + }, [isMobile]) 1500 + ``` 1501 + 1502 + ### 5.7 Put Interaction Logic in Event Handlers 1503 + 1504 + **Impact: MEDIUM (avoids effect re-runs and duplicate side effects)** 1505 + 1506 + If a side effect is triggered by a specific user action (submit, click, drag), run it in that event handler. Do not model the action as state + effect; it makes effects re-run on unrelated changes and can duplicate the action. 1507 + 1508 + **Incorrect: event modeled as state + effect** 1509 + 1510 + ```tsx 1511 + function Form() { 1512 + const [submitted, setSubmitted] = useState(false) 1513 + const theme = useContext(ThemeContext) 1514 + 1515 + useEffect(() => { 1516 + if (submitted) { 1517 + post('/api/register') 1518 + showToast('Registered', theme) 1519 + } 1520 + }, [submitted, theme]) 1521 + 1522 + return <button onClick={() => setSubmitted(true)}>Submit</button> 1523 + } 1524 + ``` 1525 + 1526 + **Correct: do it in the handler** 1527 + 1528 + ```tsx 1529 + function Form() { 1530 + const theme = useContext(ThemeContext) 1531 + 1532 + function handleSubmit() { 1533 + post('/api/register') 1534 + showToast('Registered', theme) 1535 + } 1536 + 1537 + return <button onClick={handleSubmit}>Submit</button> 1538 + } 1539 + ``` 1540 + 1541 + Reference: [https://react.dev/learn/removing-effect-dependencies#should-this-code-move-to-an-event-handler](https://react.dev/learn/removing-effect-dependencies#should-this-code-move-to-an-event-handler) 1542 + 1543 + ### 5.8 Subscribe to Derived State 1544 + 1545 + **Impact: MEDIUM (reduces re-render frequency)** 1546 + 1547 + Subscribe to derived boolean state instead of continuous values to reduce re-render frequency. 1548 + 1549 + **Incorrect: re-renders on every pixel change** 1550 + 1551 + ```tsx 1552 + function Sidebar() { 1553 + const width = useWindowWidth() // updates continuously 1554 + const isMobile = width < 768 1555 + return <nav className={isMobile ? 'mobile' : 'desktop'} /> 1556 + } 1557 + ``` 1558 + 1559 + **Correct: re-renders only when boolean changes** 1560 + 1561 + ```tsx 1562 + function Sidebar() { 1563 + const isMobile = useMediaQuery('(max-width: 767px)') 1564 + return <nav className={isMobile ? 'mobile' : 'desktop'} /> 1565 + } 1566 + ``` 1567 + 1568 + ### 5.9 Use Functional setState Updates 1569 + 1570 + **Impact: MEDIUM (prevents stale closures and unnecessary callback recreations)** 1571 + 1572 + When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references. 1573 + 1574 + **Incorrect: requires state as dependency** 1575 + 1576 + ```tsx 1577 + function TodoList() { 1578 + const [items, setItems] = useState(initialItems) 1579 + 1580 + // Callback must depend on items, recreated on every items change 1581 + const addItems = useCallback((newItems: Item[]) => { 1582 + setItems([...items, ...newItems]) 1583 + }, [items]) // ❌ items dependency causes recreations 1584 + 1585 + // Risk of stale closure if dependency is forgotten 1586 + const removeItem = useCallback((id: string) => { 1587 + setItems(items.filter(item => item.id !== id)) 1588 + }, []) // ❌ Missing items dependency - will use stale items! 1589 + 1590 + return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} /> 1591 + } 1592 + ``` 1593 + 1594 + The first callback is recreated every time `items` changes, which can cause child components to re-render unnecessarily. The second callback has a stale closure bug—it will always reference the initial `items` value. 1595 + 1596 + **Correct: stable callbacks, no stale closures** 1597 + 1598 + ```tsx 1599 + function TodoList() { 1600 + const [items, setItems] = useState(initialItems) 1601 + 1602 + // Stable callback, never recreated 1603 + const addItems = useCallback((newItems: Item[]) => { 1604 + setItems(curr => [...curr, ...newItems]) 1605 + }, []) // ✅ No dependencies needed 1606 + 1607 + // Always uses latest state, no stale closure risk 1608 + const removeItem = useCallback((id: string) => { 1609 + setItems(curr => curr.filter(item => item.id !== id)) 1610 + }, []) // ✅ Safe and stable 1611 + 1612 + return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} /> 1613 + } 1614 + ``` 1615 + 1616 + **Benefits:** 1617 + 1618 + 1. **Stable callback references** - Callbacks don't need to be recreated when state changes 1619 + 1620 + 2. **No stale closures** - Always operates on the latest state value 1621 + 1622 + 3. **Fewer dependencies** - Simplifies dependency arrays and reduces memory leaks 1623 + 1624 + 4. **Prevents bugs** - Eliminates the most common source of React closure bugs 1625 + 1626 + **When to use functional updates:** 1627 + 1628 + - Any setState that depends on the current state value 1629 + 1630 + - Inside useCallback/useMemo when state is needed 1631 + 1632 + - Event handlers that reference state 1633 + 1634 + - Async operations that update state 1635 + 1636 + **When direct updates are fine:** 1637 + 1638 + - Setting state to a static value: `setCount(0)` 1639 + 1640 + - Setting state from props/arguments only: `setName(newName)` 1641 + 1642 + - State doesn't depend on previous value 1643 + 1644 + **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler can automatically optimize some cases, but functional updates are still recommended for correctness and to prevent stale closure bugs. 1645 + 1646 + ### 5.10 Use Lazy State Initialization 1647 + 1648 + **Impact: MEDIUM (wasted computation on every render)** 1649 + 1650 + Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once. 1651 + 1652 + **Incorrect: runs on every render** 1653 + 1654 + ```tsx 1655 + function FilteredList({ items }: { items: Item[] }) { 1656 + // buildSearchIndex() runs on EVERY render, even after initialization 1657 + const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items)) 1658 + const [query, setQuery] = useState('') 1659 + 1660 + // When query changes, buildSearchIndex runs again unnecessarily 1661 + return <SearchResults index={searchIndex} query={query} /> 1662 + } 1663 + 1664 + function UserProfile() { 1665 + // JSON.parse runs on every render 1666 + const [settings, setSettings] = useState( 1667 + JSON.parse(localStorage.getItem('settings') || '{}') 1668 + ) 1669 + 1670 + return <SettingsForm settings={settings} onChange={setSettings} /> 1671 + } 1672 + ``` 1673 + 1674 + **Correct: runs only once** 1675 + 1676 + ```tsx 1677 + function FilteredList({ items }: { items: Item[] }) { 1678 + // buildSearchIndex() runs ONLY on initial render 1679 + const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items)) 1680 + const [query, setQuery] = useState('') 1681 + 1682 + return <SearchResults index={searchIndex} query={query} /> 1683 + } 1684 + 1685 + function UserProfile() { 1686 + // JSON.parse runs only on initial render 1687 + const [settings, setSettings] = useState(() => { 1688 + const stored = localStorage.getItem('settings') 1689 + return stored ? JSON.parse(stored) : {} 1690 + }) 1691 + 1692 + return <SettingsForm settings={settings} onChange={setSettings} /> 1693 + } 1694 + ``` 1695 + 1696 + Use lazy initialization when computing initial values from localStorage/sessionStorage, building data structures (indexes, maps), reading from the DOM, or performing heavy transformations. 1697 + 1698 + For simple primitives (`useState(0)`), direct references (`useState(props.value)`), or cheap literals (`useState({})`), the function form is unnecessary. 1699 + 1700 + ### 5.11 Use Transitions for Non-Urgent Updates 1701 + 1702 + **Impact: MEDIUM (maintains UI responsiveness)** 1703 + 1704 + Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness. 1705 + 1706 + **Incorrect: blocks UI on every scroll** 1707 + 1708 + ```tsx 1709 + function ScrollTracker() { 1710 + const [scrollY, setScrollY] = useState(0) 1711 + useEffect(() => { 1712 + const handler = () => setScrollY(window.scrollY) 1713 + window.addEventListener('scroll', handler, { passive: true }) 1714 + return () => window.removeEventListener('scroll', handler) 1715 + }, []) 1716 + } 1717 + ``` 1718 + 1719 + **Correct: non-blocking updates** 1720 + 1721 + ```tsx 1722 + import { startTransition } from 'react' 1723 + 1724 + function ScrollTracker() { 1725 + const [scrollY, setScrollY] = useState(0) 1726 + useEffect(() => { 1727 + const handler = () => { 1728 + startTransition(() => setScrollY(window.scrollY)) 1729 + } 1730 + window.addEventListener('scroll', handler, { passive: true }) 1731 + return () => window.removeEventListener('scroll', handler) 1732 + }, []) 1733 + } 1734 + ``` 1735 + 1736 + ### 5.12 Use useRef for Transient Values 1737 + 1738 + **Impact: MEDIUM (avoids unnecessary re-renders on frequent updates)** 1739 + 1740 + When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render. 1741 + 1742 + **Incorrect: renders every update** 1743 + 1744 + ```tsx 1745 + function Tracker() { 1746 + const [lastX, setLastX] = useState(0) 1747 + 1748 + useEffect(() => { 1749 + const onMove = (e: MouseEvent) => setLastX(e.clientX) 1750 + window.addEventListener('mousemove', onMove) 1751 + return () => window.removeEventListener('mousemove', onMove) 1752 + }, []) 1753 + 1754 + return ( 1755 + <div 1756 + style={{ 1757 + position: 'fixed', 1758 + top: 0, 1759 + left: lastX, 1760 + width: 8, 1761 + height: 8, 1762 + background: 'black', 1763 + }} 1764 + /> 1765 + ) 1766 + } 1767 + ``` 1768 + 1769 + **Correct: no re-render for tracking** 1770 + 1771 + ```tsx 1772 + function Tracker() { 1773 + const lastXRef = useRef(0) 1774 + const dotRef = useRef<HTMLDivElement>(null) 1775 + 1776 + useEffect(() => { 1777 + const onMove = (e: MouseEvent) => { 1778 + lastXRef.current = e.clientX 1779 + const node = dotRef.current 1780 + if (node) { 1781 + node.style.transform = `translateX(${e.clientX}px)` 1782 + } 1783 + } 1784 + window.addEventListener('mousemove', onMove) 1785 + return () => window.removeEventListener('mousemove', onMove) 1786 + }, []) 1787 + 1788 + return ( 1789 + <div 1790 + ref={dotRef} 1791 + style={{ 1792 + position: 'fixed', 1793 + top: 0, 1794 + left: 0, 1795 + width: 8, 1796 + height: 8, 1797 + background: 'black', 1798 + transform: 'translateX(0px)', 1799 + }} 1800 + /> 1801 + ) 1802 + } 1803 + ``` 1804 + 1805 + --- 1806 + 1807 + ## 6. Rendering Performance 1808 + 1809 + **Impact: MEDIUM** 1810 + 1811 + Optimizing the rendering process reduces the work the browser needs to do. 1812 + 1813 + ### 6.1 Animate SVG Wrapper Instead of SVG Element 1814 + 1815 + **Impact: LOW (enables hardware acceleration)** 1816 + 1817 + Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead. 1818 + 1819 + **Incorrect: animating SVG directly - no hardware acceleration** 1820 + 1821 + ```tsx 1822 + function LoadingSpinner() { 1823 + return ( 1824 + <svg 1825 + className="animate-spin" 1826 + width="24" 1827 + height="24" 1828 + viewBox="0 0 24 24" 1829 + > 1830 + <circle cx="12" cy="12" r="10" stroke="currentColor" /> 1831 + </svg> 1832 + ) 1833 + } 1834 + ``` 1835 + 1836 + **Correct: animating wrapper div - hardware accelerated** 1837 + 1838 + ```tsx 1839 + function LoadingSpinner() { 1840 + return ( 1841 + <div className="animate-spin"> 1842 + <svg 1843 + width="24" 1844 + height="24" 1845 + viewBox="0 0 24 24" 1846 + > 1847 + <circle cx="12" cy="12" r="10" stroke="currentColor" /> 1848 + </svg> 1849 + </div> 1850 + ) 1851 + } 1852 + ``` 1853 + 1854 + This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations. 1855 + 1856 + ### 6.2 CSS content-visibility for Long Lists 1857 + 1858 + **Impact: HIGH (faster initial render)** 1859 + 1860 + Apply `content-visibility: auto` to defer off-screen rendering. 1861 + 1862 + **CSS:** 1863 + 1864 + ```css 1865 + .message-item { 1866 + content-visibility: auto; 1867 + contain-intrinsic-size: 0 80px; 1868 + } 1869 + ``` 1870 + 1871 + **Example:** 1872 + 1873 + ```tsx 1874 + function MessageList({ messages }: { messages: Message[] }) { 1875 + return ( 1876 + <div className="overflow-y-auto h-screen"> 1877 + {messages.map(msg => ( 1878 + <div key={msg.id} className="message-item"> 1879 + <Avatar user={msg.author} /> 1880 + <div>{msg.content}</div> 1881 + </div> 1882 + ))} 1883 + </div> 1884 + ) 1885 + } 1886 + ``` 1887 + 1888 + For 1000 messages, browser skips layout/paint for ~990 off-screen items (10× faster initial render). 1889 + 1890 + ### 6.3 Hoist Static JSX Elements 1891 + 1892 + **Impact: LOW (avoids re-creation)** 1893 + 1894 + Extract static JSX outside components to avoid re-creation. 1895 + 1896 + **Incorrect: recreates element every render** 1897 + 1898 + ```tsx 1899 + function LoadingSkeleton() { 1900 + return <div className="animate-pulse h-20 bg-gray-200" /> 1901 + } 1902 + 1903 + function Container() { 1904 + return ( 1905 + <div> 1906 + {loading && <LoadingSkeleton />} 1907 + </div> 1908 + ) 1909 + } 1910 + ``` 1911 + 1912 + **Correct: reuses same element** 1913 + 1914 + ```tsx 1915 + const loadingSkeleton = ( 1916 + <div className="animate-pulse h-20 bg-gray-200" /> 1917 + ) 1918 + 1919 + function Container() { 1920 + return ( 1921 + <div> 1922 + {loading && loadingSkeleton} 1923 + </div> 1924 + ) 1925 + } 1926 + ``` 1927 + 1928 + This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render. 1929 + 1930 + **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary. 1931 + 1932 + ### 6.4 Optimize SVG Precision 1933 + 1934 + **Impact: LOW (reduces file size)** 1935 + 1936 + Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered. 1937 + 1938 + **Incorrect: excessive precision** 1939 + 1940 + ```svg 1941 + <path d="M 10.293847 20.847362 L 30.938472 40.192837" /> 1942 + ``` 1943 + 1944 + **Correct: 1 decimal place** 1945 + 1946 + ```svg 1947 + <path d="M 10.3 20.8 L 30.9 40.2" /> 1948 + ``` 1949 + 1950 + **Automate with SVGO:** 1951 + 1952 + ```bash 1953 + npx svgo --precision=1 --multipass icon.svg 1954 + ``` 1955 + 1956 + ### 6.5 Prevent Hydration Mismatch Without Flickering 1957 + 1958 + **Impact: MEDIUM (avoids visual flicker and hydration errors)** 1959 + 1960 + When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates. 1961 + 1962 + **Incorrect: breaks SSR** 1963 + 1964 + ```tsx 1965 + function ThemeWrapper({ children }: { children: ReactNode }) { 1966 + // localStorage is not available on server - throws error 1967 + const theme = localStorage.getItem('theme') || 'light' 1968 + 1969 + return ( 1970 + <div className={theme}> 1971 + {children} 1972 + </div> 1973 + ) 1974 + } 1975 + ``` 1976 + 1977 + Server-side rendering will fail because `localStorage` is undefined. 1978 + 1979 + **Incorrect: visual flickering** 1980 + 1981 + ```tsx 1982 + function ThemeWrapper({ children }: { children: ReactNode }) { 1983 + const [theme, setTheme] = useState('light') 1984 + 1985 + useEffect(() => { 1986 + // Runs after hydration - causes visible flash 1987 + const stored = localStorage.getItem('theme') 1988 + if (stored) { 1989 + setTheme(stored) 1990 + } 1991 + }, []) 1992 + 1993 + return ( 1994 + <div className={theme}> 1995 + {children} 1996 + </div> 1997 + ) 1998 + } 1999 + ``` 2000 + 2001 + Component first renders with default value (`light`), then updates after hydration, causing a visible flash of incorrect content. 2002 + 2003 + **Correct: no flicker, no hydration mismatch** 2004 + 2005 + ```tsx 2006 + function ThemeWrapper({ children }: { children: ReactNode }) { 2007 + return ( 2008 + <> 2009 + <div id="theme-wrapper"> 2010 + {children} 2011 + </div> 2012 + <script 2013 + dangerouslySetInnerHTML={{ 2014 + __html: ` 2015 + (function() { 2016 + try { 2017 + var theme = localStorage.getItem('theme') || 'light'; 2018 + var el = document.getElementById('theme-wrapper'); 2019 + if (el) el.className = theme; 2020 + } catch (e) {} 2021 + })(); 2022 + `, 2023 + }} 2024 + /> 2025 + </> 2026 + ) 2027 + } 2028 + ``` 2029 + 2030 + The inline script executes synchronously before showing the element, ensuring the DOM already has the correct value. No flickering, no hydration mismatch. 2031 + 2032 + This pattern is especially useful for theme toggles, user preferences, authentication states, and any client-only data that should render immediately without flashing default values. 2033 + 2034 + ### 6.6 Suppress Expected Hydration Mismatches 2035 + 2036 + **Impact: LOW-MEDIUM (avoids noisy hydration warnings for known differences)** 2037 + 2038 + In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide real bugs. Don’t overuse it. 2039 + 2040 + **Incorrect: known mismatch warnings** 2041 + 2042 + ```tsx 2043 + function Timestamp() { 2044 + return <span>{new Date().toLocaleString()}</span> 2045 + } 2046 + ``` 2047 + 2048 + **Correct: suppress expected mismatch only** 2049 + 2050 + ```tsx 2051 + function Timestamp() { 2052 + return ( 2053 + <span suppressHydrationWarning> 2054 + {new Date().toLocaleString()} 2055 + </span> 2056 + ) 2057 + } 2058 + ``` 2059 + 2060 + ### 6.7 Use Activity Component for Show/Hide 2061 + 2062 + **Impact: MEDIUM (preserves state/DOM)** 2063 + 2064 + Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility. 2065 + 2066 + **Usage:** 2067 + 2068 + ```tsx 2069 + import { Activity } from 'react' 2070 + 2071 + function Dropdown({ isOpen }: Props) { 2072 + return ( 2073 + <Activity mode={isOpen ? 'visible' : 'hidden'}> 2074 + <ExpensiveMenu /> 2075 + </Activity> 2076 + ) 2077 + } 2078 + ``` 2079 + 2080 + Avoids expensive re-renders and state loss. 2081 + 2082 + ### 6.8 Use Explicit Conditional Rendering 2083 + 2084 + **Impact: LOW (prevents rendering 0 or NaN)** 2085 + 2086 + Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render. 2087 + 2088 + **Incorrect: renders "0" when count is 0** 2089 + 2090 + ```tsx 2091 + function Badge({ count }: { count: number }) { 2092 + return ( 2093 + <div> 2094 + {count && <span className="badge">{count}</span>} 2095 + </div> 2096 + ) 2097 + } 2098 + 2099 + // When count = 0, renders: <div>0</div> 2100 + // When count = 5, renders: <div><span class="badge">5</span></div> 2101 + ``` 2102 + 2103 + **Correct: renders nothing when count is 0** 2104 + 2105 + ```tsx 2106 + function Badge({ count }: { count: number }) { 2107 + return ( 2108 + <div> 2109 + {count > 0 ? <span className="badge">{count}</span> : null} 2110 + </div> 2111 + ) 2112 + } 2113 + 2114 + // When count = 0, renders: <div></div> 2115 + // When count = 5, renders: <div><span class="badge">5</span></div> 2116 + ``` 2117 + 2118 + ### 6.9 Use useTransition Over Manual Loading States 2119 + 2120 + **Impact: LOW (reduces re-renders and improves code clarity)** 2121 + 2122 + Use `useTransition` instead of manual `useState` for loading states. This provides built-in `isPending` state and automatically manages transitions. 2123 + 2124 + **Incorrect: manual loading state** 2125 + 2126 + ```tsx 2127 + function SearchResults() { 2128 + const [query, setQuery] = useState('') 2129 + const [results, setResults] = useState([]) 2130 + const [isLoading, setIsLoading] = useState(false) 2131 + 2132 + const handleSearch = async (value: string) => { 2133 + setIsLoading(true) 2134 + setQuery(value) 2135 + const data = await fetchResults(value) 2136 + setResults(data) 2137 + setIsLoading(false) 2138 + } 2139 + 2140 + return ( 2141 + <> 2142 + <input onChange={(e) => handleSearch(e.target.value)} /> 2143 + {isLoading && <Spinner />} 2144 + <ResultsList results={results} /> 2145 + </> 2146 + ) 2147 + } 2148 + ``` 2149 + 2150 + **Correct: useTransition with built-in pending state** 2151 + 2152 + ```tsx 2153 + import { useTransition, useState } from 'react' 2154 + 2155 + function SearchResults() { 2156 + const [query, setQuery] = useState('') 2157 + const [results, setResults] = useState([]) 2158 + const [isPending, startTransition] = useTransition() 2159 + 2160 + const handleSearch = (value: string) => { 2161 + setQuery(value) // Update input immediately 2162 + 2163 + startTransition(async () => { 2164 + // Fetch and update results 2165 + const data = await fetchResults(value) 2166 + setResults(data) 2167 + }) 2168 + } 2169 + 2170 + return ( 2171 + <> 2172 + <input onChange={(e) => handleSearch(e.target.value)} /> 2173 + {isPending && <Spinner />} 2174 + <ResultsList results={results} /> 2175 + </> 2176 + ) 2177 + } 2178 + ``` 2179 + 2180 + **Benefits:** 2181 + 2182 + - **Automatic pending state**: No need to manually manage `setIsLoading(true/false)` 2183 + 2184 + - **Error resilience**: Pending state correctly resets even if the transition throws 2185 + 2186 + - **Better responsiveness**: Keeps the UI responsive during updates 2187 + 2188 + - **Interrupt handling**: New transitions automatically cancel pending ones 2189 + 2190 + Reference: [https://react.dev/reference/react/useTransition](https://react.dev/reference/react/useTransition) 2191 + 2192 + --- 2193 + 2194 + ## 7. JavaScript Performance 2195 + 2196 + **Impact: LOW-MEDIUM** 2197 + 2198 + Micro-optimizations for hot paths can add up to meaningful improvements. 2199 + 2200 + ### 7.1 Avoid Layout Thrashing 2201 + 2202 + **Impact: MEDIUM (prevents forced synchronous layouts and reduces performance bottlenecks)** 2203 + 2204 + Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow. 2205 + 2206 + **This is OK: browser batches style changes** 2207 + 2208 + ```typescript 2209 + function updateElementStyles(element: HTMLElement) { 2210 + // Each line invalidates style, but browser batches the recalculation 2211 + element.style.width = '100px' 2212 + element.style.height = '200px' 2213 + element.style.backgroundColor = 'blue' 2214 + element.style.border = '1px solid black' 2215 + } 2216 + ``` 2217 + 2218 + **Incorrect: interleaved reads and writes force reflows** 2219 + 2220 + ```typescript 2221 + function layoutThrashing(element: HTMLElement) { 2222 + element.style.width = '100px' 2223 + const width = element.offsetWidth // Forces reflow 2224 + element.style.height = '200px' 2225 + const height = element.offsetHeight // Forces another reflow 2226 + } 2227 + ``` 2228 + 2229 + **Correct: batch writes, then read once** 2230 + 2231 + ```typescript 2232 + function updateElementStyles(element: HTMLElement) { 2233 + // Batch all writes together 2234 + element.style.width = '100px' 2235 + element.style.height = '200px' 2236 + element.style.backgroundColor = 'blue' 2237 + element.style.border = '1px solid black' 2238 + 2239 + // Read after all writes are done (single reflow) 2240 + const { width, height } = element.getBoundingClientRect() 2241 + } 2242 + ``` 2243 + 2244 + **Correct: batch reads, then writes** 2245 + 2246 + ```typescript 2247 + function updateElementStyles(element: HTMLElement) { 2248 + element.classList.add('highlighted-box') 2249 + 2250 + const { width, height } = element.getBoundingClientRect() 2251 + } 2252 + ``` 2253 + 2254 + **Better: use CSS classes** 2255 + 2256 + **React example:** 2257 + 2258 + ```tsx 2259 + // Incorrect: interleaving style changes with layout queries 2260 + function Box({ isHighlighted }: { isHighlighted: boolean }) { 2261 + const ref = useRef<HTMLDivElement>(null) 2262 + 2263 + useEffect(() => { 2264 + if (ref.current && isHighlighted) { 2265 + ref.current.style.width = '100px' 2266 + const width = ref.current.offsetWidth // Forces layout 2267 + ref.current.style.height = '200px' 2268 + } 2269 + }, [isHighlighted]) 2270 + 2271 + return <div ref={ref}>Content</div> 2272 + } 2273 + 2274 + // Correct: toggle class 2275 + function Box({ isHighlighted }: { isHighlighted: boolean }) { 2276 + return ( 2277 + <div className={isHighlighted ? 'highlighted-box' : ''}> 2278 + Content 2279 + </div> 2280 + ) 2281 + } 2282 + ``` 2283 + 2284 + Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain. 2285 + 2286 + See [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations. 2287 + 2288 + ### 7.2 Build Index Maps for Repeated Lookups 2289 + 2290 + **Impact: LOW-MEDIUM (1M ops to 2K ops)** 2291 + 2292 + Multiple `.find()` calls by the same key should use a Map. 2293 + 2294 + **Incorrect (O(n) per lookup):** 2295 + 2296 + ```typescript 2297 + function processOrders(orders: Order[], users: User[]) { 2298 + return orders.map(order => ({ 2299 + ...order, 2300 + user: users.find(u => u.id === order.userId) 2301 + })) 2302 + } 2303 + ``` 2304 + 2305 + **Correct (O(1) per lookup):** 2306 + 2307 + ```typescript 2308 + function processOrders(orders: Order[], users: User[]) { 2309 + const userById = new Map(users.map(u => [u.id, u])) 2310 + 2311 + return orders.map(order => ({ 2312 + ...order, 2313 + user: userById.get(order.userId) 2314 + })) 2315 + } 2316 + ``` 2317 + 2318 + Build map once (O(n)), then all lookups are O(1). 2319 + 2320 + For 1000 orders × 1000 users: 1M ops → 2K ops. 2321 + 2322 + ### 7.3 Cache Property Access in Loops 2323 + 2324 + **Impact: LOW-MEDIUM (reduces lookups)** 2325 + 2326 + Cache object property lookups in hot paths. 2327 + 2328 + **Incorrect: 3 lookups × N iterations** 2329 + 2330 + ```typescript 2331 + for (let i = 0; i < arr.length; i++) { 2332 + process(obj.config.settings.value) 2333 + } 2334 + ``` 2335 + 2336 + **Correct: 1 lookup total** 2337 + 2338 + ```typescript 2339 + const value = obj.config.settings.value 2340 + const len = arr.length 2341 + for (let i = 0; i < len; i++) { 2342 + process(value) 2343 + } 2344 + ``` 2345 + 2346 + ### 7.4 Cache Repeated Function Calls 2347 + 2348 + **Impact: MEDIUM (avoid redundant computation)** 2349 + 2350 + Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render. 2351 + 2352 + **Incorrect: redundant computation** 2353 + 2354 + ```typescript 2355 + function ProjectList({ projects }: { projects: Project[] }) { 2356 + return ( 2357 + <div> 2358 + {projects.map(project => { 2359 + // slugify() called 100+ times for same project names 2360 + const slug = slugify(project.name) 2361 + 2362 + return <ProjectCard key={project.id} slug={slug} /> 2363 + })} 2364 + </div> 2365 + ) 2366 + } 2367 + ``` 2368 + 2369 + **Correct: cached results** 2370 + 2371 + ```typescript 2372 + // Module-level cache 2373 + const slugifyCache = new Map<string, string>() 2374 + 2375 + function cachedSlugify(text: string): string { 2376 + if (slugifyCache.has(text)) { 2377 + return slugifyCache.get(text)! 2378 + } 2379 + const result = slugify(text) 2380 + slugifyCache.set(text, result) 2381 + return result 2382 + } 2383 + 2384 + function ProjectList({ projects }: { projects: Project[] }) { 2385 + return ( 2386 + <div> 2387 + {projects.map(project => { 2388 + // Computed only once per unique project name 2389 + const slug = cachedSlugify(project.name) 2390 + 2391 + return <ProjectCard key={project.id} slug={slug} /> 2392 + })} 2393 + </div> 2394 + ) 2395 + } 2396 + ``` 2397 + 2398 + **Simpler pattern for single-value functions:** 2399 + 2400 + ```typescript 2401 + let isLoggedInCache: boolean | null = null 2402 + 2403 + function isLoggedIn(): boolean { 2404 + if (isLoggedInCache !== null) { 2405 + return isLoggedInCache 2406 + } 2407 + 2408 + isLoggedInCache = document.cookie.includes('auth=') 2409 + return isLoggedInCache 2410 + } 2411 + 2412 + // Clear cache when auth changes 2413 + function onAuthChange() { 2414 + isLoggedInCache = null 2415 + } 2416 + ``` 2417 + 2418 + Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components. 2419 + 2420 + Reference: [https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast) 2421 + 2422 + ### 7.5 Cache Storage API Calls 2423 + 2424 + **Impact: LOW-MEDIUM (reduces expensive I/O)** 2425 + 2426 + `localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory. 2427 + 2428 + **Incorrect: reads storage on every call** 2429 + 2430 + ```typescript 2431 + function getTheme() { 2432 + return localStorage.getItem('theme') ?? 'light' 2433 + } 2434 + // Called 10 times = 10 storage reads 2435 + ``` 2436 + 2437 + **Correct: Map cache** 2438 + 2439 + ```typescript 2440 + const storageCache = new Map<string, string | null>() 2441 + 2442 + function getLocalStorage(key: string) { 2443 + if (!storageCache.has(key)) { 2444 + storageCache.set(key, localStorage.getItem(key)) 2445 + } 2446 + return storageCache.get(key) 2447 + } 2448 + 2449 + function setLocalStorage(key: string, value: string) { 2450 + localStorage.setItem(key, value) 2451 + storageCache.set(key, value) // keep cache in sync 2452 + } 2453 + ``` 2454 + 2455 + Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components. 2456 + 2457 + **Cookie caching:** 2458 + 2459 + ```typescript 2460 + let cookieCache: Record<string, string> | null = null 2461 + 2462 + function getCookie(name: string) { 2463 + if (!cookieCache) { 2464 + cookieCache = Object.fromEntries( 2465 + document.cookie.split('; ').map(c => c.split('=')) 2466 + ) 2467 + } 2468 + return cookieCache[name] 2469 + } 2470 + ``` 2471 + 2472 + **Important: invalidate on external changes** 2473 + 2474 + ```typescript 2475 + window.addEventListener('storage', (e) => { 2476 + if (e.key) storageCache.delete(e.key) 2477 + }) 2478 + 2479 + document.addEventListener('visibilitychange', () => { 2480 + if (document.visibilityState === 'visible') { 2481 + storageCache.clear() 2482 + } 2483 + }) 2484 + ``` 2485 + 2486 + If storage can change externally (another tab, server-set cookies), invalidate cache: 2487 + 2488 + ### 7.6 Combine Multiple Array Iterations 2489 + 2490 + **Impact: LOW-MEDIUM (reduces iterations)** 2491 + 2492 + Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop. 2493 + 2494 + **Incorrect: 3 iterations** 2495 + 2496 + ```typescript 2497 + const admins = users.filter(u => u.isAdmin) 2498 + const testers = users.filter(u => u.isTester) 2499 + const inactive = users.filter(u => !u.isActive) 2500 + ``` 2501 + 2502 + **Correct: 1 iteration** 2503 + 2504 + ```typescript 2505 + const admins: User[] = [] 2506 + const testers: User[] = [] 2507 + const inactive: User[] = [] 2508 + 2509 + for (const user of users) { 2510 + if (user.isAdmin) admins.push(user) 2511 + if (user.isTester) testers.push(user) 2512 + if (!user.isActive) inactive.push(user) 2513 + } 2514 + ``` 2515 + 2516 + ### 7.7 Early Length Check for Array Comparisons 2517 + 2518 + **Impact: MEDIUM-HIGH (avoids expensive operations when lengths differ)** 2519 + 2520 + When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal. 2521 + 2522 + In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops). 2523 + 2524 + **Incorrect: always runs expensive comparison** 2525 + 2526 + ```typescript 2527 + function hasChanges(current: string[], original: string[]) { 2528 + // Always sorts and joins, even when lengths differ 2529 + return current.sort().join() !== original.sort().join() 2530 + } 2531 + ``` 2532 + 2533 + Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings. 2534 + 2535 + **Correct (O(1) length check first):** 2536 + 2537 + ```typescript 2538 + function hasChanges(current: string[], original: string[]) { 2539 + // Early return if lengths differ 2540 + if (current.length !== original.length) { 2541 + return true 2542 + } 2543 + // Only sort when lengths match 2544 + const currentSorted = current.toSorted() 2545 + const originalSorted = original.toSorted() 2546 + for (let i = 0; i < currentSorted.length; i++) { 2547 + if (currentSorted[i] !== originalSorted[i]) { 2548 + return true 2549 + } 2550 + } 2551 + return false 2552 + } 2553 + ``` 2554 + 2555 + This new approach is more efficient because: 2556 + 2557 + - It avoids the overhead of sorting and joining the arrays when lengths differ 2558 + 2559 + - It avoids consuming memory for the joined strings (especially important for large arrays) 2560 + 2561 + - It avoids mutating the original arrays 2562 + 2563 + - It returns early when a difference is found 2564 + 2565 + ### 7.8 Early Return from Functions 2566 + 2567 + **Impact: LOW-MEDIUM (avoids unnecessary computation)** 2568 + 2569 + Return early when result is determined to skip unnecessary processing. 2570 + 2571 + **Incorrect: processes all items even after finding answer** 2572 + 2573 + ```typescript 2574 + function validateUsers(users: User[]) { 2575 + let hasError = false 2576 + let errorMessage = '' 2577 + 2578 + for (const user of users) { 2579 + if (!user.email) { 2580 + hasError = true 2581 + errorMessage = 'Email required' 2582 + } 2583 + if (!user.name) { 2584 + hasError = true 2585 + errorMessage = 'Name required' 2586 + } 2587 + // Continues checking all users even after error found 2588 + } 2589 + 2590 + return hasError ? { valid: false, error: errorMessage } : { valid: true } 2591 + } 2592 + ``` 2593 + 2594 + **Correct: returns immediately on first error** 2595 + 2596 + ```typescript 2597 + function validateUsers(users: User[]) { 2598 + for (const user of users) { 2599 + if (!user.email) { 2600 + return { valid: false, error: 'Email required' } 2601 + } 2602 + if (!user.name) { 2603 + return { valid: false, error: 'Name required' } 2604 + } 2605 + } 2606 + 2607 + return { valid: true } 2608 + } 2609 + ``` 2610 + 2611 + ### 7.9 Hoist RegExp Creation 2612 + 2613 + **Impact: LOW-MEDIUM (avoids recreation)** 2614 + 2615 + Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`. 2616 + 2617 + **Incorrect: new RegExp every render** 2618 + 2619 + ```tsx 2620 + function Highlighter({ text, query }: Props) { 2621 + const regex = new RegExp(`(${query})`, 'gi') 2622 + const parts = text.split(regex) 2623 + return <>{parts.map((part, i) => ...)}</> 2624 + } 2625 + ``` 2626 + 2627 + **Correct: memoize or hoist** 2628 + 2629 + ```tsx 2630 + const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ 2631 + 2632 + function Highlighter({ text, query }: Props) { 2633 + const regex = useMemo( 2634 + () => new RegExp(`(${escapeRegex(query)})`, 'gi'), 2635 + [query] 2636 + ) 2637 + const parts = text.split(regex) 2638 + return <>{parts.map((part, i) => ...)}</> 2639 + } 2640 + ``` 2641 + 2642 + **Warning: global regex has mutable state** 2643 + 2644 + ```typescript 2645 + const regex = /foo/g 2646 + regex.test('foo') // true, lastIndex = 3 2647 + regex.test('foo') // false, lastIndex = 0 2648 + ``` 2649 + 2650 + Global regex (`/g`) has mutable `lastIndex` state: 2651 + 2652 + ### 7.10 Use Loop for Min/Max Instead of Sort 2653 + 2654 + **Impact: LOW (O(n) instead of O(n log n))** 2655 + 2656 + Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower. 2657 + 2658 + **Incorrect (O(n log n) - sort to find latest):** 2659 + 2660 + ```typescript 2661 + interface Project { 2662 + id: string 2663 + name: string 2664 + updatedAt: number 2665 + } 2666 + 2667 + function getLatestProject(projects: Project[]) { 2668 + const sorted = [...projects].sort((a, b) => b.updatedAt - a.updatedAt) 2669 + return sorted[0] 2670 + } 2671 + ``` 2672 + 2673 + Sorts the entire array just to find the maximum value. 2674 + 2675 + **Incorrect (O(n log n) - sort for oldest and newest):** 2676 + 2677 + ```typescript 2678 + function getOldestAndNewest(projects: Project[]) { 2679 + const sorted = [...projects].sort((a, b) => a.updatedAt - b.updatedAt) 2680 + return { oldest: sorted[0], newest: sorted[sorted.length - 1] } 2681 + } 2682 + ``` 2683 + 2684 + Still sorts unnecessarily when only min/max are needed. 2685 + 2686 + **Correct (O(n) - single loop):** 2687 + 2688 + ```typescript 2689 + function getLatestProject(projects: Project[]) { 2690 + if (projects.length === 0) return null 2691 + 2692 + let latest = projects[0] 2693 + 2694 + for (let i = 1; i < projects.length; i++) { 2695 + if (projects[i].updatedAt > latest.updatedAt) { 2696 + latest = projects[i] 2697 + } 2698 + } 2699 + 2700 + return latest 2701 + } 2702 + 2703 + function getOldestAndNewest(projects: Project[]) { 2704 + if (projects.length === 0) return { oldest: null, newest: null } 2705 + 2706 + let oldest = projects[0] 2707 + let newest = projects[0] 2708 + 2709 + for (let i = 1; i < projects.length; i++) { 2710 + if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i] 2711 + if (projects[i].updatedAt > newest.updatedAt) newest = projects[i] 2712 + } 2713 + 2714 + return { oldest, newest } 2715 + } 2716 + ``` 2717 + 2718 + Single pass through the array, no copying, no sorting. 2719 + 2720 + **Alternative: Math.min/Math.max for small arrays** 2721 + 2722 + ```typescript 2723 + const numbers = [5, 2, 8, 1, 9] 2724 + const min = Math.min(...numbers) 2725 + const max = Math.max(...numbers) 2726 + ``` 2727 + 2728 + This works for small arrays, but can be slower or just throw an error for very large arrays due to spread operator limitations. Maximal array length is approximately 124000 in Chrome 143 and 638000 in Safari 18; exact numbers may vary - see [the fiddle](https://jsfiddle.net/qw1jabsx/4/). Use the loop approach for reliability. 2729 + 2730 + ### 7.11 Use Set/Map for O(1) Lookups 2731 + 2732 + **Impact: LOW-MEDIUM (O(n) to O(1))** 2733 + 2734 + Convert arrays to Set/Map for repeated membership checks. 2735 + 2736 + **Incorrect (O(n) per check):** 2737 + 2738 + ```typescript 2739 + const allowedIds = ['a', 'b', 'c', ...] 2740 + items.filter(item => allowedIds.includes(item.id)) 2741 + ``` 2742 + 2743 + **Correct (O(1) per check):** 2744 + 2745 + ```typescript 2746 + const allowedIds = new Set(['a', 'b', 'c', ...]) 2747 + items.filter(item => allowedIds.has(item.id)) 2748 + ``` 2749 + 2750 + ### 7.12 Use toSorted() Instead of sort() for Immutability 2751 + 2752 + **Impact: MEDIUM-HIGH (prevents mutation bugs in React state)** 2753 + 2754 + `.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation. 2755 + 2756 + **Incorrect: mutates original array** 2757 + 2758 + ```typescript 2759 + function UserList({ users }: { users: User[] }) { 2760 + // Mutates the users prop array! 2761 + const sorted = useMemo( 2762 + () => users.sort((a, b) => a.name.localeCompare(b.name)), 2763 + [users] 2764 + ) 2765 + return <div>{sorted.map(renderUser)}</div> 2766 + } 2767 + ``` 2768 + 2769 + **Correct: creates new array** 2770 + 2771 + ```typescript 2772 + function UserList({ users }: { users: User[] }) { 2773 + // Creates new sorted array, original unchanged 2774 + const sorted = useMemo( 2775 + () => users.toSorted((a, b) => a.name.localeCompare(b.name)), 2776 + [users] 2777 + ) 2778 + return <div>{sorted.map(renderUser)}</div> 2779 + } 2780 + ``` 2781 + 2782 + **Why this matters in React:** 2783 + 2784 + 1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only 2785 + 2786 + 2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior 2787 + 2788 + **Browser support: fallback for older browsers** 2789 + 2790 + ```typescript 2791 + // Fallback for older browsers 2792 + const sorted = [...items].sort((a, b) => a.value - b.value) 2793 + ``` 2794 + 2795 + `.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator: 2796 + 2797 + **Other immutable array methods:** 2798 + 2799 + - `.toSorted()` - immutable sort 2800 + 2801 + - `.toReversed()` - immutable reverse 2802 + 2803 + - `.toSpliced()` - immutable splice 2804 + 2805 + - `.with()` - immutable element replacement 2806 + 2807 + --- 2808 + 2809 + ## 8. Advanced Patterns 2810 + 2811 + **Impact: LOW** 2812 + 2813 + Advanced patterns for specific cases that require careful implementation. 2814 + 2815 + ### 8.1 Initialize App Once, Not Per Mount 2816 + 2817 + **Impact: LOW-MEDIUM (avoids duplicate init in development)** 2818 + 2819 + Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead. 2820 + 2821 + **Incorrect: runs twice in dev, re-runs on remount** 2822 + 2823 + ```tsx 2824 + function Comp() { 2825 + useEffect(() => { 2826 + loadFromStorage() 2827 + checkAuthToken() 2828 + }, []) 2829 + 2830 + // ... 2831 + } 2832 + ``` 2833 + 2834 + **Correct: once per app load** 2835 + 2836 + ```tsx 2837 + let didInit = false 2838 + 2839 + function Comp() { 2840 + useEffect(() => { 2841 + if (didInit) return 2842 + didInit = true 2843 + loadFromStorage() 2844 + checkAuthToken() 2845 + }, []) 2846 + 2847 + // ... 2848 + } 2849 + ``` 2850 + 2851 + Reference: [https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application) 2852 + 2853 + ### 8.2 Store Event Handlers in Refs 2854 + 2855 + **Impact: LOW (stable subscriptions)** 2856 + 2857 + Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes. 2858 + 2859 + **Incorrect: re-subscribes on every render** 2860 + 2861 + ```tsx 2862 + function useWindowEvent(event: string, handler: (e) => void) { 2863 + useEffect(() => { 2864 + window.addEventListener(event, handler) 2865 + return () => window.removeEventListener(event, handler) 2866 + }, [event, handler]) 2867 + } 2868 + ``` 2869 + 2870 + **Correct: stable subscription** 2871 + 2872 + ```tsx 2873 + import { useEffectEvent } from 'react' 2874 + 2875 + function useWindowEvent(event: string, handler: (e) => void) { 2876 + const onEvent = useEffectEvent(handler) 2877 + 2878 + useEffect(() => { 2879 + window.addEventListener(event, onEvent) 2880 + return () => window.removeEventListener(event, onEvent) 2881 + }, [event]) 2882 + } 2883 + ``` 2884 + 2885 + **Alternative: use `useEffectEvent` if you're on latest React:** 2886 + 2887 + `useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler. 2888 + 2889 + ### 8.3 useEffectEvent for Stable Callback Refs 2890 + 2891 + **Impact: LOW (prevents effect re-runs)** 2892 + 2893 + Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures. 2894 + 2895 + **Incorrect: effect re-runs on every callback change** 2896 + 2897 + ```tsx 2898 + function SearchInput({ onSearch }: { onSearch: (q: string) => void }) { 2899 + const [query, setQuery] = useState('') 2900 + 2901 + useEffect(() => { 2902 + const timeout = setTimeout(() => onSearch(query), 300) 2903 + return () => clearTimeout(timeout) 2904 + }, [query, onSearch]) 2905 + } 2906 + ``` 2907 + 2908 + **Correct: using React's useEffectEvent** 2909 + 2910 + ```tsx 2911 + import { useEffectEvent } from 'react'; 2912 + 2913 + function SearchInput({ onSearch }: { onSearch: (q: string) => void }) { 2914 + const [query, setQuery] = useState('') 2915 + const onSearchEvent = useEffectEvent(onSearch) 2916 + 2917 + useEffect(() => { 2918 + const timeout = setTimeout(() => onSearchEvent(query), 300) 2919 + return () => clearTimeout(timeout) 2920 + }, [query]) 2921 + } 2922 + ``` 2923 + 2924 + --- 2925 + 2926 + ## References 2927 + 2928 + 1. [https://react.dev](https://react.dev) 2929 + 2. [https://nextjs.org](https://nextjs.org) 2930 + 3. [https://swr.vercel.app](https://swr.vercel.app) 2931 + 4. [https://github.com/shuding/better-all](https://github.com/shuding/better-all) 2932 + 5. [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache) 2933 + 6. [https://vercel.com/blog/how-we-optimized-package-imports-in-next-js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js) 2934 + 7. [https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)
+123
.claude/skills/react-best-practices/README.md
··· 1 + # React Best Practices 2 + 3 + A structured repository for creating and maintaining React Best Practices optimized for agents and LLMs. 4 + 5 + ## Structure 6 + 7 + - `rules/` - Individual rule files (one per rule) 8 + - `_sections.md` - Section metadata (titles, impacts, descriptions) 9 + - `_template.md` - Template for creating new rules 10 + - `area-description.md` - Individual rule files 11 + - `src/` - Build scripts and utilities 12 + - `metadata.json` - Document metadata (version, organization, abstract) 13 + - __`AGENTS.md`__ - Compiled output (generated) 14 + - __`test-cases.json`__ - Test cases for LLM evaluation (generated) 15 + 16 + ## Getting Started 17 + 18 + 1. Install dependencies: 19 + ```bash 20 + pnpm install 21 + ``` 22 + 23 + 2. Build AGENTS.md from rules: 24 + ```bash 25 + pnpm build 26 + ``` 27 + 28 + 3. Validate rule files: 29 + ```bash 30 + pnpm validate 31 + ``` 32 + 33 + 4. Extract test cases: 34 + ```bash 35 + pnpm extract-tests 36 + ``` 37 + 38 + ## Creating a New Rule 39 + 40 + 1. Copy `rules/_template.md` to `rules/area-description.md` 41 + 2. Choose the appropriate area prefix: 42 + - `async-` for Eliminating Waterfalls (Section 1) 43 + - `bundle-` for Bundle Size Optimization (Section 2) 44 + - `server-` for Server-Side Performance (Section 3) 45 + - `client-` for Client-Side Data Fetching (Section 4) 46 + - `rerender-` for Re-render Optimization (Section 5) 47 + - `rendering-` for Rendering Performance (Section 6) 48 + - `js-` for JavaScript Performance (Section 7) 49 + - `advanced-` for Advanced Patterns (Section 8) 50 + 3. Fill in the frontmatter and content 51 + 4. Ensure you have clear examples with explanations 52 + 5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json 53 + 54 + ## Rule File Structure 55 + 56 + Each rule file should follow this structure: 57 + 58 + ```markdown 59 + --- 60 + title: Rule Title Here 61 + impact: MEDIUM 62 + impactDescription: Optional description 63 + tags: tag1, tag2, tag3 64 + --- 65 + 66 + ## Rule Title Here 67 + 68 + Brief explanation of the rule and why it matters. 69 + 70 + **Incorrect (description of what's wrong):** 71 + 72 + ```typescript 73 + // Bad code example 74 + ``` 75 + 76 + **Correct (description of what's right):** 77 + 78 + ```typescript 79 + // Good code example 80 + ``` 81 + 82 + Optional explanatory text after examples. 83 + 84 + Reference: [Link](https://example.com) 85 + 86 + ## File Naming Convention 87 + 88 + - Files starting with `_` are special (excluded from build) 89 + - Rule files: `area-description.md` (e.g., `async-parallel.md`) 90 + - Section is automatically inferred from filename prefix 91 + - Rules are sorted alphabetically by title within each section 92 + - IDs (e.g., 1.1, 1.2) are auto-generated during build 93 + 94 + ## Impact Levels 95 + 96 + - `CRITICAL` - Highest priority, major performance gains 97 + - `HIGH` - Significant performance improvements 98 + - `MEDIUM-HIGH` - Moderate-high gains 99 + - `MEDIUM` - Moderate performance improvements 100 + - `LOW-MEDIUM` - Low-medium gains 101 + - `LOW` - Incremental improvements 102 + 103 + ## Scripts 104 + 105 + - `pnpm build` - Compile rules into AGENTS.md 106 + - `pnpm validate` - Validate all rule files 107 + - `pnpm extract-tests` - Extract test cases for LLM evaluation 108 + - `pnpm dev` - Build and validate 109 + 110 + ## Contributing 111 + 112 + When adding or modifying rules: 113 + 114 + 1. Use the correct filename prefix for your section 115 + 2. Follow the `_template.md` structure 116 + 3. Include clear bad/good examples with explanations 117 + 4. Add appropriate tags 118 + 5. Run `pnpm build` to regenerate AGENTS.md and test-cases.json 119 + 6. Rules are automatically sorted by title - no need to manage numbers! 120 + 121 + ## Acknowledgments 122 + 123 + Originally created by [@shuding](https://x.com/shuding) at [Vercel](https://vercel.com).
+136
.claude/skills/react-best-practices/SKILL.md
··· 1 + --- 2 + name: vercel-react-best-practices 3 + description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements. 4 + license: MIT 5 + metadata: 6 + author: vercel 7 + version: "1.0.0" 8 + --- 9 + 10 + # Vercel React Best Practices 11 + 12 + Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 57 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation. 13 + 14 + ## When to Apply 15 + 16 + Reference these guidelines when: 17 + - Writing new React components or Next.js pages 18 + - Implementing data fetching (client or server-side) 19 + - Reviewing code for performance issues 20 + - Refactoring existing React/Next.js code 21 + - Optimizing bundle size or load times 22 + 23 + ## Rule Categories by Priority 24 + 25 + | Priority | Category | Impact | Prefix | 26 + |----------|----------|--------|--------| 27 + | 1 | Eliminating Waterfalls | CRITICAL | `async-` | 28 + | 2 | Bundle Size Optimization | CRITICAL | `bundle-` | 29 + | 3 | Server-Side Performance | HIGH | `server-` | 30 + | 4 | Client-Side Data Fetching | MEDIUM-HIGH | `client-` | 31 + | 5 | Re-render Optimization | MEDIUM | `rerender-` | 32 + | 6 | Rendering Performance | MEDIUM | `rendering-` | 33 + | 7 | JavaScript Performance | LOW-MEDIUM | `js-` | 34 + | 8 | Advanced Patterns | LOW | `advanced-` | 35 + 36 + ## Quick Reference 37 + 38 + ### 1. Eliminating Waterfalls (CRITICAL) 39 + 40 + - `async-defer-await` - Move await into branches where actually used 41 + - `async-parallel` - Use Promise.all() for independent operations 42 + - `async-dependencies` - Use better-all for partial dependencies 43 + - `async-api-routes` - Start promises early, await late in API routes 44 + - `async-suspense-boundaries` - Use Suspense to stream content 45 + 46 + ### 2. Bundle Size Optimization (CRITICAL) 47 + 48 + - `bundle-barrel-imports` - Import directly, avoid barrel files 49 + - `bundle-dynamic-imports` - Use next/dynamic for heavy components 50 + - `bundle-defer-third-party` - Load analytics/logging after hydration 51 + - `bundle-conditional` - Load modules only when feature is activated 52 + - `bundle-preload` - Preload on hover/focus for perceived speed 53 + 54 + ### 3. Server-Side Performance (HIGH) 55 + 56 + - `server-auth-actions` - Authenticate server actions like API routes 57 + - `server-cache-react` - Use React.cache() for per-request deduplication 58 + - `server-cache-lru` - Use LRU cache for cross-request caching 59 + - `server-dedup-props` - Avoid duplicate serialization in RSC props 60 + - `server-serialization` - Minimize data passed to client components 61 + - `server-parallel-fetching` - Restructure components to parallelize fetches 62 + - `server-after-nonblocking` - Use after() for non-blocking operations 63 + 64 + ### 4. Client-Side Data Fetching (MEDIUM-HIGH) 65 + 66 + - `client-swr-dedup` - Use SWR for automatic request deduplication 67 + - `client-event-listeners` - Deduplicate global event listeners 68 + - `client-passive-event-listeners` - Use passive listeners for scroll 69 + - `client-localstorage-schema` - Version and minimize localStorage data 70 + 71 + ### 5. Re-render Optimization (MEDIUM) 72 + 73 + - `rerender-defer-reads` - Don't subscribe to state only used in callbacks 74 + - `rerender-memo` - Extract expensive work into memoized components 75 + - `rerender-memo-with-default-value` - Hoist default non-primitive props 76 + - `rerender-dependencies` - Use primitive dependencies in effects 77 + - `rerender-derived-state` - Subscribe to derived booleans, not raw values 78 + - `rerender-derived-state-no-effect` - Derive state during render, not effects 79 + - `rerender-functional-setstate` - Use functional setState for stable callbacks 80 + - `rerender-lazy-state-init` - Pass function to useState for expensive values 81 + - `rerender-simple-expression-in-memo` - Avoid memo for simple primitives 82 + - `rerender-move-effect-to-event` - Put interaction logic in event handlers 83 + - `rerender-transitions` - Use startTransition for non-urgent updates 84 + - `rerender-use-ref-transient-values` - Use refs for transient frequent values 85 + 86 + ### 6. Rendering Performance (MEDIUM) 87 + 88 + - `rendering-animate-svg-wrapper` - Animate div wrapper, not SVG element 89 + - `rendering-content-visibility` - Use content-visibility for long lists 90 + - `rendering-hoist-jsx` - Extract static JSX outside components 91 + - `rendering-svg-precision` - Reduce SVG coordinate precision 92 + - `rendering-hydration-no-flicker` - Use inline script for client-only data 93 + - `rendering-hydration-suppress-warning` - Suppress expected mismatches 94 + - `rendering-activity` - Use Activity component for show/hide 95 + - `rendering-conditional-render` - Use ternary, not && for conditionals 96 + - `rendering-usetransition-loading` - Prefer useTransition for loading state 97 + 98 + ### 7. JavaScript Performance (LOW-MEDIUM) 99 + 100 + - `js-batch-dom-css` - Group CSS changes via classes or cssText 101 + - `js-index-maps` - Build Map for repeated lookups 102 + - `js-cache-property-access` - Cache object properties in loops 103 + - `js-cache-function-results` - Cache function results in module-level Map 104 + - `js-cache-storage` - Cache localStorage/sessionStorage reads 105 + - `js-combine-iterations` - Combine multiple filter/map into one loop 106 + - `js-length-check-first` - Check array length before expensive comparison 107 + - `js-early-exit` - Return early from functions 108 + - `js-hoist-regexp` - Hoist RegExp creation outside loops 109 + - `js-min-max-loop` - Use loop for min/max instead of sort 110 + - `js-set-map-lookups` - Use Set/Map for O(1) lookups 111 + - `js-tosorted-immutable` - Use toSorted() for immutability 112 + 113 + ### 8. Advanced Patterns (LOW) 114 + 115 + - `advanced-event-handler-refs` - Store event handlers in refs 116 + - `advanced-init-once` - Initialize app once per app load 117 + - `advanced-use-latest` - useLatest for stable callback refs 118 + 119 + ## How to Use 120 + 121 + Read individual rule files for detailed explanations and code examples: 122 + 123 + ``` 124 + rules/async-parallel.md 125 + rules/bundle-barrel-imports.md 126 + ``` 127 + 128 + Each rule file contains: 129 + - Brief explanation of why it matters 130 + - Incorrect code example with explanation 131 + - Correct code example with explanation 132 + - Additional context and references 133 + 134 + ## Full Compiled Document 135 + 136 + For the complete guide with all rules expanded: `AGENTS.md`
+15
.claude/skills/react-best-practices/metadata.json
··· 1 + { 2 + "version": "1.0.0", 3 + "organization": "Vercel Engineering", 4 + "date": "January 2026", 5 + "abstract": "Comprehensive performance optimization guide for React and Next.js applications, designed for AI agents and LLMs. Contains 40+ rules across 8 categories, prioritized by impact from critical (eliminating waterfalls, reducing bundle size) to incremental (advanced patterns). Each rule includes detailed explanations, real-world examples comparing incorrect vs. correct implementations, and specific impact metrics to guide automated refactoring and code generation.", 6 + "references": [ 7 + "https://react.dev", 8 + "https://nextjs.org", 9 + "https://swr.vercel.app", 10 + "https://github.com/shuding/better-all", 11 + "https://github.com/isaacs/node-lru-cache", 12 + "https://vercel.com/blog/how-we-optimized-package-imports-in-next-js", 13 + "https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast" 14 + ] 15 + }
+46
.claude/skills/react-best-practices/rules/_sections.md
··· 1 + # Sections 2 + 3 + This file defines all sections, their ordering, impact levels, and descriptions. 4 + The section ID (in parentheses) is the filename prefix used to group rules. 5 + 6 + --- 7 + 8 + ## 1. Eliminating Waterfalls (async) 9 + 10 + **Impact:** CRITICAL 11 + **Description:** Waterfalls are the #1 performance killer. Each sequential await adds full network latency. Eliminating them yields the largest gains. 12 + 13 + ## 2. Bundle Size Optimization (bundle) 14 + 15 + **Impact:** CRITICAL 16 + **Description:** Reducing initial bundle size improves Time to Interactive and Largest Contentful Paint. 17 + 18 + ## 3. Server-Side Performance (server) 19 + 20 + **Impact:** HIGH 21 + **Description:** Optimizing server-side rendering and data fetching eliminates server-side waterfalls and reduces response times. 22 + 23 + ## 4. Client-Side Data Fetching (client) 24 + 25 + **Impact:** MEDIUM-HIGH 26 + **Description:** Automatic deduplication and efficient data fetching patterns reduce redundant network requests. 27 + 28 + ## 5. Re-render Optimization (rerender) 29 + 30 + **Impact:** MEDIUM 31 + **Description:** Reducing unnecessary re-renders minimizes wasted computation and improves UI responsiveness. 32 + 33 + ## 6. Rendering Performance (rendering) 34 + 35 + **Impact:** MEDIUM 36 + **Description:** Optimizing the rendering process reduces the work the browser needs to do. 37 + 38 + ## 7. JavaScript Performance (js) 39 + 40 + **Impact:** LOW-MEDIUM 41 + **Description:** Micro-optimizations for hot paths can add up to meaningful improvements. 42 + 43 + ## 8. Advanced Patterns (advanced) 44 + 45 + **Impact:** LOW 46 + **Description:** Advanced patterns for specific cases that require careful implementation.
+28
.claude/skills/react-best-practices/rules/_template.md
··· 1 + --- 2 + title: Rule Title Here 3 + impact: MEDIUM 4 + impactDescription: Optional description of impact (e.g., "20-50% improvement") 5 + tags: tag1, tag2 6 + --- 7 + 8 + ## Rule Title Here 9 + 10 + **Impact: MEDIUM (optional impact description)** 11 + 12 + Brief explanation of the rule and why it matters. This should be clear and concise, explaining the performance implications. 13 + 14 + **Incorrect (description of what's wrong):** 15 + 16 + ```typescript 17 + // Bad code example here 18 + const bad = example() 19 + ``` 20 + 21 + **Correct (description of what's right):** 22 + 23 + ```typescript 24 + // Good code example here 25 + const good = example() 26 + ``` 27 + 28 + Reference: [Link to documentation or resource](https://example.com)
+55
.claude/skills/react-best-practices/rules/advanced-event-handler-refs.md
··· 1 + --- 2 + title: Store Event Handlers in Refs 3 + impact: LOW 4 + impactDescription: stable subscriptions 5 + tags: advanced, hooks, refs, event-handlers, optimization 6 + --- 7 + 8 + ## Store Event Handlers in Refs 9 + 10 + Store callbacks in refs when used in effects that shouldn't re-subscribe on callback changes. 11 + 12 + **Incorrect (re-subscribes on every render):** 13 + 14 + ```tsx 15 + function useWindowEvent(event: string, handler: (e) => void) { 16 + useEffect(() => { 17 + window.addEventListener(event, handler) 18 + return () => window.removeEventListener(event, handler) 19 + }, [event, handler]) 20 + } 21 + ``` 22 + 23 + **Correct (stable subscription):** 24 + 25 + ```tsx 26 + function useWindowEvent(event: string, handler: (e) => void) { 27 + const handlerRef = useRef(handler) 28 + useEffect(() => { 29 + handlerRef.current = handler 30 + }, [handler]) 31 + 32 + useEffect(() => { 33 + const listener = (e) => handlerRef.current(e) 34 + window.addEventListener(event, listener) 35 + return () => window.removeEventListener(event, listener) 36 + }, [event]) 37 + } 38 + ``` 39 + 40 + **Alternative: use `useEffectEvent` if you're on latest React:** 41 + 42 + ```tsx 43 + import { useEffectEvent } from 'react' 44 + 45 + function useWindowEvent(event: string, handler: (e) => void) { 46 + const onEvent = useEffectEvent(handler) 47 + 48 + useEffect(() => { 49 + window.addEventListener(event, onEvent) 50 + return () => window.removeEventListener(event, onEvent) 51 + }, [event]) 52 + } 53 + ``` 54 + 55 + `useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.
+42
.claude/skills/react-best-practices/rules/advanced-init-once.md
··· 1 + --- 2 + title: Initialize App Once, Not Per Mount 3 + impact: LOW-MEDIUM 4 + impactDescription: avoids duplicate init in development 5 + tags: initialization, useEffect, app-startup, side-effects 6 + --- 7 + 8 + ## Initialize App Once, Not Per Mount 9 + 10 + Do not put app-wide initialization that must run once per app load inside `useEffect([])` of a component. Components can remount and effects will re-run. Use a module-level guard or top-level init in the entry module instead. 11 + 12 + **Incorrect (runs twice in dev, re-runs on remount):** 13 + 14 + ```tsx 15 + function Comp() { 16 + useEffect(() => { 17 + loadFromStorage() 18 + checkAuthToken() 19 + }, []) 20 + 21 + // ... 22 + } 23 + ``` 24 + 25 + **Correct (once per app load):** 26 + 27 + ```tsx 28 + let didInit = false 29 + 30 + function Comp() { 31 + useEffect(() => { 32 + if (didInit) return 33 + didInit = true 34 + loadFromStorage() 35 + checkAuthToken() 36 + }, []) 37 + 38 + // ... 39 + } 40 + ``` 41 + 42 + Reference: [Initializing the application](https://react.dev/learn/you-might-not-need-an-effect#initializing-the-application)
+39
.claude/skills/react-best-practices/rules/advanced-use-latest.md
··· 1 + --- 2 + title: useEffectEvent for Stable Callback Refs 3 + impact: LOW 4 + impactDescription: prevents effect re-runs 5 + tags: advanced, hooks, useEffectEvent, refs, optimization 6 + --- 7 + 8 + ## useEffectEvent for Stable Callback Refs 9 + 10 + Access latest values in callbacks without adding them to dependency arrays. Prevents effect re-runs while avoiding stale closures. 11 + 12 + **Incorrect (effect re-runs on every callback change):** 13 + 14 + ```tsx 15 + function SearchInput({ onSearch }: { onSearch: (q: string) => void }) { 16 + const [query, setQuery] = useState('') 17 + 18 + useEffect(() => { 19 + const timeout = setTimeout(() => onSearch(query), 300) 20 + return () => clearTimeout(timeout) 21 + }, [query, onSearch]) 22 + } 23 + ``` 24 + 25 + **Correct (using React's useEffectEvent):** 26 + 27 + ```tsx 28 + import { useEffectEvent } from 'react'; 29 + 30 + function SearchInput({ onSearch }: { onSearch: (q: string) => void }) { 31 + const [query, setQuery] = useState('') 32 + const onSearchEvent = useEffectEvent(onSearch) 33 + 34 + useEffect(() => { 35 + const timeout = setTimeout(() => onSearchEvent(query), 300) 36 + return () => clearTimeout(timeout) 37 + }, [query]) 38 + } 39 + ```
+38
.claude/skills/react-best-practices/rules/async-api-routes.md
··· 1 + --- 2 + title: Prevent Waterfall Chains in API Routes 3 + impact: CRITICAL 4 + impactDescription: 2-10× improvement 5 + tags: api-routes, server-actions, waterfalls, parallelization 6 + --- 7 + 8 + ## Prevent Waterfall Chains in API Routes 9 + 10 + In API routes and Server Actions, start independent operations immediately, even if you don't await them yet. 11 + 12 + **Incorrect (config waits for auth, data waits for both):** 13 + 14 + ```typescript 15 + export async function GET(request: Request) { 16 + const session = await auth() 17 + const config = await fetchConfig() 18 + const data = await fetchData(session.user.id) 19 + return Response.json({ data, config }) 20 + } 21 + ``` 22 + 23 + **Correct (auth and config start immediately):** 24 + 25 + ```typescript 26 + export async function GET(request: Request) { 27 + const sessionPromise = auth() 28 + const configPromise = fetchConfig() 29 + const session = await sessionPromise 30 + const [config, data] = await Promise.all([ 31 + configPromise, 32 + fetchData(session.user.id) 33 + ]) 34 + return Response.json({ data, config }) 35 + } 36 + ``` 37 + 38 + For operations with more complex dependency chains, use `better-all` to automatically maximize parallelism (see Dependency-Based Parallelization).
+80
.claude/skills/react-best-practices/rules/async-defer-await.md
··· 1 + --- 2 + title: Defer Await Until Needed 3 + impact: HIGH 4 + impactDescription: avoids blocking unused code paths 5 + tags: async, await, conditional, optimization 6 + --- 7 + 8 + ## Defer Await Until Needed 9 + 10 + Move `await` operations into the branches where they're actually used to avoid blocking code paths that don't need them. 11 + 12 + **Incorrect (blocks both branches):** 13 + 14 + ```typescript 15 + async function handleRequest(userId: string, skipProcessing: boolean) { 16 + const userData = await fetchUserData(userId) 17 + 18 + if (skipProcessing) { 19 + // Returns immediately but still waited for userData 20 + return { skipped: true } 21 + } 22 + 23 + // Only this branch uses userData 24 + return processUserData(userData) 25 + } 26 + ``` 27 + 28 + **Correct (only blocks when needed):** 29 + 30 + ```typescript 31 + async function handleRequest(userId: string, skipProcessing: boolean) { 32 + if (skipProcessing) { 33 + // Returns immediately without waiting 34 + return { skipped: true } 35 + } 36 + 37 + // Fetch only when needed 38 + const userData = await fetchUserData(userId) 39 + return processUserData(userData) 40 + } 41 + ``` 42 + 43 + **Another example (early return optimization):** 44 + 45 + ```typescript 46 + // Incorrect: always fetches permissions 47 + async function updateResource(resourceId: string, userId: string) { 48 + const permissions = await fetchPermissions(userId) 49 + const resource = await getResource(resourceId) 50 + 51 + if (!resource) { 52 + return { error: 'Not found' } 53 + } 54 + 55 + if (!permissions.canEdit) { 56 + return { error: 'Forbidden' } 57 + } 58 + 59 + return await updateResourceData(resource, permissions) 60 + } 61 + 62 + // Correct: fetches only when needed 63 + async function updateResource(resourceId: string, userId: string) { 64 + const resource = await getResource(resourceId) 65 + 66 + if (!resource) { 67 + return { error: 'Not found' } 68 + } 69 + 70 + const permissions = await fetchPermissions(userId) 71 + 72 + if (!permissions.canEdit) { 73 + return { error: 'Forbidden' } 74 + } 75 + 76 + return await updateResourceData(resource, permissions) 77 + } 78 + ``` 79 + 80 + This optimization is especially valuable when the skipped branch is frequently taken, or when the deferred operation is expensive.
+51
.claude/skills/react-best-practices/rules/async-dependencies.md
··· 1 + --- 2 + title: Dependency-Based Parallelization 3 + impact: CRITICAL 4 + impactDescription: 2-10× improvement 5 + tags: async, parallelization, dependencies, better-all 6 + --- 7 + 8 + ## Dependency-Based Parallelization 9 + 10 + For operations with partial dependencies, use `better-all` to maximize parallelism. It automatically starts each task at the earliest possible moment. 11 + 12 + **Incorrect (profile waits for config unnecessarily):** 13 + 14 + ```typescript 15 + const [user, config] = await Promise.all([ 16 + fetchUser(), 17 + fetchConfig() 18 + ]) 19 + const profile = await fetchProfile(user.id) 20 + ``` 21 + 22 + **Correct (config and profile run in parallel):** 23 + 24 + ```typescript 25 + import { all } from 'better-all' 26 + 27 + const { user, config, profile } = await all({ 28 + async user() { return fetchUser() }, 29 + async config() { return fetchConfig() }, 30 + async profile() { 31 + return fetchProfile((await this.$.user).id) 32 + } 33 + }) 34 + ``` 35 + 36 + **Alternative without extra dependencies:** 37 + 38 + We can also create all the promises first, and do `Promise.all()` at the end. 39 + 40 + ```typescript 41 + const userPromise = fetchUser() 42 + const profilePromise = userPromise.then(user => fetchProfile(user.id)) 43 + 44 + const [user, config, profile] = await Promise.all([ 45 + userPromise, 46 + fetchConfig(), 47 + profilePromise 48 + ]) 49 + ``` 50 + 51 + Reference: [https://github.com/shuding/better-all](https://github.com/shuding/better-all)
+28
.claude/skills/react-best-practices/rules/async-parallel.md
··· 1 + --- 2 + title: Promise.all() for Independent Operations 3 + impact: CRITICAL 4 + impactDescription: 2-10× improvement 5 + tags: async, parallelization, promises, waterfalls 6 + --- 7 + 8 + ## Promise.all() for Independent Operations 9 + 10 + When async operations have no interdependencies, execute them concurrently using `Promise.all()`. 11 + 12 + **Incorrect (sequential execution, 3 round trips):** 13 + 14 + ```typescript 15 + const user = await fetchUser() 16 + const posts = await fetchPosts() 17 + const comments = await fetchComments() 18 + ``` 19 + 20 + **Correct (parallel execution, 1 round trip):** 21 + 22 + ```typescript 23 + const [user, posts, comments] = await Promise.all([ 24 + fetchUser(), 25 + fetchPosts(), 26 + fetchComments() 27 + ]) 28 + ```
+99
.claude/skills/react-best-practices/rules/async-suspense-boundaries.md
··· 1 + --- 2 + title: Strategic Suspense Boundaries 3 + impact: HIGH 4 + impactDescription: faster initial paint 5 + tags: async, suspense, streaming, layout-shift 6 + --- 7 + 8 + ## Strategic Suspense Boundaries 9 + 10 + Instead of awaiting data in async components before returning JSX, use Suspense boundaries to show the wrapper UI faster while data loads. 11 + 12 + **Incorrect (wrapper blocked by data fetching):** 13 + 14 + ```tsx 15 + async function Page() { 16 + const data = await fetchData() // Blocks entire page 17 + 18 + return ( 19 + <div> 20 + <div>Sidebar</div> 21 + <div>Header</div> 22 + <div> 23 + <DataDisplay data={data} /> 24 + </div> 25 + <div>Footer</div> 26 + </div> 27 + ) 28 + } 29 + ``` 30 + 31 + The entire layout waits for data even though only the middle section needs it. 32 + 33 + **Correct (wrapper shows immediately, data streams in):** 34 + 35 + ```tsx 36 + function Page() { 37 + return ( 38 + <div> 39 + <div>Sidebar</div> 40 + <div>Header</div> 41 + <div> 42 + <Suspense fallback={<Skeleton />}> 43 + <DataDisplay /> 44 + </Suspense> 45 + </div> 46 + <div>Footer</div> 47 + </div> 48 + ) 49 + } 50 + 51 + async function DataDisplay() { 52 + const data = await fetchData() // Only blocks this component 53 + return <div>{data.content}</div> 54 + } 55 + ``` 56 + 57 + Sidebar, Header, and Footer render immediately. Only DataDisplay waits for data. 58 + 59 + **Alternative (share promise across components):** 60 + 61 + ```tsx 62 + function Page() { 63 + // Start fetch immediately, but don't await 64 + const dataPromise = fetchData() 65 + 66 + return ( 67 + <div> 68 + <div>Sidebar</div> 69 + <div>Header</div> 70 + <Suspense fallback={<Skeleton />}> 71 + <DataDisplay dataPromise={dataPromise} /> 72 + <DataSummary dataPromise={dataPromise} /> 73 + </Suspense> 74 + <div>Footer</div> 75 + </div> 76 + ) 77 + } 78 + 79 + function DataDisplay({ dataPromise }: { dataPromise: Promise<Data> }) { 80 + const data = use(dataPromise) // Unwraps the promise 81 + return <div>{data.content}</div> 82 + } 83 + 84 + function DataSummary({ dataPromise }: { dataPromise: Promise<Data> }) { 85 + const data = use(dataPromise) // Reuses the same promise 86 + return <div>{data.summary}</div> 87 + } 88 + ``` 89 + 90 + Both components share the same promise, so only one fetch occurs. Layout renders immediately while both components wait together. 91 + 92 + **When NOT to use this pattern:** 93 + 94 + - Critical data needed for layout decisions (affects positioning) 95 + - SEO-critical content above the fold 96 + - Small, fast queries where suspense overhead isn't worth it 97 + - When you want to avoid layout shift (loading → content jump) 98 + 99 + **Trade-off:** Faster initial paint vs potential layout shift. Choose based on your UX priorities.
+59
.claude/skills/react-best-practices/rules/bundle-barrel-imports.md
··· 1 + --- 2 + title: Avoid Barrel File Imports 3 + impact: CRITICAL 4 + impactDescription: 200-800ms import cost, slow builds 5 + tags: bundle, imports, tree-shaking, barrel-files, performance 6 + --- 7 + 8 + ## Avoid Barrel File Imports 9 + 10 + Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`). 11 + 12 + Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts. 13 + 14 + **Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph. 15 + 16 + **Incorrect (imports entire library):** 17 + 18 + ```tsx 19 + import { Check, X, Menu } from 'lucide-react' 20 + // Loads 1,583 modules, takes ~2.8s extra in dev 21 + // Runtime cost: 200-800ms on every cold start 22 + 23 + import { Button, TextField } from '@mui/material' 24 + // Loads 2,225 modules, takes ~4.2s extra in dev 25 + ``` 26 + 27 + **Correct (imports only what you need):** 28 + 29 + ```tsx 30 + import Check from 'lucide-react/dist/esm/icons/check' 31 + import X from 'lucide-react/dist/esm/icons/x' 32 + import Menu from 'lucide-react/dist/esm/icons/menu' 33 + // Loads only 3 modules (~2KB vs ~1MB) 34 + 35 + import Button from '@mui/material/Button' 36 + import TextField from '@mui/material/TextField' 37 + // Loads only what you use 38 + ``` 39 + 40 + **Alternative (Next.js 13.5+):** 41 + 42 + ```js 43 + // next.config.js - use optimizePackageImports 44 + module.exports = { 45 + experimental: { 46 + optimizePackageImports: ['lucide-react', '@mui/material'] 47 + } 48 + } 49 + 50 + // Then you can keep the ergonomic barrel imports: 51 + import { Check, X, Menu } from 'lucide-react' 52 + // Automatically transformed to direct imports at build time 53 + ``` 54 + 55 + Direct imports provide 15-70% faster dev boot, 28% faster builds, 40% faster cold starts, and significantly faster HMR. 56 + 57 + Libraries commonly affected: `lucide-react`, `@mui/material`, `@mui/icons-material`, `@tabler/icons-react`, `react-icons`, `@headlessui/react`, `@radix-ui/react-*`, `lodash`, `ramda`, `date-fns`, `rxjs`, `react-use`. 58 + 59 + Reference: [How we optimized package imports in Next.js](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)
+31
.claude/skills/react-best-practices/rules/bundle-conditional.md
··· 1 + --- 2 + title: Conditional Module Loading 3 + impact: HIGH 4 + impactDescription: loads large data only when needed 5 + tags: bundle, conditional-loading, lazy-loading 6 + --- 7 + 8 + ## Conditional Module Loading 9 + 10 + Load large data or modules only when a feature is activated. 11 + 12 + **Example (lazy-load animation frames):** 13 + 14 + ```tsx 15 + function AnimationPlayer({ enabled, setEnabled }: { enabled: boolean; setEnabled: React.Dispatch<React.SetStateAction<boolean>> }) { 16 + const [frames, setFrames] = useState<Frame[] | null>(null) 17 + 18 + useEffect(() => { 19 + if (enabled && !frames && typeof window !== 'undefined') { 20 + import('./animation-frames.js') 21 + .then(mod => setFrames(mod.frames)) 22 + .catch(() => setEnabled(false)) 23 + } 24 + }, [enabled, frames, setEnabled]) 25 + 26 + if (!frames) return <Skeleton /> 27 + return <Canvas frames={frames} /> 28 + } 29 + ``` 30 + 31 + The `typeof window !== 'undefined'` check prevents bundling this module for SSR, optimizing server bundle size and build speed.
+49
.claude/skills/react-best-practices/rules/bundle-defer-third-party.md
··· 1 + --- 2 + title: Defer Non-Critical Third-Party Libraries 3 + impact: MEDIUM 4 + impactDescription: loads after hydration 5 + tags: bundle, third-party, analytics, defer 6 + --- 7 + 8 + ## Defer Non-Critical Third-Party Libraries 9 + 10 + Analytics, logging, and error tracking don't block user interaction. Load them after hydration. 11 + 12 + **Incorrect (blocks initial bundle):** 13 + 14 + ```tsx 15 + import { Analytics } from '@vercel/analytics/react' 16 + 17 + export default function RootLayout({ children }) { 18 + return ( 19 + <html> 20 + <body> 21 + {children} 22 + <Analytics /> 23 + </body> 24 + </html> 25 + ) 26 + } 27 + ``` 28 + 29 + **Correct (loads after hydration):** 30 + 31 + ```tsx 32 + import dynamic from 'next/dynamic' 33 + 34 + const Analytics = dynamic( 35 + () => import('@vercel/analytics/react').then(m => m.Analytics), 36 + { ssr: false } 37 + ) 38 + 39 + export default function RootLayout({ children }) { 40 + return ( 41 + <html> 42 + <body> 43 + {children} 44 + <Analytics /> 45 + </body> 46 + </html> 47 + ) 48 + } 49 + ```
+35
.claude/skills/react-best-practices/rules/bundle-dynamic-imports.md
··· 1 + --- 2 + title: Dynamic Imports for Heavy Components 3 + impact: CRITICAL 4 + impactDescription: directly affects TTI and LCP 5 + tags: bundle, dynamic-import, code-splitting, next-dynamic 6 + --- 7 + 8 + ## Dynamic Imports for Heavy Components 9 + 10 + Use `next/dynamic` to lazy-load large components not needed on initial render. 11 + 12 + **Incorrect (Monaco bundles with main chunk ~300KB):** 13 + 14 + ```tsx 15 + import { MonacoEditor } from './monaco-editor' 16 + 17 + function CodePanel({ code }: { code: string }) { 18 + return <MonacoEditor value={code} /> 19 + } 20 + ``` 21 + 22 + **Correct (Monaco loads on demand):** 23 + 24 + ```tsx 25 + import dynamic from 'next/dynamic' 26 + 27 + const MonacoEditor = dynamic( 28 + () => import('./monaco-editor').then(m => m.MonacoEditor), 29 + { ssr: false } 30 + ) 31 + 32 + function CodePanel({ code }: { code: string }) { 33 + return <MonacoEditor value={code} /> 34 + } 35 + ```
+50
.claude/skills/react-best-practices/rules/bundle-preload.md
··· 1 + --- 2 + title: Preload Based on User Intent 3 + impact: MEDIUM 4 + impactDescription: reduces perceived latency 5 + tags: bundle, preload, user-intent, hover 6 + --- 7 + 8 + ## Preload Based on User Intent 9 + 10 + Preload heavy bundles before they're needed to reduce perceived latency. 11 + 12 + **Example (preload on hover/focus):** 13 + 14 + ```tsx 15 + function EditorButton({ onClick }: { onClick: () => void }) { 16 + const preload = () => { 17 + if (typeof window !== 'undefined') { 18 + void import('./monaco-editor') 19 + } 20 + } 21 + 22 + return ( 23 + <button 24 + onMouseEnter={preload} 25 + onFocus={preload} 26 + onClick={onClick} 27 + > 28 + Open Editor 29 + </button> 30 + ) 31 + } 32 + ``` 33 + 34 + **Example (preload when feature flag is enabled):** 35 + 36 + ```tsx 37 + function FlagsProvider({ children, flags }: Props) { 38 + useEffect(() => { 39 + if (flags.editorEnabled && typeof window !== 'undefined') { 40 + void import('./monaco-editor').then(mod => mod.init()) 41 + } 42 + }, [flags.editorEnabled]) 43 + 44 + return <FlagsContext.Provider value={flags}> 45 + {children} 46 + </FlagsContext.Provider> 47 + } 48 + ``` 49 + 50 + The `typeof window !== 'undefined'` check prevents bundling preloaded modules for SSR, optimizing server bundle size and build speed.
+74
.claude/skills/react-best-practices/rules/client-event-listeners.md
··· 1 + --- 2 + title: Deduplicate Global Event Listeners 3 + impact: LOW 4 + impactDescription: single listener for N components 5 + tags: client, swr, event-listeners, subscription 6 + --- 7 + 8 + ## Deduplicate Global Event Listeners 9 + 10 + Use `useSWRSubscription()` to share global event listeners across component instances. 11 + 12 + **Incorrect (N instances = N listeners):** 13 + 14 + ```tsx 15 + function useKeyboardShortcut(key: string, callback: () => void) { 16 + useEffect(() => { 17 + const handler = (e: KeyboardEvent) => { 18 + if (e.metaKey && e.key === key) { 19 + callback() 20 + } 21 + } 22 + window.addEventListener('keydown', handler) 23 + return () => window.removeEventListener('keydown', handler) 24 + }, [key, callback]) 25 + } 26 + ``` 27 + 28 + When using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener. 29 + 30 + **Correct (N instances = 1 listener):** 31 + 32 + ```tsx 33 + import useSWRSubscription from 'swr/subscription' 34 + 35 + // Module-level Map to track callbacks per key 36 + const keyCallbacks = new Map<string, Set<() => void>>() 37 + 38 + function useKeyboardShortcut(key: string, callback: () => void) { 39 + // Register this callback in the Map 40 + useEffect(() => { 41 + if (!keyCallbacks.has(key)) { 42 + keyCallbacks.set(key, new Set()) 43 + } 44 + keyCallbacks.get(key)!.add(callback) 45 + 46 + return () => { 47 + const set = keyCallbacks.get(key) 48 + if (set) { 49 + set.delete(callback) 50 + if (set.size === 0) { 51 + keyCallbacks.delete(key) 52 + } 53 + } 54 + } 55 + }, [key, callback]) 56 + 57 + useSWRSubscription('global-keydown', () => { 58 + const handler = (e: KeyboardEvent) => { 59 + if (e.metaKey && keyCallbacks.has(e.key)) { 60 + keyCallbacks.get(e.key)!.forEach(cb => cb()) 61 + } 62 + } 63 + window.addEventListener('keydown', handler) 64 + return () => window.removeEventListener('keydown', handler) 65 + }) 66 + } 67 + 68 + function Profile() { 69 + // Multiple shortcuts will share the same listener 70 + useKeyboardShortcut('p', () => { /* ... */ }) 71 + useKeyboardShortcut('k', () => { /* ... */ }) 72 + // ... 73 + } 74 + ```
+71
.claude/skills/react-best-practices/rules/client-localstorage-schema.md
··· 1 + --- 2 + title: Version and Minimize localStorage Data 3 + impact: MEDIUM 4 + impactDescription: prevents schema conflicts, reduces storage size 5 + tags: client, localStorage, storage, versioning, data-minimization 6 + --- 7 + 8 + ## Version and Minimize localStorage Data 9 + 10 + Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data. 11 + 12 + **Incorrect:** 13 + 14 + ```typescript 15 + // No version, stores everything, no error handling 16 + localStorage.setItem('userConfig', JSON.stringify(fullUserObject)) 17 + const data = localStorage.getItem('userConfig') 18 + ``` 19 + 20 + **Correct:** 21 + 22 + ```typescript 23 + const VERSION = 'v2' 24 + 25 + function saveConfig(config: { theme: string; language: string }) { 26 + try { 27 + localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config)) 28 + } catch { 29 + // Throws in incognito/private browsing, quota exceeded, or disabled 30 + } 31 + } 32 + 33 + function loadConfig() { 34 + try { 35 + const data = localStorage.getItem(`userConfig:${VERSION}`) 36 + return data ? JSON.parse(data) : null 37 + } catch { 38 + return null 39 + } 40 + } 41 + 42 + // Migration from v1 to v2 43 + function migrate() { 44 + try { 45 + const v1 = localStorage.getItem('userConfig:v1') 46 + if (v1) { 47 + const old = JSON.parse(v1) 48 + saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang }) 49 + localStorage.removeItem('userConfig:v1') 50 + } 51 + } catch {} 52 + } 53 + ``` 54 + 55 + **Store minimal fields from server responses:** 56 + 57 + ```typescript 58 + // User object has 20+ fields, only store what UI needs 59 + function cachePrefs(user: FullUser) { 60 + try { 61 + localStorage.setItem('prefs:v1', JSON.stringify({ 62 + theme: user.preferences.theme, 63 + notifications: user.preferences.notifications 64 + })) 65 + } catch {} 66 + } 67 + ``` 68 + 69 + **Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled. 70 + 71 + **Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.
+48
.claude/skills/react-best-practices/rules/client-passive-event-listeners.md
··· 1 + --- 2 + title: Use Passive Event Listeners for Scrolling Performance 3 + impact: MEDIUM 4 + impactDescription: eliminates scroll delay caused by event listeners 5 + tags: client, event-listeners, scrolling, performance, touch, wheel 6 + --- 7 + 8 + ## Use Passive Event Listeners for Scrolling Performance 9 + 10 + Add `{ passive: true }` to touch and wheel event listeners to enable immediate scrolling. Browsers normally wait for listeners to finish to check if `preventDefault()` is called, causing scroll delay. 11 + 12 + **Incorrect:** 13 + 14 + ```typescript 15 + useEffect(() => { 16 + const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX) 17 + const handleWheel = (e: WheelEvent) => console.log(e.deltaY) 18 + 19 + document.addEventListener('touchstart', handleTouch) 20 + document.addEventListener('wheel', handleWheel) 21 + 22 + return () => { 23 + document.removeEventListener('touchstart', handleTouch) 24 + document.removeEventListener('wheel', handleWheel) 25 + } 26 + }, []) 27 + ``` 28 + 29 + **Correct:** 30 + 31 + ```typescript 32 + useEffect(() => { 33 + const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX) 34 + const handleWheel = (e: WheelEvent) => console.log(e.deltaY) 35 + 36 + document.addEventListener('touchstart', handleTouch, { passive: true }) 37 + document.addEventListener('wheel', handleWheel, { passive: true }) 38 + 39 + return () => { 40 + document.removeEventListener('touchstart', handleTouch) 41 + document.removeEventListener('wheel', handleWheel) 42 + } 43 + }, []) 44 + ``` 45 + 46 + **Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`. 47 + 48 + **Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.
+56
.claude/skills/react-best-practices/rules/client-swr-dedup.md
··· 1 + --- 2 + title: Use SWR for Automatic Deduplication 3 + impact: MEDIUM-HIGH 4 + impactDescription: automatic deduplication 5 + tags: client, swr, deduplication, data-fetching 6 + --- 7 + 8 + ## Use SWR for Automatic Deduplication 9 + 10 + SWR enables request deduplication, caching, and revalidation across component instances. 11 + 12 + **Incorrect (no deduplication, each instance fetches):** 13 + 14 + ```tsx 15 + function UserList() { 16 + const [users, setUsers] = useState([]) 17 + useEffect(() => { 18 + fetch('/api/users') 19 + .then(r => r.json()) 20 + .then(setUsers) 21 + }, []) 22 + } 23 + ``` 24 + 25 + **Correct (multiple instances share one request):** 26 + 27 + ```tsx 28 + import useSWR from 'swr' 29 + 30 + function UserList() { 31 + const { data: users } = useSWR('/api/users', fetcher) 32 + } 33 + ``` 34 + 35 + **For immutable data:** 36 + 37 + ```tsx 38 + import { useImmutableSWR } from '@/lib/swr' 39 + 40 + function StaticContent() { 41 + const { data } = useImmutableSWR('/api/config', fetcher) 42 + } 43 + ``` 44 + 45 + **For mutations:** 46 + 47 + ```tsx 48 + import { useSWRMutation } from 'swr/mutation' 49 + 50 + function UpdateButton() { 51 + const { trigger } = useSWRMutation('/api/user', updateUser) 52 + return <button onClick={() => trigger()}>Update</button> 53 + } 54 + ``` 55 + 56 + Reference: [https://swr.vercel.app](https://swr.vercel.app)
+107
.claude/skills/react-best-practices/rules/js-batch-dom-css.md
··· 1 + --- 2 + title: Avoid Layout Thrashing 3 + impact: MEDIUM 4 + impactDescription: prevents forced synchronous layouts and reduces performance bottlenecks 5 + tags: javascript, dom, css, performance, reflow, layout-thrashing 6 + --- 7 + 8 + ## Avoid Layout Thrashing 9 + 10 + Avoid interleaving style writes with layout reads. When you read a layout property (like `offsetWidth`, `getBoundingClientRect()`, or `getComputedStyle()`) between style changes, the browser is forced to trigger a synchronous reflow. 11 + 12 + **This is OK (browser batches style changes):** 13 + ```typescript 14 + function updateElementStyles(element: HTMLElement) { 15 + // Each line invalidates style, but browser batches the recalculation 16 + element.style.width = '100px' 17 + element.style.height = '200px' 18 + element.style.backgroundColor = 'blue' 19 + element.style.border = '1px solid black' 20 + } 21 + ``` 22 + 23 + **Incorrect (interleaved reads and writes force reflows):** 24 + ```typescript 25 + function layoutThrashing(element: HTMLElement) { 26 + element.style.width = '100px' 27 + const width = element.offsetWidth // Forces reflow 28 + element.style.height = '200px' 29 + const height = element.offsetHeight // Forces another reflow 30 + } 31 + ``` 32 + 33 + **Correct (batch writes, then read once):** 34 + ```typescript 35 + function updateElementStyles(element: HTMLElement) { 36 + // Batch all writes together 37 + element.style.width = '100px' 38 + element.style.height = '200px' 39 + element.style.backgroundColor = 'blue' 40 + element.style.border = '1px solid black' 41 + 42 + // Read after all writes are done (single reflow) 43 + const { width, height } = element.getBoundingClientRect() 44 + } 45 + ``` 46 + 47 + **Correct (batch reads, then writes):** 48 + ```typescript 49 + function avoidThrashing(element: HTMLElement) { 50 + // Read phase - all layout queries first 51 + const rect1 = element.getBoundingClientRect() 52 + const offsetWidth = element.offsetWidth 53 + const offsetHeight = element.offsetHeight 54 + 55 + // Write phase - all style changes after 56 + element.style.width = '100px' 57 + element.style.height = '200px' 58 + } 59 + ``` 60 + 61 + **Better: use CSS classes** 62 + ```css 63 + .highlighted-box { 64 + width: 100px; 65 + height: 200px; 66 + background-color: blue; 67 + border: 1px solid black; 68 + } 69 + ``` 70 + ```typescript 71 + function updateElementStyles(element: HTMLElement) { 72 + element.classList.add('highlighted-box') 73 + 74 + const { width, height } = element.getBoundingClientRect() 75 + } 76 + ``` 77 + 78 + **React example:** 79 + ```tsx 80 + // Incorrect: interleaving style changes with layout queries 81 + function Box({ isHighlighted }: { isHighlighted: boolean }) { 82 + const ref = useRef<HTMLDivElement>(null) 83 + 84 + useEffect(() => { 85 + if (ref.current && isHighlighted) { 86 + ref.current.style.width = '100px' 87 + const width = ref.current.offsetWidth // Forces layout 88 + ref.current.style.height = '200px' 89 + } 90 + }, [isHighlighted]) 91 + 92 + return <div ref={ref}>Content</div> 93 + } 94 + 95 + // Correct: toggle class 96 + function Box({ isHighlighted }: { isHighlighted: boolean }) { 97 + return ( 98 + <div className={isHighlighted ? 'highlighted-box' : ''}> 99 + Content 100 + </div> 101 + ) 102 + } 103 + ``` 104 + 105 + Prefer CSS classes over inline styles when possible. CSS files are cached by the browser, and classes provide better separation of concerns and are easier to maintain. 106 + 107 + See [this gist](https://gist.github.com/paulirish/5d52fb081b3570c81e3a) and [CSS Triggers](https://csstriggers.com/) for more information on layout-forcing operations.
+80
.claude/skills/react-best-practices/rules/js-cache-function-results.md
··· 1 + --- 2 + title: Cache Repeated Function Calls 3 + impact: MEDIUM 4 + impactDescription: avoid redundant computation 5 + tags: javascript, cache, memoization, performance 6 + --- 7 + 8 + ## Cache Repeated Function Calls 9 + 10 + Use a module-level Map to cache function results when the same function is called repeatedly with the same inputs during render. 11 + 12 + **Incorrect (redundant computation):** 13 + 14 + ```typescript 15 + function ProjectList({ projects }: { projects: Project[] }) { 16 + return ( 17 + <div> 18 + {projects.map(project => { 19 + // slugify() called 100+ times for same project names 20 + const slug = slugify(project.name) 21 + 22 + return <ProjectCard key={project.id} slug={slug} /> 23 + })} 24 + </div> 25 + ) 26 + } 27 + ``` 28 + 29 + **Correct (cached results):** 30 + 31 + ```typescript 32 + // Module-level cache 33 + const slugifyCache = new Map<string, string>() 34 + 35 + function cachedSlugify(text: string): string { 36 + if (slugifyCache.has(text)) { 37 + return slugifyCache.get(text)! 38 + } 39 + const result = slugify(text) 40 + slugifyCache.set(text, result) 41 + return result 42 + } 43 + 44 + function ProjectList({ projects }: { projects: Project[] }) { 45 + return ( 46 + <div> 47 + {projects.map(project => { 48 + // Computed only once per unique project name 49 + const slug = cachedSlugify(project.name) 50 + 51 + return <ProjectCard key={project.id} slug={slug} /> 52 + })} 53 + </div> 54 + ) 55 + } 56 + ``` 57 + 58 + **Simpler pattern for single-value functions:** 59 + 60 + ```typescript 61 + let isLoggedInCache: boolean | null = null 62 + 63 + function isLoggedIn(): boolean { 64 + if (isLoggedInCache !== null) { 65 + return isLoggedInCache 66 + } 67 + 68 + isLoggedInCache = document.cookie.includes('auth=') 69 + return isLoggedInCache 70 + } 71 + 72 + // Clear cache when auth changes 73 + function onAuthChange() { 74 + isLoggedInCache = null 75 + } 76 + ``` 77 + 78 + Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components. 79 + 80 + Reference: [How we made the Vercel Dashboard twice as fast](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)
+28
.claude/skills/react-best-practices/rules/js-cache-property-access.md
··· 1 + --- 2 + title: Cache Property Access in Loops 3 + impact: LOW-MEDIUM 4 + impactDescription: reduces lookups 5 + tags: javascript, loops, optimization, caching 6 + --- 7 + 8 + ## Cache Property Access in Loops 9 + 10 + Cache object property lookups in hot paths. 11 + 12 + **Incorrect (3 lookups × N iterations):** 13 + 14 + ```typescript 15 + for (let i = 0; i < arr.length; i++) { 16 + process(obj.config.settings.value) 17 + } 18 + ``` 19 + 20 + **Correct (1 lookup total):** 21 + 22 + ```typescript 23 + const value = obj.config.settings.value 24 + const len = arr.length 25 + for (let i = 0; i < len; i++) { 26 + process(value) 27 + } 28 + ```
+70
.claude/skills/react-best-practices/rules/js-cache-storage.md
··· 1 + --- 2 + title: Cache Storage API Calls 3 + impact: LOW-MEDIUM 4 + impactDescription: reduces expensive I/O 5 + tags: javascript, localStorage, storage, caching, performance 6 + --- 7 + 8 + ## Cache Storage API Calls 9 + 10 + `localStorage`, `sessionStorage`, and `document.cookie` are synchronous and expensive. Cache reads in memory. 11 + 12 + **Incorrect (reads storage on every call):** 13 + 14 + ```typescript 15 + function getTheme() { 16 + return localStorage.getItem('theme') ?? 'light' 17 + } 18 + // Called 10 times = 10 storage reads 19 + ``` 20 + 21 + **Correct (Map cache):** 22 + 23 + ```typescript 24 + const storageCache = new Map<string, string | null>() 25 + 26 + function getLocalStorage(key: string) { 27 + if (!storageCache.has(key)) { 28 + storageCache.set(key, localStorage.getItem(key)) 29 + } 30 + return storageCache.get(key) 31 + } 32 + 33 + function setLocalStorage(key: string, value: string) { 34 + localStorage.setItem(key, value) 35 + storageCache.set(key, value) // keep cache in sync 36 + } 37 + ``` 38 + 39 + Use a Map (not a hook) so it works everywhere: utilities, event handlers, not just React components. 40 + 41 + **Cookie caching:** 42 + 43 + ```typescript 44 + let cookieCache: Record<string, string> | null = null 45 + 46 + function getCookie(name: string) { 47 + if (!cookieCache) { 48 + cookieCache = Object.fromEntries( 49 + document.cookie.split('; ').map(c => c.split('=')) 50 + ) 51 + } 52 + return cookieCache[name] 53 + } 54 + ``` 55 + 56 + **Important (invalidate on external changes):** 57 + 58 + If storage can change externally (another tab, server-set cookies), invalidate cache: 59 + 60 + ```typescript 61 + window.addEventListener('storage', (e) => { 62 + if (e.key) storageCache.delete(e.key) 63 + }) 64 + 65 + document.addEventListener('visibilitychange', () => { 66 + if (document.visibilityState === 'visible') { 67 + storageCache.clear() 68 + } 69 + }) 70 + ```
+32
.claude/skills/react-best-practices/rules/js-combine-iterations.md
··· 1 + --- 2 + title: Combine Multiple Array Iterations 3 + impact: LOW-MEDIUM 4 + impactDescription: reduces iterations 5 + tags: javascript, arrays, loops, performance 6 + --- 7 + 8 + ## Combine Multiple Array Iterations 9 + 10 + Multiple `.filter()` or `.map()` calls iterate the array multiple times. Combine into one loop. 11 + 12 + **Incorrect (3 iterations):** 13 + 14 + ```typescript 15 + const admins = users.filter(u => u.isAdmin) 16 + const testers = users.filter(u => u.isTester) 17 + const inactive = users.filter(u => !u.isActive) 18 + ``` 19 + 20 + **Correct (1 iteration):** 21 + 22 + ```typescript 23 + const admins: User[] = [] 24 + const testers: User[] = [] 25 + const inactive: User[] = [] 26 + 27 + for (const user of users) { 28 + if (user.isAdmin) admins.push(user) 29 + if (user.isTester) testers.push(user) 30 + if (!user.isActive) inactive.push(user) 31 + } 32 + ```
+50
.claude/skills/react-best-practices/rules/js-early-exit.md
··· 1 + --- 2 + title: Early Return from Functions 3 + impact: LOW-MEDIUM 4 + impactDescription: avoids unnecessary computation 5 + tags: javascript, functions, optimization, early-return 6 + --- 7 + 8 + ## Early Return from Functions 9 + 10 + Return early when result is determined to skip unnecessary processing. 11 + 12 + **Incorrect (processes all items even after finding answer):** 13 + 14 + ```typescript 15 + function validateUsers(users: User[]) { 16 + let hasError = false 17 + let errorMessage = '' 18 + 19 + for (const user of users) { 20 + if (!user.email) { 21 + hasError = true 22 + errorMessage = 'Email required' 23 + } 24 + if (!user.name) { 25 + hasError = true 26 + errorMessage = 'Name required' 27 + } 28 + // Continues checking all users even after error found 29 + } 30 + 31 + return hasError ? { valid: false, error: errorMessage } : { valid: true } 32 + } 33 + ``` 34 + 35 + **Correct (returns immediately on first error):** 36 + 37 + ```typescript 38 + function validateUsers(users: User[]) { 39 + for (const user of users) { 40 + if (!user.email) { 41 + return { valid: false, error: 'Email required' } 42 + } 43 + if (!user.name) { 44 + return { valid: false, error: 'Name required' } 45 + } 46 + } 47 + 48 + return { valid: true } 49 + } 50 + ```
+45
.claude/skills/react-best-practices/rules/js-hoist-regexp.md
··· 1 + --- 2 + title: Hoist RegExp Creation 3 + impact: LOW-MEDIUM 4 + impactDescription: avoids recreation 5 + tags: javascript, regexp, optimization, memoization 6 + --- 7 + 8 + ## Hoist RegExp Creation 9 + 10 + Don't create RegExp inside render. Hoist to module scope or memoize with `useMemo()`. 11 + 12 + **Incorrect (new RegExp every render):** 13 + 14 + ```tsx 15 + function Highlighter({ text, query }: Props) { 16 + const regex = new RegExp(`(${query})`, 'gi') 17 + const parts = text.split(regex) 18 + return <>{parts.map((part, i) => ...)}</> 19 + } 20 + ``` 21 + 22 + **Correct (memoize or hoist):** 23 + 24 + ```tsx 25 + const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ 26 + 27 + function Highlighter({ text, query }: Props) { 28 + const regex = useMemo( 29 + () => new RegExp(`(${escapeRegex(query)})`, 'gi'), 30 + [query] 31 + ) 32 + const parts = text.split(regex) 33 + return <>{parts.map((part, i) => ...)}</> 34 + } 35 + ``` 36 + 37 + **Warning (global regex has mutable state):** 38 + 39 + Global regex (`/g`) has mutable `lastIndex` state: 40 + 41 + ```typescript 42 + const regex = /foo/g 43 + regex.test('foo') // true, lastIndex = 3 44 + regex.test('foo') // false, lastIndex = 0 45 + ```
+37
.claude/skills/react-best-practices/rules/js-index-maps.md
··· 1 + --- 2 + title: Build Index Maps for Repeated Lookups 3 + impact: LOW-MEDIUM 4 + impactDescription: 1M ops to 2K ops 5 + tags: javascript, map, indexing, optimization, performance 6 + --- 7 + 8 + ## Build Index Maps for Repeated Lookups 9 + 10 + Multiple `.find()` calls by the same key should use a Map. 11 + 12 + **Incorrect (O(n) per lookup):** 13 + 14 + ```typescript 15 + function processOrders(orders: Order[], users: User[]) { 16 + return orders.map(order => ({ 17 + ...order, 18 + user: users.find(u => u.id === order.userId) 19 + })) 20 + } 21 + ``` 22 + 23 + **Correct (O(1) per lookup):** 24 + 25 + ```typescript 26 + function processOrders(orders: Order[], users: User[]) { 27 + const userById = new Map(users.map(u => [u.id, u])) 28 + 29 + return orders.map(order => ({ 30 + ...order, 31 + user: userById.get(order.userId) 32 + })) 33 + } 34 + ``` 35 + 36 + Build map once (O(n)), then all lookups are O(1). 37 + For 1000 orders × 1000 users: 1M ops → 2K ops.
+49
.claude/skills/react-best-practices/rules/js-length-check-first.md
··· 1 + --- 2 + title: Early Length Check for Array Comparisons 3 + impact: MEDIUM-HIGH 4 + impactDescription: avoids expensive operations when lengths differ 5 + tags: javascript, arrays, performance, optimization, comparison 6 + --- 7 + 8 + ## Early Length Check for Array Comparisons 9 + 10 + When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal. 11 + 12 + In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops). 13 + 14 + **Incorrect (always runs expensive comparison):** 15 + 16 + ```typescript 17 + function hasChanges(current: string[], original: string[]) { 18 + // Always sorts and joins, even when lengths differ 19 + return current.sort().join() !== original.sort().join() 20 + } 21 + ``` 22 + 23 + Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings. 24 + 25 + **Correct (O(1) length check first):** 26 + 27 + ```typescript 28 + function hasChanges(current: string[], original: string[]) { 29 + // Early return if lengths differ 30 + if (current.length !== original.length) { 31 + return true 32 + } 33 + // Only sort when lengths match 34 + const currentSorted = current.toSorted() 35 + const originalSorted = original.toSorted() 36 + for (let i = 0; i < currentSorted.length; i++) { 37 + if (currentSorted[i] !== originalSorted[i]) { 38 + return true 39 + } 40 + } 41 + return false 42 + } 43 + ``` 44 + 45 + This new approach is more efficient because: 46 + - It avoids the overhead of sorting and joining the arrays when lengths differ 47 + - It avoids consuming memory for the joined strings (especially important for large arrays) 48 + - It avoids mutating the original arrays 49 + - It returns early when a difference is found
+82
.claude/skills/react-best-practices/rules/js-min-max-loop.md
··· 1 + --- 2 + title: Use Loop for Min/Max Instead of Sort 3 + impact: LOW 4 + impactDescription: O(n) instead of O(n log n) 5 + tags: javascript, arrays, performance, sorting, algorithms 6 + --- 7 + 8 + ## Use Loop for Min/Max Instead of Sort 9 + 10 + Finding the smallest or largest element only requires a single pass through the array. Sorting is wasteful and slower. 11 + 12 + **Incorrect (O(n log n) - sort to find latest):** 13 + 14 + ```typescript 15 + interface Project { 16 + id: string 17 + name: string 18 + updatedAt: number 19 + } 20 + 21 + function getLatestProject(projects: Project[]) { 22 + const sorted = [...projects].sort((a, b) => b.updatedAt - a.updatedAt) 23 + return sorted[0] 24 + } 25 + ``` 26 + 27 + Sorts the entire array just to find the maximum value. 28 + 29 + **Incorrect (O(n log n) - sort for oldest and newest):** 30 + 31 + ```typescript 32 + function getOldestAndNewest(projects: Project[]) { 33 + const sorted = [...projects].sort((a, b) => a.updatedAt - b.updatedAt) 34 + return { oldest: sorted[0], newest: sorted[sorted.length - 1] } 35 + } 36 + ``` 37 + 38 + Still sorts unnecessarily when only min/max are needed. 39 + 40 + **Correct (O(n) - single loop):** 41 + 42 + ```typescript 43 + function getLatestProject(projects: Project[]) { 44 + if (projects.length === 0) return null 45 + 46 + let latest = projects[0] 47 + 48 + for (let i = 1; i < projects.length; i++) { 49 + if (projects[i].updatedAt > latest.updatedAt) { 50 + latest = projects[i] 51 + } 52 + } 53 + 54 + return latest 55 + } 56 + 57 + function getOldestAndNewest(projects: Project[]) { 58 + if (projects.length === 0) return { oldest: null, newest: null } 59 + 60 + let oldest = projects[0] 61 + let newest = projects[0] 62 + 63 + for (let i = 1; i < projects.length; i++) { 64 + if (projects[i].updatedAt < oldest.updatedAt) oldest = projects[i] 65 + if (projects[i].updatedAt > newest.updatedAt) newest = projects[i] 66 + } 67 + 68 + return { oldest, newest } 69 + } 70 + ``` 71 + 72 + Single pass through the array, no copying, no sorting. 73 + 74 + **Alternative (Math.min/Math.max for small arrays):** 75 + 76 + ```typescript 77 + const numbers = [5, 2, 8, 1, 9] 78 + const min = Math.min(...numbers) 79 + const max = Math.max(...numbers) 80 + ``` 81 + 82 + This works for small arrays, but can be slower or just throw an error for very large arrays due to spread operator limitations. Maximal array length is approximately 124000 in Chrome 143 and 638000 in Safari 18; exact numbers may vary - see [the fiddle](https://jsfiddle.net/qw1jabsx/4/). Use the loop approach for reliability.
+24
.claude/skills/react-best-practices/rules/js-set-map-lookups.md
··· 1 + --- 2 + title: Use Set/Map for O(1) Lookups 3 + impact: LOW-MEDIUM 4 + impactDescription: O(n) to O(1) 5 + tags: javascript, set, map, data-structures, performance 6 + --- 7 + 8 + ## Use Set/Map for O(1) Lookups 9 + 10 + Convert arrays to Set/Map for repeated membership checks. 11 + 12 + **Incorrect (O(n) per check):** 13 + 14 + ```typescript 15 + const allowedIds = ['a', 'b', 'c', ...] 16 + items.filter(item => allowedIds.includes(item.id)) 17 + ``` 18 + 19 + **Correct (O(1) per check):** 20 + 21 + ```typescript 22 + const allowedIds = new Set(['a', 'b', 'c', ...]) 23 + items.filter(item => allowedIds.has(item.id)) 24 + ```
+57
.claude/skills/react-best-practices/rules/js-tosorted-immutable.md
··· 1 + --- 2 + title: Use toSorted() Instead of sort() for Immutability 3 + impact: MEDIUM-HIGH 4 + impactDescription: prevents mutation bugs in React state 5 + tags: javascript, arrays, immutability, react, state, mutation 6 + --- 7 + 8 + ## Use toSorted() Instead of sort() for Immutability 9 + 10 + `.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation. 11 + 12 + **Incorrect (mutates original array):** 13 + 14 + ```typescript 15 + function UserList({ users }: { users: User[] }) { 16 + // Mutates the users prop array! 17 + const sorted = useMemo( 18 + () => users.sort((a, b) => a.name.localeCompare(b.name)), 19 + [users] 20 + ) 21 + return <div>{sorted.map(renderUser)}</div> 22 + } 23 + ``` 24 + 25 + **Correct (creates new array):** 26 + 27 + ```typescript 28 + function UserList({ users }: { users: User[] }) { 29 + // Creates new sorted array, original unchanged 30 + const sorted = useMemo( 31 + () => users.toSorted((a, b) => a.name.localeCompare(b.name)), 32 + [users] 33 + ) 34 + return <div>{sorted.map(renderUser)}</div> 35 + } 36 + ``` 37 + 38 + **Why this matters in React:** 39 + 40 + 1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only 41 + 2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior 42 + 43 + **Browser support (fallback for older browsers):** 44 + 45 + `.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator: 46 + 47 + ```typescript 48 + // Fallback for older browsers 49 + const sorted = [...items].sort((a, b) => a.value - b.value) 50 + ``` 51 + 52 + **Other immutable array methods:** 53 + 54 + - `.toSorted()` - immutable sort 55 + - `.toReversed()` - immutable reverse 56 + - `.toSpliced()` - immutable splice 57 + - `.with()` - immutable element replacement
+26
.claude/skills/react-best-practices/rules/rendering-activity.md
··· 1 + --- 2 + title: Use Activity Component for Show/Hide 3 + impact: MEDIUM 4 + impactDescription: preserves state/DOM 5 + tags: rendering, activity, visibility, state-preservation 6 + --- 7 + 8 + ## Use Activity Component for Show/Hide 9 + 10 + Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility. 11 + 12 + **Usage:** 13 + 14 + ```tsx 15 + import { Activity } from 'react' 16 + 17 + function Dropdown({ isOpen }: Props) { 18 + return ( 19 + <Activity mode={isOpen ? 'visible' : 'hidden'}> 20 + <ExpensiveMenu /> 21 + </Activity> 22 + ) 23 + } 24 + ``` 25 + 26 + Avoids expensive re-renders and state loss.
+47
.claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md
··· 1 + --- 2 + title: Animate SVG Wrapper Instead of SVG Element 3 + impact: LOW 4 + impactDescription: enables hardware acceleration 5 + tags: rendering, svg, css, animation, performance 6 + --- 7 + 8 + ## Animate SVG Wrapper Instead of SVG Element 9 + 10 + Many browsers don't have hardware acceleration for CSS3 animations on SVG elements. Wrap SVG in a `<div>` and animate the wrapper instead. 11 + 12 + **Incorrect (animating SVG directly - no hardware acceleration):** 13 + 14 + ```tsx 15 + function LoadingSpinner() { 16 + return ( 17 + <svg 18 + className="animate-spin" 19 + width="24" 20 + height="24" 21 + viewBox="0 0 24 24" 22 + > 23 + <circle cx="12" cy="12" r="10" stroke="currentColor" /> 24 + </svg> 25 + ) 26 + } 27 + ``` 28 + 29 + **Correct (animating wrapper div - hardware accelerated):** 30 + 31 + ```tsx 32 + function LoadingSpinner() { 33 + return ( 34 + <div className="animate-spin"> 35 + <svg 36 + width="24" 37 + height="24" 38 + viewBox="0 0 24 24" 39 + > 40 + <circle cx="12" cy="12" r="10" stroke="currentColor" /> 41 + </svg> 42 + </div> 43 + ) 44 + } 45 + ``` 46 + 47 + This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations.
+40
.claude/skills/react-best-practices/rules/rendering-conditional-render.md
··· 1 + --- 2 + title: Use Explicit Conditional Rendering 3 + impact: LOW 4 + impactDescription: prevents rendering 0 or NaN 5 + tags: rendering, conditional, jsx, falsy-values 6 + --- 7 + 8 + ## Use Explicit Conditional Rendering 9 + 10 + Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render. 11 + 12 + **Incorrect (renders "0" when count is 0):** 13 + 14 + ```tsx 15 + function Badge({ count }: { count: number }) { 16 + return ( 17 + <div> 18 + {count && <span className="badge">{count}</span>} 19 + </div> 20 + ) 21 + } 22 + 23 + // When count = 0, renders: <div>0</div> 24 + // When count = 5, renders: <div><span class="badge">5</span></div> 25 + ``` 26 + 27 + **Correct (renders nothing when count is 0):** 28 + 29 + ```tsx 30 + function Badge({ count }: { count: number }) { 31 + return ( 32 + <div> 33 + {count > 0 ? <span className="badge">{count}</span> : null} 34 + </div> 35 + ) 36 + } 37 + 38 + // When count = 0, renders: <div></div> 39 + // When count = 5, renders: <div><span class="badge">5</span></div> 40 + ```
+38
.claude/skills/react-best-practices/rules/rendering-content-visibility.md
··· 1 + --- 2 + title: CSS content-visibility for Long Lists 3 + impact: HIGH 4 + impactDescription: faster initial render 5 + tags: rendering, css, content-visibility, long-lists 6 + --- 7 + 8 + ## CSS content-visibility for Long Lists 9 + 10 + Apply `content-visibility: auto` to defer off-screen rendering. 11 + 12 + **CSS:** 13 + 14 + ```css 15 + .message-item { 16 + content-visibility: auto; 17 + contain-intrinsic-size: 0 80px; 18 + } 19 + ``` 20 + 21 + **Example:** 22 + 23 + ```tsx 24 + function MessageList({ messages }: { messages: Message[] }) { 25 + return ( 26 + <div className="overflow-y-auto h-screen"> 27 + {messages.map(msg => ( 28 + <div key={msg.id} className="message-item"> 29 + <Avatar user={msg.author} /> 30 + <div>{msg.content}</div> 31 + </div> 32 + ))} 33 + </div> 34 + ) 35 + } 36 + ``` 37 + 38 + For 1000 messages, browser skips layout/paint for ~990 off-screen items (10× faster initial render).
+46
.claude/skills/react-best-practices/rules/rendering-hoist-jsx.md
··· 1 + --- 2 + title: Hoist Static JSX Elements 3 + impact: LOW 4 + impactDescription: avoids re-creation 5 + tags: rendering, jsx, static, optimization 6 + --- 7 + 8 + ## Hoist Static JSX Elements 9 + 10 + Extract static JSX outside components to avoid re-creation. 11 + 12 + **Incorrect (recreates element every render):** 13 + 14 + ```tsx 15 + function LoadingSkeleton() { 16 + return <div className="animate-pulse h-20 bg-gray-200" /> 17 + } 18 + 19 + function Container() { 20 + return ( 21 + <div> 22 + {loading && <LoadingSkeleton />} 23 + </div> 24 + ) 25 + } 26 + ``` 27 + 28 + **Correct (reuses same element):** 29 + 30 + ```tsx 31 + const loadingSkeleton = ( 32 + <div className="animate-pulse h-20 bg-gray-200" /> 33 + ) 34 + 35 + function Container() { 36 + return ( 37 + <div> 38 + {loading && loadingSkeleton} 39 + </div> 40 + ) 41 + } 42 + ``` 43 + 44 + This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render. 45 + 46 + **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary.
+82
.claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md
··· 1 + --- 2 + title: Prevent Hydration Mismatch Without Flickering 3 + impact: MEDIUM 4 + impactDescription: avoids visual flicker and hydration errors 5 + tags: rendering, ssr, hydration, localStorage, flicker 6 + --- 7 + 8 + ## Prevent Hydration Mismatch Without Flickering 9 + 10 + When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates. 11 + 12 + **Incorrect (breaks SSR):** 13 + 14 + ```tsx 15 + function ThemeWrapper({ children }: { children: ReactNode }) { 16 + // localStorage is not available on server - throws error 17 + const theme = localStorage.getItem('theme') || 'light' 18 + 19 + return ( 20 + <div className={theme}> 21 + {children} 22 + </div> 23 + ) 24 + } 25 + ``` 26 + 27 + Server-side rendering will fail because `localStorage` is undefined. 28 + 29 + **Incorrect (visual flickering):** 30 + 31 + ```tsx 32 + function ThemeWrapper({ children }: { children: ReactNode }) { 33 + const [theme, setTheme] = useState('light') 34 + 35 + useEffect(() => { 36 + // Runs after hydration - causes visible flash 37 + const stored = localStorage.getItem('theme') 38 + if (stored) { 39 + setTheme(stored) 40 + } 41 + }, []) 42 + 43 + return ( 44 + <div className={theme}> 45 + {children} 46 + </div> 47 + ) 48 + } 49 + ``` 50 + 51 + Component first renders with default value (`light`), then updates after hydration, causing a visible flash of incorrect content. 52 + 53 + **Correct (no flicker, no hydration mismatch):** 54 + 55 + ```tsx 56 + function ThemeWrapper({ children }: { children: ReactNode }) { 57 + return ( 58 + <> 59 + <div id="theme-wrapper"> 60 + {children} 61 + </div> 62 + <script 63 + dangerouslySetInnerHTML={{ 64 + __html: ` 65 + (function() { 66 + try { 67 + var theme = localStorage.getItem('theme') || 'light'; 68 + var el = document.getElementById('theme-wrapper'); 69 + if (el) el.className = theme; 70 + } catch (e) {} 71 + })(); 72 + `, 73 + }} 74 + /> 75 + </> 76 + ) 77 + } 78 + ``` 79 + 80 + The inline script executes synchronously before showing the element, ensuring the DOM already has the correct value. No flickering, no hydration mismatch. 81 + 82 + This pattern is especially useful for theme toggles, user preferences, authentication states, and any client-only data that should render immediately without flashing default values.
+30
.claude/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md
··· 1 + --- 2 + title: Suppress Expected Hydration Mismatches 3 + impact: LOW-MEDIUM 4 + impactDescription: avoids noisy hydration warnings for known differences 5 + tags: rendering, hydration, ssr, nextjs 6 + --- 7 + 8 + ## Suppress Expected Hydration Mismatches 9 + 10 + In SSR frameworks (e.g., Next.js), some values are intentionally different on server vs client (random IDs, dates, locale/timezone formatting). For these *expected* mismatches, wrap the dynamic text in an element with `suppressHydrationWarning` to prevent noisy warnings. Do not use this to hide real bugs. Don’t overuse it. 11 + 12 + **Incorrect (known mismatch warnings):** 13 + 14 + ```tsx 15 + function Timestamp() { 16 + return <span>{new Date().toLocaleString()}</span> 17 + } 18 + ``` 19 + 20 + **Correct (suppress expected mismatch only):** 21 + 22 + ```tsx 23 + function Timestamp() { 24 + return ( 25 + <span suppressHydrationWarning> 26 + {new Date().toLocaleString()} 27 + </span> 28 + ) 29 + } 30 + ```
+28
.claude/skills/react-best-practices/rules/rendering-svg-precision.md
··· 1 + --- 2 + title: Optimize SVG Precision 3 + impact: LOW 4 + impactDescription: reduces file size 5 + tags: rendering, svg, optimization, svgo 6 + --- 7 + 8 + ## Optimize SVG Precision 9 + 10 + Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered. 11 + 12 + **Incorrect (excessive precision):** 13 + 14 + ```svg 15 + <path d="M 10.293847 20.847362 L 30.938472 40.192837" /> 16 + ``` 17 + 18 + **Correct (1 decimal place):** 19 + 20 + ```svg 21 + <path d="M 10.3 20.8 L 30.9 40.2" /> 22 + ``` 23 + 24 + **Automate with SVGO:** 25 + 26 + ```bash 27 + npx svgo --precision=1 --multipass icon.svg 28 + ```
+75
.claude/skills/react-best-practices/rules/rendering-usetransition-loading.md
··· 1 + --- 2 + title: Use useTransition Over Manual Loading States 3 + impact: LOW 4 + impactDescription: reduces re-renders and improves code clarity 5 + tags: rendering, transitions, useTransition, loading, state 6 + --- 7 + 8 + ## Use useTransition Over Manual Loading States 9 + 10 + Use `useTransition` instead of manual `useState` for loading states. This provides built-in `isPending` state and automatically manages transitions. 11 + 12 + **Incorrect (manual loading state):** 13 + 14 + ```tsx 15 + function SearchResults() { 16 + const [query, setQuery] = useState('') 17 + const [results, setResults] = useState([]) 18 + const [isLoading, setIsLoading] = useState(false) 19 + 20 + const handleSearch = async (value: string) => { 21 + setIsLoading(true) 22 + setQuery(value) 23 + const data = await fetchResults(value) 24 + setResults(data) 25 + setIsLoading(false) 26 + } 27 + 28 + return ( 29 + <> 30 + <input onChange={(e) => handleSearch(e.target.value)} /> 31 + {isLoading && <Spinner />} 32 + <ResultsList results={results} /> 33 + </> 34 + ) 35 + } 36 + ``` 37 + 38 + **Correct (useTransition with built-in pending state):** 39 + 40 + ```tsx 41 + import { useTransition, useState } from 'react' 42 + 43 + function SearchResults() { 44 + const [query, setQuery] = useState('') 45 + const [results, setResults] = useState([]) 46 + const [isPending, startTransition] = useTransition() 47 + 48 + const handleSearch = (value: string) => { 49 + setQuery(value) // Update input immediately 50 + 51 + startTransition(async () => { 52 + // Fetch and update results 53 + const data = await fetchResults(value) 54 + setResults(data) 55 + }) 56 + } 57 + 58 + return ( 59 + <> 60 + <input onChange={(e) => handleSearch(e.target.value)} /> 61 + {isPending && <Spinner />} 62 + <ResultsList results={results} /> 63 + </> 64 + ) 65 + } 66 + ``` 67 + 68 + **Benefits:** 69 + 70 + - **Automatic pending state**: No need to manually manage `setIsLoading(true/false)` 71 + - **Error resilience**: Pending state correctly resets even if the transition throws 72 + - **Better responsiveness**: Keeps the UI responsive during updates 73 + - **Interrupt handling**: New transitions automatically cancel pending ones 74 + 75 + Reference: [useTransition](https://react.dev/reference/react/useTransition)
+39
.claude/skills/react-best-practices/rules/rerender-defer-reads.md
··· 1 + --- 2 + title: Defer State Reads to Usage Point 3 + impact: MEDIUM 4 + impactDescription: avoids unnecessary subscriptions 5 + tags: rerender, searchParams, localStorage, optimization 6 + --- 7 + 8 + ## Defer State Reads to Usage Point 9 + 10 + Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks. 11 + 12 + **Incorrect (subscribes to all searchParams changes):** 13 + 14 + ```tsx 15 + function ShareButton({ chatId }: { chatId: string }) { 16 + const searchParams = useSearchParams() 17 + 18 + const handleShare = () => { 19 + const ref = searchParams.get('ref') 20 + shareChat(chatId, { ref }) 21 + } 22 + 23 + return <button onClick={handleShare}>Share</button> 24 + } 25 + ``` 26 + 27 + **Correct (reads on demand, no subscription):** 28 + 29 + ```tsx 30 + function ShareButton({ chatId }: { chatId: string }) { 31 + const handleShare = () => { 32 + const params = new URLSearchParams(window.location.search) 33 + const ref = params.get('ref') 34 + shareChat(chatId, { ref }) 35 + } 36 + 37 + return <button onClick={handleShare}>Share</button> 38 + } 39 + ```
+45
.claude/skills/react-best-practices/rules/rerender-dependencies.md
··· 1 + --- 2 + title: Narrow Effect Dependencies 3 + impact: LOW 4 + impactDescription: minimizes effect re-runs 5 + tags: rerender, useEffect, dependencies, optimization 6 + --- 7 + 8 + ## Narrow Effect Dependencies 9 + 10 + Specify primitive dependencies instead of objects to minimize effect re-runs. 11 + 12 + **Incorrect (re-runs on any user field change):** 13 + 14 + ```tsx 15 + useEffect(() => { 16 + console.log(user.id) 17 + }, [user]) 18 + ``` 19 + 20 + **Correct (re-runs only when id changes):** 21 + 22 + ```tsx 23 + useEffect(() => { 24 + console.log(user.id) 25 + }, [user.id]) 26 + ``` 27 + 28 + **For derived state, compute outside effect:** 29 + 30 + ```tsx 31 + // Incorrect: runs on width=767, 766, 765... 32 + useEffect(() => { 33 + if (width < 768) { 34 + enableMobileMode() 35 + } 36 + }, [width]) 37 + 38 + // Correct: runs only on boolean transition 39 + const isMobile = width < 768 40 + useEffect(() => { 41 + if (isMobile) { 42 + enableMobileMode() 43 + } 44 + }, [isMobile]) 45 + ```
+40
.claude/skills/react-best-practices/rules/rerender-derived-state-no-effect.md
··· 1 + --- 2 + title: Calculate Derived State During Rendering 3 + impact: MEDIUM 4 + impactDescription: avoids redundant renders and state drift 5 + tags: rerender, derived-state, useEffect, state 6 + --- 7 + 8 + ## Calculate Derived State During Rendering 9 + 10 + If a value can be computed from current props/state, do not store it in state or update it in an effect. Derive it during render to avoid extra renders and state drift. Do not set state in effects solely in response to prop changes; prefer derived values or keyed resets instead. 11 + 12 + **Incorrect (redundant state and effect):** 13 + 14 + ```tsx 15 + function Form() { 16 + const [firstName, setFirstName] = useState('First') 17 + const [lastName, setLastName] = useState('Last') 18 + const [fullName, setFullName] = useState('') 19 + 20 + useEffect(() => { 21 + setFullName(firstName + ' ' + lastName) 22 + }, [firstName, lastName]) 23 + 24 + return <p>{fullName}</p> 25 + } 26 + ``` 27 + 28 + **Correct (derive during render):** 29 + 30 + ```tsx 31 + function Form() { 32 + const [firstName, setFirstName] = useState('First') 33 + const [lastName, setLastName] = useState('Last') 34 + const fullName = firstName + ' ' + lastName 35 + 36 + return <p>{fullName}</p> 37 + } 38 + ``` 39 + 40 + References: [You Might Not Need an Effect](https://react.dev/learn/you-might-not-need-an-effect)
+29
.claude/skills/react-best-practices/rules/rerender-derived-state.md
··· 1 + --- 2 + title: Subscribe to Derived State 3 + impact: MEDIUM 4 + impactDescription: reduces re-render frequency 5 + tags: rerender, derived-state, media-query, optimization 6 + --- 7 + 8 + ## Subscribe to Derived State 9 + 10 + Subscribe to derived boolean state instead of continuous values to reduce re-render frequency. 11 + 12 + **Incorrect (re-renders on every pixel change):** 13 + 14 + ```tsx 15 + function Sidebar() { 16 + const width = useWindowWidth() // updates continuously 17 + const isMobile = width < 768 18 + return <nav className={isMobile ? 'mobile' : 'desktop'} /> 19 + } 20 + ``` 21 + 22 + **Correct (re-renders only when boolean changes):** 23 + 24 + ```tsx 25 + function Sidebar() { 26 + const isMobile = useMediaQuery('(max-width: 767px)') 27 + return <nav className={isMobile ? 'mobile' : 'desktop'} /> 28 + } 29 + ```
+74
.claude/skills/react-best-practices/rules/rerender-functional-setstate.md
··· 1 + --- 2 + title: Use Functional setState Updates 3 + impact: MEDIUM 4 + impactDescription: prevents stale closures and unnecessary callback recreations 5 + tags: react, hooks, useState, useCallback, callbacks, closures 6 + --- 7 + 8 + ## Use Functional setState Updates 9 + 10 + When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references. 11 + 12 + **Incorrect (requires state as dependency):** 13 + 14 + ```tsx 15 + function TodoList() { 16 + const [items, setItems] = useState(initialItems) 17 + 18 + // Callback must depend on items, recreated on every items change 19 + const addItems = useCallback((newItems: Item[]) => { 20 + setItems([...items, ...newItems]) 21 + }, [items]) // ❌ items dependency causes recreations 22 + 23 + // Risk of stale closure if dependency is forgotten 24 + const removeItem = useCallback((id: string) => { 25 + setItems(items.filter(item => item.id !== id)) 26 + }, []) // ❌ Missing items dependency - will use stale items! 27 + 28 + return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} /> 29 + } 30 + ``` 31 + 32 + The first callback is recreated every time `items` changes, which can cause child components to re-render unnecessarily. The second callback has a stale closure bug—it will always reference the initial `items` value. 33 + 34 + **Correct (stable callbacks, no stale closures):** 35 + 36 + ```tsx 37 + function TodoList() { 38 + const [items, setItems] = useState(initialItems) 39 + 40 + // Stable callback, never recreated 41 + const addItems = useCallback((newItems: Item[]) => { 42 + setItems(curr => [...curr, ...newItems]) 43 + }, []) // ✅ No dependencies needed 44 + 45 + // Always uses latest state, no stale closure risk 46 + const removeItem = useCallback((id: string) => { 47 + setItems(curr => curr.filter(item => item.id !== id)) 48 + }, []) // ✅ Safe and stable 49 + 50 + return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} /> 51 + } 52 + ``` 53 + 54 + **Benefits:** 55 + 56 + 1. **Stable callback references** - Callbacks don't need to be recreated when state changes 57 + 2. **No stale closures** - Always operates on the latest state value 58 + 3. **Fewer dependencies** - Simplifies dependency arrays and reduces memory leaks 59 + 4. **Prevents bugs** - Eliminates the most common source of React closure bugs 60 + 61 + **When to use functional updates:** 62 + 63 + - Any setState that depends on the current state value 64 + - Inside useCallback/useMemo when state is needed 65 + - Event handlers that reference state 66 + - Async operations that update state 67 + 68 + **When direct updates are fine:** 69 + 70 + - Setting state to a static value: `setCount(0)` 71 + - Setting state from props/arguments only: `setName(newName)` 72 + - State doesn't depend on previous value 73 + 74 + **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler can automatically optimize some cases, but functional updates are still recommended for correctness and to prevent stale closure bugs.
+58
.claude/skills/react-best-practices/rules/rerender-lazy-state-init.md
··· 1 + --- 2 + title: Use Lazy State Initialization 3 + impact: MEDIUM 4 + impactDescription: wasted computation on every render 5 + tags: react, hooks, useState, performance, initialization 6 + --- 7 + 8 + ## Use Lazy State Initialization 9 + 10 + Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once. 11 + 12 + **Incorrect (runs on every render):** 13 + 14 + ```tsx 15 + function FilteredList({ items }: { items: Item[] }) { 16 + // buildSearchIndex() runs on EVERY render, even after initialization 17 + const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items)) 18 + const [query, setQuery] = useState('') 19 + 20 + // When query changes, buildSearchIndex runs again unnecessarily 21 + return <SearchResults index={searchIndex} query={query} /> 22 + } 23 + 24 + function UserProfile() { 25 + // JSON.parse runs on every render 26 + const [settings, setSettings] = useState( 27 + JSON.parse(localStorage.getItem('settings') || '{}') 28 + ) 29 + 30 + return <SettingsForm settings={settings} onChange={setSettings} /> 31 + } 32 + ``` 33 + 34 + **Correct (runs only once):** 35 + 36 + ```tsx 37 + function FilteredList({ items }: { items: Item[] }) { 38 + // buildSearchIndex() runs ONLY on initial render 39 + const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items)) 40 + const [query, setQuery] = useState('') 41 + 42 + return <SearchResults index={searchIndex} query={query} /> 43 + } 44 + 45 + function UserProfile() { 46 + // JSON.parse runs only on initial render 47 + const [settings, setSettings] = useState(() => { 48 + const stored = localStorage.getItem('settings') 49 + return stored ? JSON.parse(stored) : {} 50 + }) 51 + 52 + return <SettingsForm settings={settings} onChange={setSettings} /> 53 + } 54 + ``` 55 + 56 + Use lazy initialization when computing initial values from localStorage/sessionStorage, building data structures (indexes, maps), reading from the DOM, or performing heavy transformations. 57 + 58 + For simple primitives (`useState(0)`), direct references (`useState(props.value)`), or cheap literals (`useState({})`), the function form is unnecessary.
+38
.claude/skills/react-best-practices/rules/rerender-memo-with-default-value.md
··· 1 + --- 2 + 3 + title: Extract Default Non-primitive Parameter Value from Memoized Component to Constant 4 + impact: MEDIUM 5 + impactDescription: restores memoization by using a constant for default value 6 + tags: rerender, memo, optimization 7 + 8 + --- 9 + 10 + ## Extract Default Non-primitive Parameter Value from Memoized Component to Constant 11 + 12 + When memoized component has a default value for some non-primitive optional parameter, such as an array, function, or object, calling the component without that parameter results in broken memoization. This is because new value instances are created on every rerender, and they do not pass strict equality comparison in `memo()`. 13 + 14 + To address this issue, extract the default value into a constant. 15 + 16 + **Incorrect (`onClick` has different values on every rerender):** 17 + 18 + ```tsx 19 + const UserAvatar = memo(function UserAvatar({ onClick = () => {} }: { onClick?: () => void }) { 20 + // ... 21 + }) 22 + 23 + // Used without optional onClick 24 + <UserAvatar /> 25 + ``` 26 + 27 + **Correct (stable default value):** 28 + 29 + ```tsx 30 + const NOOP = () => {}; 31 + 32 + const UserAvatar = memo(function UserAvatar({ onClick = NOOP }: { onClick?: () => void }) { 33 + // ... 34 + }) 35 + 36 + // Used without optional onClick 37 + <UserAvatar /> 38 + ```
+44
.claude/skills/react-best-practices/rules/rerender-memo.md
··· 1 + --- 2 + title: Extract to Memoized Components 3 + impact: MEDIUM 4 + impactDescription: enables early returns 5 + tags: rerender, memo, useMemo, optimization 6 + --- 7 + 8 + ## Extract to Memoized Components 9 + 10 + Extract expensive work into memoized components to enable early returns before computation. 11 + 12 + **Incorrect (computes avatar even when loading):** 13 + 14 + ```tsx 15 + function Profile({ user, loading }: Props) { 16 + const avatar = useMemo(() => { 17 + const id = computeAvatarId(user) 18 + return <Avatar id={id} /> 19 + }, [user]) 20 + 21 + if (loading) return <Skeleton /> 22 + return <div>{avatar}</div> 23 + } 24 + ``` 25 + 26 + **Correct (skips computation when loading):** 27 + 28 + ```tsx 29 + const UserAvatar = memo(function UserAvatar({ user }: { user: User }) { 30 + const id = useMemo(() => computeAvatarId(user), [user]) 31 + return <Avatar id={id} /> 32 + }) 33 + 34 + function Profile({ user, loading }: Props) { 35 + if (loading) return <Skeleton /> 36 + return ( 37 + <div> 38 + <UserAvatar user={user} /> 39 + </div> 40 + ) 41 + } 42 + ``` 43 + 44 + **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, manual memoization with `memo()` and `useMemo()` is not necessary. The compiler automatically optimizes re-renders.
+45
.claude/skills/react-best-practices/rules/rerender-move-effect-to-event.md
··· 1 + --- 2 + title: Put Interaction Logic in Event Handlers 3 + impact: MEDIUM 4 + impactDescription: avoids effect re-runs and duplicate side effects 5 + tags: rerender, useEffect, events, side-effects, dependencies 6 + --- 7 + 8 + ## Put Interaction Logic in Event Handlers 9 + 10 + If a side effect is triggered by a specific user action (submit, click, drag), run it in that event handler. Do not model the action as state + effect; it makes effects re-run on unrelated changes and can duplicate the action. 11 + 12 + **Incorrect (event modeled as state + effect):** 13 + 14 + ```tsx 15 + function Form() { 16 + const [submitted, setSubmitted] = useState(false) 17 + const theme = useContext(ThemeContext) 18 + 19 + useEffect(() => { 20 + if (submitted) { 21 + post('/api/register') 22 + showToast('Registered', theme) 23 + } 24 + }, [submitted, theme]) 25 + 26 + return <button onClick={() => setSubmitted(true)}>Submit</button> 27 + } 28 + ``` 29 + 30 + **Correct (do it in the handler):** 31 + 32 + ```tsx 33 + function Form() { 34 + const theme = useContext(ThemeContext) 35 + 36 + function handleSubmit() { 37 + post('/api/register') 38 + showToast('Registered', theme) 39 + } 40 + 41 + return <button onClick={handleSubmit}>Submit</button> 42 + } 43 + ``` 44 + 45 + Reference: [Should this code move to an event handler?](https://react.dev/learn/removing-effect-dependencies#should-this-code-move-to-an-event-handler)
+35
.claude/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md
··· 1 + --- 2 + title: Do not wrap a simple expression with a primitive result type in useMemo 3 + impact: LOW-MEDIUM 4 + impactDescription: wasted computation on every render 5 + tags: rerender, useMemo, optimization 6 + --- 7 + 8 + ## Do not wrap a simple expression with a primitive result type in useMemo 9 + 10 + When an expression is simple (few logical or arithmetical operators) and has a primitive result type (boolean, number, string), do not wrap it in `useMemo`. 11 + Calling `useMemo` and comparing hook dependencies may consume more resources than the expression itself. 12 + 13 + **Incorrect:** 14 + 15 + ```tsx 16 + function Header({ user, notifications }: Props) { 17 + const isLoading = useMemo(() => { 18 + return user.isLoading || notifications.isLoading 19 + }, [user.isLoading, notifications.isLoading]) 20 + 21 + if (isLoading) return <Skeleton /> 22 + // return some markup 23 + } 24 + ``` 25 + 26 + **Correct:** 27 + 28 + ```tsx 29 + function Header({ user, notifications }: Props) { 30 + const isLoading = user.isLoading || notifications.isLoading 31 + 32 + if (isLoading) return <Skeleton /> 33 + // return some markup 34 + } 35 + ```
+40
.claude/skills/react-best-practices/rules/rerender-transitions.md
··· 1 + --- 2 + title: Use Transitions for Non-Urgent Updates 3 + impact: MEDIUM 4 + impactDescription: maintains UI responsiveness 5 + tags: rerender, transitions, startTransition, performance 6 + --- 7 + 8 + ## Use Transitions for Non-Urgent Updates 9 + 10 + Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness. 11 + 12 + **Incorrect (blocks UI on every scroll):** 13 + 14 + ```tsx 15 + function ScrollTracker() { 16 + const [scrollY, setScrollY] = useState(0) 17 + useEffect(() => { 18 + const handler = () => setScrollY(window.scrollY) 19 + window.addEventListener('scroll', handler, { passive: true }) 20 + return () => window.removeEventListener('scroll', handler) 21 + }, []) 22 + } 23 + ``` 24 + 25 + **Correct (non-blocking updates):** 26 + 27 + ```tsx 28 + import { startTransition } from 'react' 29 + 30 + function ScrollTracker() { 31 + const [scrollY, setScrollY] = useState(0) 32 + useEffect(() => { 33 + const handler = () => { 34 + startTransition(() => setScrollY(window.scrollY)) 35 + } 36 + window.addEventListener('scroll', handler, { passive: true }) 37 + return () => window.removeEventListener('scroll', handler) 38 + }, []) 39 + } 40 + ```
+73
.claude/skills/react-best-practices/rules/rerender-use-ref-transient-values.md
··· 1 + --- 2 + title: Use useRef for Transient Values 3 + impact: MEDIUM 4 + impactDescription: avoids unnecessary re-renders on frequent updates 5 + tags: rerender, useref, state, performance 6 + --- 7 + 8 + ## Use useRef for Transient Values 9 + 10 + When a value changes frequently and you don't want a re-render on every update (e.g., mouse trackers, intervals, transient flags), store it in `useRef` instead of `useState`. Keep component state for UI; use refs for temporary DOM-adjacent values. Updating a ref does not trigger a re-render. 11 + 12 + **Incorrect (renders every update):** 13 + 14 + ```tsx 15 + function Tracker() { 16 + const [lastX, setLastX] = useState(0) 17 + 18 + useEffect(() => { 19 + const onMove = (e: MouseEvent) => setLastX(e.clientX) 20 + window.addEventListener('mousemove', onMove) 21 + return () => window.removeEventListener('mousemove', onMove) 22 + }, []) 23 + 24 + return ( 25 + <div 26 + style={{ 27 + position: 'fixed', 28 + top: 0, 29 + left: lastX, 30 + width: 8, 31 + height: 8, 32 + background: 'black', 33 + }} 34 + /> 35 + ) 36 + } 37 + ``` 38 + 39 + **Correct (no re-render for tracking):** 40 + 41 + ```tsx 42 + function Tracker() { 43 + const lastXRef = useRef(0) 44 + const dotRef = useRef<HTMLDivElement>(null) 45 + 46 + useEffect(() => { 47 + const onMove = (e: MouseEvent) => { 48 + lastXRef.current = e.clientX 49 + const node = dotRef.current 50 + if (node) { 51 + node.style.transform = `translateX(${e.clientX}px)` 52 + } 53 + } 54 + window.addEventListener('mousemove', onMove) 55 + return () => window.removeEventListener('mousemove', onMove) 56 + }, []) 57 + 58 + return ( 59 + <div 60 + ref={dotRef} 61 + style={{ 62 + position: 'fixed', 63 + top: 0, 64 + left: 0, 65 + width: 8, 66 + height: 8, 67 + background: 'black', 68 + transform: 'translateX(0px)', 69 + }} 70 + /> 71 + ) 72 + } 73 + ```
+73
.claude/skills/react-best-practices/rules/server-after-nonblocking.md
··· 1 + --- 2 + title: Use after() for Non-Blocking Operations 3 + impact: MEDIUM 4 + impactDescription: faster response times 5 + tags: server, async, logging, analytics, side-effects 6 + --- 7 + 8 + ## Use after() for Non-Blocking Operations 9 + 10 + Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response. 11 + 12 + **Incorrect (blocks response):** 13 + 14 + ```tsx 15 + import { logUserAction } from '@/app/utils' 16 + 17 + export async function POST(request: Request) { 18 + // Perform mutation 19 + await updateDatabase(request) 20 + 21 + // Logging blocks the response 22 + const userAgent = request.headers.get('user-agent') || 'unknown' 23 + await logUserAction({ userAgent }) 24 + 25 + return new Response(JSON.stringify({ status: 'success' }), { 26 + status: 200, 27 + headers: { 'Content-Type': 'application/json' } 28 + }) 29 + } 30 + ``` 31 + 32 + **Correct (non-blocking):** 33 + 34 + ```tsx 35 + import { after } from 'next/server' 36 + import { headers, cookies } from 'next/headers' 37 + import { logUserAction } from '@/app/utils' 38 + 39 + export async function POST(request: Request) { 40 + // Perform mutation 41 + await updateDatabase(request) 42 + 43 + // Log after response is sent 44 + after(async () => { 45 + const userAgent = (await headers()).get('user-agent') || 'unknown' 46 + const sessionCookie = (await cookies()).get('session-id')?.value || 'anonymous' 47 + 48 + logUserAction({ sessionCookie, userAgent }) 49 + }) 50 + 51 + return new Response(JSON.stringify({ status: 'success' }), { 52 + status: 200, 53 + headers: { 'Content-Type': 'application/json' } 54 + }) 55 + } 56 + ``` 57 + 58 + The response is sent immediately while logging happens in the background. 59 + 60 + **Common use cases:** 61 + 62 + - Analytics tracking 63 + - Audit logging 64 + - Sending notifications 65 + - Cache invalidation 66 + - Cleanup tasks 67 + 68 + **Important notes:** 69 + 70 + - `after()` runs even if the response fails or redirects 71 + - Works in Server Actions, Route Handlers, and Server Components 72 + 73 + Reference: [https://nextjs.org/docs/app/api-reference/functions/after](https://nextjs.org/docs/app/api-reference/functions/after)
+96
.claude/skills/react-best-practices/rules/server-auth-actions.md
··· 1 + --- 2 + title: Authenticate Server Actions Like API Routes 3 + impact: CRITICAL 4 + impactDescription: prevents unauthorized access to server mutations 5 + tags: server, server-actions, authentication, security, authorization 6 + --- 7 + 8 + ## Authenticate Server Actions Like API Routes 9 + 10 + **Impact: CRITICAL (prevents unauthorized access to server mutations)** 11 + 12 + Server Actions (functions with `"use server"`) are exposed as public endpoints, just like API routes. Always verify authentication and authorization **inside** each Server Action—do not rely solely on middleware, layout guards, or page-level checks, as Server Actions can be invoked directly. 13 + 14 + Next.js documentation explicitly states: "Treat Server Actions with the same security considerations as public-facing API endpoints, and verify if the user is allowed to perform a mutation." 15 + 16 + **Incorrect (no authentication check):** 17 + 18 + ```typescript 19 + 'use server' 20 + 21 + export async function deleteUser(userId: string) { 22 + // Anyone can call this! No auth check 23 + await db.user.delete({ where: { id: userId } }) 24 + return { success: true } 25 + } 26 + ``` 27 + 28 + **Correct (authentication inside the action):** 29 + 30 + ```typescript 31 + 'use server' 32 + 33 + import { verifySession } from '@/lib/auth' 34 + import { unauthorized } from '@/lib/errors' 35 + 36 + export async function deleteUser(userId: string) { 37 + // Always check auth inside the action 38 + const session = await verifySession() 39 + 40 + if (!session) { 41 + throw unauthorized('Must be logged in') 42 + } 43 + 44 + // Check authorization too 45 + if (session.user.role !== 'admin' && session.user.id !== userId) { 46 + throw unauthorized('Cannot delete other users') 47 + } 48 + 49 + await db.user.delete({ where: { id: userId } }) 50 + return { success: true } 51 + } 52 + ``` 53 + 54 + **With input validation:** 55 + 56 + ```typescript 57 + 'use server' 58 + 59 + import { verifySession } from '@/lib/auth' 60 + import { z } from 'zod' 61 + 62 + const updateProfileSchema = z.object({ 63 + userId: z.string().uuid(), 64 + name: z.string().min(1).max(100), 65 + email: z.string().email() 66 + }) 67 + 68 + export async function updateProfile(data: unknown) { 69 + // Validate input first 70 + const validated = updateProfileSchema.parse(data) 71 + 72 + // Then authenticate 73 + const session = await verifySession() 74 + if (!session) { 75 + throw new Error('Unauthorized') 76 + } 77 + 78 + // Then authorize 79 + if (session.user.id !== validated.userId) { 80 + throw new Error('Can only update own profile') 81 + } 82 + 83 + // Finally perform the mutation 84 + await db.user.update({ 85 + where: { id: validated.userId }, 86 + data: { 87 + name: validated.name, 88 + email: validated.email 89 + } 90 + }) 91 + 92 + return { success: true } 93 + } 94 + ``` 95 + 96 + Reference: [https://nextjs.org/docs/app/guides/authentication](https://nextjs.org/docs/app/guides/authentication)
+41
.claude/skills/react-best-practices/rules/server-cache-lru.md
··· 1 + --- 2 + title: Cross-Request LRU Caching 3 + impact: HIGH 4 + impactDescription: caches across requests 5 + tags: server, cache, lru, cross-request 6 + --- 7 + 8 + ## Cross-Request LRU Caching 9 + 10 + `React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache. 11 + 12 + **Implementation:** 13 + 14 + ```typescript 15 + import { LRUCache } from 'lru-cache' 16 + 17 + const cache = new LRUCache<string, any>({ 18 + max: 1000, 19 + ttl: 5 * 60 * 1000 // 5 minutes 20 + }) 21 + 22 + export async function getUser(id: string) { 23 + const cached = cache.get(id) 24 + if (cached) return cached 25 + 26 + const user = await db.user.findUnique({ where: { id } }) 27 + cache.set(id, user) 28 + return user 29 + } 30 + 31 + // Request 1: DB query, result cached 32 + // Request 2: cache hit, no DB query 33 + ``` 34 + 35 + Use when sequential user actions hit multiple endpoints needing the same data within seconds. 36 + 37 + **With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis. 38 + 39 + **In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching. 40 + 41 + Reference: [https://github.com/isaacs/node-lru-cache](https://github.com/isaacs/node-lru-cache)
+76
.claude/skills/react-best-practices/rules/server-cache-react.md
··· 1 + --- 2 + title: Per-Request Deduplication with React.cache() 3 + impact: MEDIUM 4 + impactDescription: deduplicates within request 5 + tags: server, cache, react-cache, deduplication 6 + --- 7 + 8 + ## Per-Request Deduplication with React.cache() 9 + 10 + Use `React.cache()` for server-side request deduplication. Authentication and database queries benefit most. 11 + 12 + **Usage:** 13 + 14 + ```typescript 15 + import { cache } from 'react' 16 + 17 + export const getCurrentUser = cache(async () => { 18 + const session = await auth() 19 + if (!session?.user?.id) return null 20 + return await db.user.findUnique({ 21 + where: { id: session.user.id } 22 + }) 23 + }) 24 + ``` 25 + 26 + Within a single request, multiple calls to `getCurrentUser()` execute the query only once. 27 + 28 + **Avoid inline objects as arguments:** 29 + 30 + `React.cache()` uses shallow equality (`Object.is`) to determine cache hits. Inline objects create new references each call, preventing cache hits. 31 + 32 + **Incorrect (always cache miss):** 33 + 34 + ```typescript 35 + const getUser = cache(async (params: { uid: number }) => { 36 + return await db.user.findUnique({ where: { id: params.uid } }) 37 + }) 38 + 39 + // Each call creates new object, never hits cache 40 + getUser({ uid: 1 }) 41 + getUser({ uid: 1 }) // Cache miss, runs query again 42 + ``` 43 + 44 + **Correct (cache hit):** 45 + 46 + ```typescript 47 + const getUser = cache(async (uid: number) => { 48 + return await db.user.findUnique({ where: { id: uid } }) 49 + }) 50 + 51 + // Primitive args use value equality 52 + getUser(1) 53 + getUser(1) // Cache hit, returns cached result 54 + ``` 55 + 56 + If you must pass objects, pass the same reference: 57 + 58 + ```typescript 59 + const params = { uid: 1 } 60 + getUser(params) // Query runs 61 + getUser(params) // Cache hit (same reference) 62 + ``` 63 + 64 + **Next.js-Specific Note:** 65 + 66 + In Next.js, the `fetch` API is automatically extended with request memoization. Requests with the same URL and options are automatically deduplicated within a single request, so you don't need `React.cache()` for `fetch` calls. However, `React.cache()` is still essential for other async tasks: 67 + 68 + - Database queries (Prisma, Drizzle, etc.) 69 + - Heavy computations 70 + - Authentication checks 71 + - File system operations 72 + - Any non-fetch async work 73 + 74 + Use `React.cache()` to deduplicate these operations across your component tree. 75 + 76 + Reference: [React.cache documentation](https://react.dev/reference/react/cache)
+65
.claude/skills/react-best-practices/rules/server-dedup-props.md
··· 1 + --- 2 + title: Avoid Duplicate Serialization in RSC Props 3 + impact: LOW 4 + impactDescription: reduces network payload by avoiding duplicate serialization 5 + tags: server, rsc, serialization, props, client-components 6 + --- 7 + 8 + ## Avoid Duplicate Serialization in RSC Props 9 + 10 + **Impact: LOW (reduces network payload by avoiding duplicate serialization)** 11 + 12 + RSC→client serialization deduplicates by object reference, not value. Same reference = serialized once; new reference = serialized again. Do transformations (`.toSorted()`, `.filter()`, `.map()`) in client, not server. 13 + 14 + **Incorrect (duplicates array):** 15 + 16 + ```tsx 17 + // RSC: sends 6 strings (2 arrays × 3 items) 18 + <ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} /> 19 + ``` 20 + 21 + **Correct (sends 3 strings):** 22 + 23 + ```tsx 24 + // RSC: send once 25 + <ClientList usernames={usernames} /> 26 + 27 + // Client: transform there 28 + 'use client' 29 + const sorted = useMemo(() => [...usernames].sort(), [usernames]) 30 + ``` 31 + 32 + **Nested deduplication behavior:** 33 + 34 + Deduplication works recursively. Impact varies by data type: 35 + 36 + - `string[]`, `number[]`, `boolean[]`: **HIGH impact** - array + all primitives fully duplicated 37 + - `object[]`: **LOW impact** - array duplicated, but nested objects deduplicated by reference 38 + 39 + ```tsx 40 + // string[] - duplicates everything 41 + usernames={['a','b']} sorted={usernames.toSorted()} // sends 4 strings 42 + 43 + // object[] - duplicates array structure only 44 + users={[{id:1},{id:2}]} sorted={users.toSorted()} // sends 2 arrays + 2 unique objects (not 4) 45 + ``` 46 + 47 + **Operations breaking deduplication (create new references):** 48 + 49 + - Arrays: `.toSorted()`, `.filter()`, `.map()`, `.slice()`, `[...arr]` 50 + - Objects: `{...obj}`, `Object.assign()`, `structuredClone()`, `JSON.parse(JSON.stringify())` 51 + 52 + **More examples:** 53 + 54 + ```tsx 55 + // ❌ Bad 56 + <C users={users} active={users.filter(u => u.active)} /> 57 + <C product={product} productName={product.name} /> 58 + 59 + // ✅ Good 60 + <C users={users} /> 61 + <C product={product} /> 62 + // Do filtering/destructuring in client 63 + ``` 64 + 65 + **Exception:** Pass derived data when transformation is expensive or client doesn't need original.
+83
.claude/skills/react-best-practices/rules/server-parallel-fetching.md
··· 1 + --- 2 + title: Parallel Data Fetching with Component Composition 3 + impact: CRITICAL 4 + impactDescription: eliminates server-side waterfalls 5 + tags: server, rsc, parallel-fetching, composition 6 + --- 7 + 8 + ## Parallel Data Fetching with Component Composition 9 + 10 + React Server Components execute sequentially within a tree. Restructure with composition to parallelize data fetching. 11 + 12 + **Incorrect (Sidebar waits for Page's fetch to complete):** 13 + 14 + ```tsx 15 + export default async function Page() { 16 + const header = await fetchHeader() 17 + return ( 18 + <div> 19 + <div>{header}</div> 20 + <Sidebar /> 21 + </div> 22 + ) 23 + } 24 + 25 + async function Sidebar() { 26 + const items = await fetchSidebarItems() 27 + return <nav>{items.map(renderItem)}</nav> 28 + } 29 + ``` 30 + 31 + **Correct (both fetch simultaneously):** 32 + 33 + ```tsx 34 + async function Header() { 35 + const data = await fetchHeader() 36 + return <div>{data}</div> 37 + } 38 + 39 + async function Sidebar() { 40 + const items = await fetchSidebarItems() 41 + return <nav>{items.map(renderItem)}</nav> 42 + } 43 + 44 + export default function Page() { 45 + return ( 46 + <div> 47 + <Header /> 48 + <Sidebar /> 49 + </div> 50 + ) 51 + } 52 + ``` 53 + 54 + **Alternative with children prop:** 55 + 56 + ```tsx 57 + async function Header() { 58 + const data = await fetchHeader() 59 + return <div>{data}</div> 60 + } 61 + 62 + async function Sidebar() { 63 + const items = await fetchSidebarItems() 64 + return <nav>{items.map(renderItem)}</nav> 65 + } 66 + 67 + function Layout({ children }: { children: ReactNode }) { 68 + return ( 69 + <div> 70 + <Header /> 71 + {children} 72 + </div> 73 + ) 74 + } 75 + 76 + export default function Page() { 77 + return ( 78 + <Layout> 79 + <Sidebar /> 80 + </Layout> 81 + ) 82 + } 83 + ```
+38
.claude/skills/react-best-practices/rules/server-serialization.md
··· 1 + --- 2 + title: Minimize Serialization at RSC Boundaries 3 + impact: HIGH 4 + impactDescription: reduces data transfer size 5 + tags: server, rsc, serialization, props 6 + --- 7 + 8 + ## Minimize Serialization at RSC Boundaries 9 + 10 + The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses. 11 + 12 + **Incorrect (serializes all 50 fields):** 13 + 14 + ```tsx 15 + async function Page() { 16 + const user = await fetchUser() // 50 fields 17 + return <Profile user={user} /> 18 + } 19 + 20 + 'use client' 21 + function Profile({ user }: { user: User }) { 22 + return <div>{user.name}</div> // uses 1 field 23 + } 24 + ``` 25 + 26 + **Correct (serializes only 1 field):** 27 + 28 + ```tsx 29 + async function Page() { 30 + const user = await fetchUser() 31 + return <Profile name={user.name} /> 32 + } 33 + 34 + 'use client' 35 + function Profile({ name }: { name: string }) { 36 + return <div>{name}</div> 37 + } 38 + ```
+16 -24
.gitignore
··· 1 1 node_modules 2 - 3 - # Output 4 - .output 5 - .vercel 6 - .netlify 7 - .wrangler 8 - /.svelte-kit 9 - /build 2 + package-lock.json 3 + yarn.lock 10 4 11 - # OS 12 5 .DS_Store 13 - Thumbs.db 14 - 15 - # Env 6 + .cache 16 7 .env 17 - .env.* 18 - !.env.example 19 - !.env.test 20 - 21 - # Vite 22 - vite.config.js.timestamp-* 23 - vite.config.ts.timestamp-* 24 - 25 - # Cloudflare Types 26 - /worker-configuration.d.ts 8 + .vercel 9 + .output 27 10 28 - # SQLite 29 - *.db 11 + /build/ 12 + /api/ 13 + /server/build 14 + /public/build 15 + # Sentry Config File 16 + .env.sentry-build-plugin 17 + /test-results/ 18 + /playwright-report/ 19 + /blob-report/ 20 + /playwright/.cache/ 21 + .tanstack
+255 -673
bun.lock
··· 4 4 "workspaces": { 5 5 "": { 6 6 "name": "morgenblau", 7 + "dependencies": { 8 + "@base-ui-components/react": "^1.0.0-rc.0", 9 + "@tanstack/react-router": "^1.163.2", 10 + "@tanstack/react-start": "^1.163.2", 11 + "motion": "^12.34.3", 12 + "react": "^19.2.4", 13 + "react-dom": "^19.2.4", 14 + }, 7 15 "devDependencies": { 8 - "@eslint/compat": "^2.0.1", 9 - "@eslint/js": "^9.39.2", 10 - "@libsql/client": "^0.17.0", 11 - "@sveltejs/adapter-cloudflare": "^7.2.6", 12 - "@sveltejs/kit": "^2.50.1", 13 - "@sveltejs/vite-plugin-svelte": "^6.2.4", 14 16 "@tailwindcss/forms": "^0.5.11", 15 17 "@tailwindcss/typography": "^0.5.19", 16 - "@tailwindcss/vite": "^4.1.18", 17 - "@types/node": "^24", 18 - "@vitest/browser-playwright": "^4.0.18", 19 - "drizzle-kit": "^0.31.8", 20 - "drizzle-orm": "^0.45.1", 21 - "eslint": "^9.39.2", 22 - "eslint-config-prettier": "^10.1.8", 23 - "eslint-plugin-svelte": "^3.14.0", 24 - "globals": "^17.1.0", 25 - "playwright": "^1.58.0", 26 - "prettier": "^3.8.1", 27 - "prettier-plugin-svelte": "^3.4.1", 28 - "prettier-plugin-tailwindcss": "^0.7.2", 29 - "svelte": "^5.48.2", 30 - "svelte-check": "^4.3.5", 31 - "tailwindcss": "^4.1.18", 18 + "@tailwindcss/vite": "^4.2.1", 19 + "@types/node": "^25.3.0", 20 + "@types/react": "^19.2.14", 21 + "@types/react-dom": "^19.2.3", 22 + "@vitejs/plugin-react": "^5.1.4", 23 + "tailwindcss": "^4.2.1", 32 24 "typescript": "^5.9.3", 33 - "typescript-eslint": "^8.53.1", 34 25 "vite": "^7.3.1", 35 - "vite-plugin-devtools-json": "^1.0.0", 36 - "vitest": "^4.0.18", 37 - "vitest-browser-svelte": "^2.0.2", 38 - "wrangler": "^4.60.0", 26 + "vite-tsconfig-paths": "^6.1.1", 39 27 }, 40 28 }, 41 29 }, 42 30 "packages": { 43 - "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="], 44 - 45 - "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.12.0", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": "^1.20260115.0" }, "optionalPeers": ["workerd"] }, "sha512-NK4vN+2Z/GbfGS4BamtbbVk1rcu5RmqaYGiyHJQrA09AoxdZPHDF3W/EhgI0YSK8p3vRo/VNCtbSJFPON7FWMQ=="], 46 - 47 - "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260131.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-+1X4qErc715NUhJZNhtlpuCxajhD5YNre7Cz50WPMmj+BMUrh9h7fntKEadtrUo5SM2YONY7CDzK7wdWbJJBVA=="], 48 - 49 - "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260131.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-M84mXR8WEMEBuX4/dL2IQ4wHV/ALwYjx9if5ePZR8rdbD7if/fkEEoMBq0bGS/1gMLRqqCZLstabxHV+g92NNg=="], 50 - 51 - "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260131.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SWzr48bCL9y5wjkj23tXS6t/6us99EAH9T5TAscMV0hfJFZQt97RY/gaHKyRRjFv6jfJZvk7d4g+OmGeYBnwcg=="], 52 - 53 - "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260131.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-mL0kLPGIBJRPeHS3+erJ2t5dJT3ODhsKvR9aA4BcsY7M30/QhlgJIF6wsgwNisTJ23q8PbobZNHBUKIe8l/E9A=="], 54 - 55 - "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260131.0", "", { "os": "win32", "cpu": "x64" }, "sha512-hoQqTFBpP1zntP2OQSpt5dEWbd9vSBliK+G7LmDXjKitPkmkRFo2PB4P9aBRE1edPAIO/fpdoJv928k2HaAn4A=="], 56 - 57 - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260203.0", "", {}, "sha512-XD2uglpGbVppjXXLuAdalKkcTi/i4TyQSx0w/ijJbvrR1Cfm7zNkxtvFBNy3tBNxZOiFIJtw5bszifQB1eow6A=="], 58 - 59 - "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], 60 - 61 - "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], 62 - 63 - "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], 64 - 65 - "@esbuild-kit/core-utils": ["@esbuild-kit/core-utils@3.3.2", "", { "dependencies": { "esbuild": "~0.18.20", "source-map-support": "^0.5.21" } }, "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ=="], 66 - 67 - "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], 68 - 69 - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], 70 - 71 - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], 72 - 73 - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], 74 - 75 - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], 76 - 77 - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], 78 - 79 - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], 80 - 81 - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], 82 - 83 - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], 84 - 85 - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], 86 - 87 - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], 31 + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], 88 32 89 - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], 33 + "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], 90 34 91 - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], 35 + "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], 92 36 93 - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], 37 + "@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], 94 38 95 - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], 39 + "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@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" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], 96 40 97 - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], 41 + "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], 98 42 99 - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], 43 + "@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], 100 44 101 - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], 45 + "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], 102 46 103 - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], 104 - 105 - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], 47 + "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.28.6", "", {}, "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug=="], 106 48 107 - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], 108 - 109 - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], 49 + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], 110 50 111 - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], 51 + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], 112 52 113 - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], 53 + "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], 114 54 115 - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], 55 + "@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], 116 56 117 - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], 57 + "@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], 118 58 119 - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], 59 + "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="], 120 60 121 - "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], 61 + "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A=="], 122 62 123 - "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], 63 + "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], 124 64 125 - "@eslint/compat": ["@eslint/compat@2.0.2", "", { "dependencies": { "@eslint/core": "^1.1.0" }, "peerDependencies": { "eslint": "^8.40 || 9 || 10" }, "optionalPeers": ["eslint"] }, "sha512-pR1DoD0h3HfF675QZx0xsyrsU8q70Z/plx7880NOhS02NuWLgBCOMDL787nUeQ7EWLkxv3bPQJaarjcPQb2Dwg=="], 65 + "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], 126 66 127 - "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], 67 + "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], 128 68 129 - "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="], 69 + "@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], 130 70 131 - "@eslint/core": ["@eslint/core@1.1.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-/nr9K9wkr3P1EzFTdFdMoLuo1PmIxjmwvPozwoSodjNBdefGujXQUF93u1DDZpEaTuDvMsIQddsd35BwtrW9Xw=="], 71 + "@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], 132 72 133 - "@eslint/eslintrc": ["@eslint/eslintrc@3.3.3", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ=="], 73 + "@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], 134 74 135 - "@eslint/js": ["@eslint/js@9.39.2", "", {}, "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA=="], 75 + "@base-ui-components/react": ["@base-ui-components/react@1.0.0-rc.0", "", { "dependencies": { "@babel/runtime": "^7.28.4", "@base-ui-components/utils": "0.2.2", "@floating-ui/react-dom": "^2.1.6", "@floating-ui/utils": "^0.2.10", "reselect": "^5.1.1", "tabbable": "^6.3.0", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-9lhUFbJcbXvc9KulLev1WTFxS/alJRBWDH/ibKSQaNvmDwMFS2gKp1sTeeldYSfKuS/KC1w2MZutc0wHu2hRHQ=="], 136 76 137 - "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], 77 + "@base-ui-components/utils": ["@base-ui-components/utils@0.2.2", "", { "dependencies": { "@babel/runtime": "^7.28.4", "@floating-ui/utils": "^0.2.10", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-rNJCD6TFy3OSRDKVHJDzLpxO3esTV1/drRtWNUpe7rCpPN9HZVHUCuP+6rdDYDGWfXnQHbqi05xOyRP2iZAlkw=="], 138 78 139 - "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="], 79 + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], 140 80 141 - "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], 81 + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], 142 82 143 - "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], 83 + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], 144 84 145 - "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], 85 + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], 146 86 147 - "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], 87 + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], 148 88 149 - "@img/colour": ["@img/colour@1.0.0", "", {}, "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw=="], 89 + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], 150 90 151 - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], 91 + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], 152 92 153 - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], 93 + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], 154 94 155 - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], 95 + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], 156 96 157 - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], 97 + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], 158 98 159 - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], 99 + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], 160 100 161 - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], 101 + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], 162 102 163 - "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], 103 + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], 164 104 165 - "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], 105 + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], 166 106 167 - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], 107 + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], 168 108 169 - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], 109 + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], 170 110 171 - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], 111 + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], 172 112 173 - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], 113 + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], 174 114 175 - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], 115 + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], 176 116 177 - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], 117 + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], 178 118 179 - "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], 119 + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], 180 120 181 - "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], 121 + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], 182 122 183 - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], 123 + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], 184 124 185 - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], 125 + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], 186 126 187 - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], 127 + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], 188 128 189 - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], 129 + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], 190 130 191 - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], 131 + "@floating-ui/core": ["@floating-ui/core@1.7.4", "", { "dependencies": { "@floating-ui/utils": "^0.2.10" } }, "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg=="], 192 132 193 - "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], 133 + "@floating-ui/dom": ["@floating-ui/dom@1.7.5", "", { "dependencies": { "@floating-ui/core": "^1.7.4", "@floating-ui/utils": "^0.2.10" } }, "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg=="], 194 134 195 - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], 135 + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.7", "", { "dependencies": { "@floating-ui/dom": "^1.7.5" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg=="], 196 136 197 - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], 137 + "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], 198 138 199 139 "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 200 140 ··· 206 146 207 147 "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 208 148 209 - "@libsql/client": ["@libsql/client@0.17.0", "", { "dependencies": { "@libsql/core": "^0.17.0", "@libsql/hrana-client": "^0.9.0", "js-base64": "^3.7.5", "libsql": "^0.5.22", "promise-limit": "^2.7.0" } }, "sha512-TLjSU9Otdpq0SpKHl1tD1Nc9MKhrsZbCFGot3EbCxRa8m1E5R1mMwoOjKMMM31IyF7fr+hPNHLpYfwbMKNusmg=="], 210 - 211 - "@libsql/core": ["@libsql/core@0.17.0", "", { "dependencies": { "js-base64": "^3.7.5" } }, "sha512-hnZRnJHiS+nrhHKLGYPoJbc78FE903MSDrFJTbftxo+e52X+E0Y0fHOCVYsKWcg6XgB7BbJYUrz/xEkVTSaipw=="], 212 - 213 - "@libsql/darwin-arm64": ["@libsql/darwin-arm64@0.5.22", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4B8ZlX3nIDPndfct7GNe0nI3Yw6ibocEicWdC4fvQbSs/jdq/RC2oCsoJxJ4NzXkvktX70C1J4FcmmoBy069UA=="], 214 - 215 - "@libsql/darwin-x64": ["@libsql/darwin-x64@0.5.22", "", { "os": "darwin", "cpu": "x64" }, "sha512-ny2HYWt6lFSIdNFzUFIJ04uiW6finXfMNJ7wypkAD8Pqdm6nAByO+Fdqu8t7sD0sqJGeUCiOg480icjyQ2/8VA=="], 216 - 217 - "@libsql/hrana-client": ["@libsql/hrana-client@0.9.0", "", { "dependencies": { "@libsql/isomorphic-ws": "^0.1.5", "cross-fetch": "^4.0.0", "js-base64": "^3.7.5", "node-fetch": "^3.3.2" } }, "sha512-pxQ1986AuWfPX4oXzBvLwBnfgKDE5OMhAdR/5cZmRaB4Ygz5MecQybvwZupnRz341r2CtFmbk/BhSu7k2Lm+Jw=="], 218 - 219 - "@libsql/isomorphic-ws": ["@libsql/isomorphic-ws@0.1.5", "", { "dependencies": { "@types/ws": "^8.5.4", "ws": "^8.13.0" } }, "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg=="], 220 - 221 - "@libsql/linux-arm-gnueabihf": ["@libsql/linux-arm-gnueabihf@0.5.22", "", { "os": "linux", "cpu": "arm" }, "sha512-3Uo3SoDPJe/zBnyZKosziRGtszXaEtv57raWrZIahtQDsjxBVjuzYQinCm9LRCJCUT5t2r5Z5nLDPJi2CwZVoA=="], 222 - 223 - "@libsql/linux-arm-musleabihf": ["@libsql/linux-arm-musleabihf@0.5.22", "", { "os": "linux", "cpu": "arm" }, "sha512-LCsXh07jvSojTNJptT9CowOzwITznD+YFGGW+1XxUr7fS+7/ydUrpDfsMX7UqTqjm7xG17eq86VkWJgHJfvpNg=="], 224 - 225 - "@libsql/linux-arm64-gnu": ["@libsql/linux-arm64-gnu@0.5.22", "", { "os": "linux", "cpu": "arm64" }, "sha512-KSdnOMy88c9mpOFKUEzPskSaF3VLflfSUCBwas/pn1/sV3pEhtMF6H8VUCd2rsedwoukeeCSEONqX7LLnQwRMA=="], 226 - 227 - "@libsql/linux-arm64-musl": ["@libsql/linux-arm64-musl@0.5.22", "", { "os": "linux", "cpu": "arm64" }, "sha512-mCHSMAsDTLK5YH//lcV3eFEgiR23Ym0U9oEvgZA0667gqRZg/2px+7LshDvErEKv2XZ8ixzw3p1IrBzLQHGSsw=="], 228 - 229 - "@libsql/linux-x64-gnu": ["@libsql/linux-x64-gnu@0.5.22", "", { "os": "linux", "cpu": "x64" }, "sha512-kNBHaIkSg78Y4BqAdgjcR2mBilZXs4HYkAmi58J+4GRwDQZh5fIUWbnQvB9f95DkWUIGVeenqLRFY2pcTmlsew=="], 230 - 231 - "@libsql/linux-x64-musl": ["@libsql/linux-x64-musl@0.5.22", "", { "os": "linux", "cpu": "x64" }, "sha512-UZ4Xdxm4pu3pQXjvfJiyCzZop/9j/eA2JjmhMaAhe3EVLH2g11Fy4fwyUp9sT1QJYR1kpc2JLuybPM0kuXv/Tg=="], 232 - 233 - "@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.5.22", "", { "os": "win32", "cpu": "x64" }, "sha512-Fj0j8RnBpo43tVZUVoNK6BV/9AtDUM5S7DF3LB4qTYg1LMSZqi3yeCneUTLJD6XomQJlZzbI4mst89yspVSAnA=="], 234 - 235 - "@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="], 236 - 237 - "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], 238 - 239 - "@poppinss/colors": ["@poppinss/colors@4.1.6", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg=="], 240 - 241 - "@poppinss/dumper": ["@poppinss/dumper@0.6.5", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw=="], 242 - 243 - "@poppinss/exception": ["@poppinss/exception@1.2.3", "", {}, "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw=="], 149 + "@oozcitak/dom": ["@oozcitak/dom@2.0.2", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/url": "^3.0.0", "@oozcitak/util": "^10.0.0" } }, "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w=="], 244 150 245 - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.57.1", "", { "os": "android", "cpu": "arm" }, "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg=="], 151 + "@oozcitak/infra": ["@oozcitak/infra@2.0.2", "", { "dependencies": { "@oozcitak/util": "^10.0.0" } }, "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA=="], 246 152 247 - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.57.1", "", { "os": "android", "cpu": "arm64" }, "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w=="], 153 + "@oozcitak/url": ["@oozcitak/url@3.0.0", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0" } }, "sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ=="], 248 154 249 - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.57.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg=="], 155 + "@oozcitak/util": ["@oozcitak/util@10.0.0", "", {}, "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA=="], 250 156 251 - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.57.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w=="], 157 + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], 252 158 253 - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.57.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug=="], 159 + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="], 254 160 255 - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.57.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q=="], 161 + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="], 256 162 257 - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw=="], 258 - 259 - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.57.1", "", { "os": "linux", "cpu": "arm" }, "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw=="], 260 - 261 - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g=="], 262 - 263 - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.57.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q=="], 163 + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.59.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg=="], 264 164 265 - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA=="], 165 + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.59.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w=="], 266 166 267 - "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw=="], 167 + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.59.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA=="], 268 168 269 - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w=="], 270 - 271 - "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.57.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw=="], 169 + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.59.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg=="], 272 170 273 - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A=="], 171 + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw=="], 274 172 275 - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.57.1", "", { "os": "linux", "cpu": "none" }, "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw=="], 173 + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA=="], 276 174 277 - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.57.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg=="], 175 + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA=="], 278 176 279 - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg=="], 177 + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA=="], 280 178 281 - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.57.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw=="], 179 + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg=="], 282 180 283 - "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.57.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw=="], 181 + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q=="], 284 182 285 - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.57.1", "", { "os": "none", "cpu": "arm64" }, "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ=="], 183 + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA=="], 286 184 287 - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.57.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ=="], 185 + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA=="], 288 186 289 - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.57.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew=="], 187 + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg=="], 290 188 291 - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ=="], 189 + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg=="], 292 190 293 - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], 191 + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.59.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w=="], 294 192 295 - "@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="], 193 + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg=="], 296 194 297 - "@speed-highlight/core": ["@speed-highlight/core@1.2.14", "", {}, "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA=="], 195 + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg=="], 298 196 299 - "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], 197 + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.59.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ=="], 300 198 301 - "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.8", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA=="], 199 + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.59.0", "", { "os": "none", "cpu": "arm64" }, "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA=="], 302 200 303 - "@sveltejs/adapter-cloudflare": ["@sveltejs/adapter-cloudflare@7.2.6", "", { "dependencies": { "@cloudflare/workers-types": "^4.20250507.0", "worktop": "0.8.0-next.18" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0", "wrangler": "^4.0.0" } }, "sha512-PmaWW6EdMue8s24bUwa9EMsnjMaCS1HroM8HwlvwSxO8Cq5LldAxnnaUS5cnJ3RdVRorJZtL71eMTs+wbuXHgw=="], 201 + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.59.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A=="], 304 202 305 - "@sveltejs/kit": ["@sveltejs/kit@2.50.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.6.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^3.0.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": "^5.3.3", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api", "typescript"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-875hTUkEbz+MyJIxWbQjfMaekqdmEKUUfR7JyKcpfMRZqcGyrO9Gd+iS1D/Dx8LpE5FEtutWGOtlAh4ReSAiOA=="], 203 + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.59.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA=="], 306 204 307 - "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.4", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA=="], 205 + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA=="], 308 206 309 - "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.2", "", { "dependencies": { "obug": "^2.1.0" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig=="], 207 + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="], 310 208 311 209 "@tailwindcss/forms": ["@tailwindcss/forms@0.5.11", "", { "dependencies": { "mini-svg-data-uri": "^1.2.3" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" } }, "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA=="], 312 210 313 - "@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="], 211 + "@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="], 314 212 315 - "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.18", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.18", "@tailwindcss/oxide-darwin-arm64": "4.1.18", "@tailwindcss/oxide-darwin-x64": "4.1.18", "@tailwindcss/oxide-freebsd-x64": "4.1.18", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", "@tailwindcss/oxide-linux-x64-musl": "4.1.18", "@tailwindcss/oxide-wasm32-wasi": "4.1.18", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A=="], 213 + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="], 316 214 317 - "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.18", "", { "os": "android", "cpu": "arm64" }, "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q=="], 215 + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg=="], 318 216 319 - "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A=="], 217 + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw=="], 320 218 321 - "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw=="], 219 + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw=="], 322 220 323 - "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.18", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA=="], 221 + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA=="], 324 222 325 - "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18", "", { "os": "linux", "cpu": "arm" }, "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA=="], 223 + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw=="], 326 224 327 - "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw=="], 225 + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ=="], 328 226 329 - "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg=="], 227 + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ=="], 330 228 331 - "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g=="], 229 + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g=="], 332 230 333 - "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.18", "", { "os": "linux", "cpu": "x64" }, "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ=="], 231 + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g=="], 334 232 335 - "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.18", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA=="], 233 + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.1", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q=="], 336 234 337 - "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA=="], 235 + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA=="], 338 236 339 - "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.18", "", { "os": "win32", "cpu": "x64" }, "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q=="], 237 + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ=="], 340 238 341 239 "@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], 342 240 343 - "@tailwindcss/vite": ["@tailwindcss/vite@4.1.18", "", { "dependencies": { "@tailwindcss/node": "4.1.18", "@tailwindcss/oxide": "4.1.18", "tailwindcss": "4.1.18" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA=="], 241 + "@tailwindcss/vite": ["@tailwindcss/vite@4.2.1", "", { "dependencies": { "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "tailwindcss": "4.2.1" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w=="], 344 242 345 - "@testing-library/svelte-core": ["@testing-library/svelte-core@1.0.0", "", { "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0" } }, "sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ=="], 243 + "@tanstack/history": ["@tanstack/history@1.161.4", "", {}, "sha512-Kp/WSt411ZWYvgXy6uiv5RmhHrz9cAml05AQPrtdAp7eUqvIDbMGPnML25OKbzR3RJ1q4wgENxDTvlGPa9+Mww=="], 346 244 347 - "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], 245 + "@tanstack/react-router": ["@tanstack/react-router@1.163.2", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/react-store": "^0.9.1", "@tanstack/router-core": "1.163.2", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-1LosUlpL2mRMWxUZXmkEg5+Br5P5j9TrLngqRgHVbZoFkjnbcj1x9fQN2OVLrBv9Npw97NRsHeJljnAH/c7oSw=="], 348 246 349 - "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], 350 - 351 - "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], 247 + "@tanstack/react-start": ["@tanstack/react-start@1.163.2", "", { "dependencies": { "@tanstack/react-router": "1.163.2", "@tanstack/react-start-client": "1.163.2", "@tanstack/react-start-server": "1.163.2", "@tanstack/router-utils": "^1.161.4", "@tanstack/start-client-core": "1.163.2", "@tanstack/start-plugin-core": "1.163.2", "@tanstack/start-server-core": "1.163.2", "pathe": "^2.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "vite": ">=7.0.0" } }, "sha512-Y/dxoxH1+YLeRECvMlcHaCgL6YdKXO5D5P5ZInjGAha/GJ0v3KNwS5u+45UZfmQRZXqmilsgaeLEMVV5AvgBnw=="], 352 248 353 - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 354 - 355 - "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], 249 + "@tanstack/react-start-client": ["@tanstack/react-start-client@1.163.2", "", { "dependencies": { "@tanstack/react-router": "1.163.2", "@tanstack/router-core": "1.163.2", "@tanstack/start-client-core": "1.163.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-JBlJflRK9lIkuOySEdwrAAyhbIc3DCrUsLUT59+oX7DntBsFnfWLEhXNtZ58PcSR7G6fprGLF4ASW6VAMmA87g=="], 356 250 357 - "@types/node": ["@types/node@24.10.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-+0/4J266CBGPUq/ELg7QUHhN25WYjE0wYTPSQJn1xeu8DOlIOPxXxrNGiLmfAWl7HMMgWFWXpt9IDjMWrF5Iow=="], 251 + "@tanstack/react-start-server": ["@tanstack/react-start-server@1.163.2", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/react-router": "1.163.2", "@tanstack/router-core": "1.163.2", "@tanstack/start-client-core": "1.163.2", "@tanstack/start-server-core": "1.163.2" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-Qjg4hZf10GlgH+Mod/yrStHvYOb2XkDjzdMUJ+KX63XlZBT/+0mhg90NgqXAIfa1D3g45caCFrGg4Xd+imYpig=="], 358 252 359 - "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], 253 + "@tanstack/react-store": ["@tanstack/react-store@0.9.1", "", { "dependencies": { "@tanstack/store": "0.9.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-YzJLnRvy5lIEFTLWBAZmcOjK3+2AepnBv/sr6NZmiqJvq7zTQggyK99Gw8fqYdMdHPQWXjz0epFKJXC+9V2xDA=="], 360 254 361 - "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.54.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/type-utils": "8.54.0", "@typescript-eslint/utils": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.54.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ=="], 255 + "@tanstack/router-core": ["@tanstack/router-core@1.163.2", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/store": "^0.9.1", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-mD0Pav6kcpS317XSJN+wCZaxLLngDhlwgzPNca56dWCp8YKPEvhhj/Zdl+LdRlJQ2VJ5BOy7FbOV1hErc9Nj5Q=="], 362 256 363 - "@typescript-eslint/parser": ["@typescript-eslint/parser@8.54.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/types": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA=="], 257 + "@tanstack/router-generator": ["@tanstack/router-generator@1.163.2", "", { "dependencies": { "@tanstack/router-core": "1.163.2", "@tanstack/router-utils": "1.161.4", "@tanstack/virtual-file-routes": "1.161.4", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-6LjU3+8iKEgt8iOaYCmCnQCs0jsOhc7z8fa1yAYlj3s82uYWv3g5CB9mwv8wZXblXBQWOl+hW4PI6WNjP/CK9w=="], 364 258 365 - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.54.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.54.0", "@typescript-eslint/types": "^8.54.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g=="], 259 + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.163.2", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.163.2", "@tanstack/router-generator": "1.163.2", "@tanstack/router-utils": "1.161.4", "@tanstack/virtual-file-routes": "1.161.4", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.163.2", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-SrVILMz/c15RYWxIMG+bf/glLbP/O9DUxOg0E7bo9pooBxGPvgWSlEzHNjhVekLhK5l7fiuQZzKsfksVeIEqDA=="], 366 260 367 - "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.54.0", "", { "dependencies": { "@typescript-eslint/types": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0" } }, "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg=="], 261 + "@tanstack/router-utils": ["@tanstack/router-utils@1.161.4", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-r8TpjyIZoqrXXaf2DDyjd44gjGBoyE+/oEaaH68yLI9ySPO1gUWmQENZ1MZnmBnpUGN24NOZxdjDLc8npK0SAw=="], 368 262 369 - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.54.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw=="], 263 + "@tanstack/start-client-core": ["@tanstack/start-client-core@1.163.2", "", { "dependencies": { "@tanstack/router-core": "1.163.2", "@tanstack/start-fn-stubs": "1.161.4", "@tanstack/start-storage-context": "1.163.2", "seroval": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-PiOnvd83rqOsyWcN3CxJqoqr41pxM2wAHfchMITKz7rlsX+4J8CWMpHM3yNnLrKRqUnqX3Td9VUV4ueU+4ZCNA=="], 370 264 371 - "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.54.0", "", { "dependencies": { "@typescript-eslint/types": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/utils": "8.54.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA=="], 265 + "@tanstack/start-fn-stubs": ["@tanstack/start-fn-stubs@1.161.4", "", {}, "sha512-b8s6iSQ+ny0P4lGK0n3DKaL6EI7SECG0/89svDeYieVw2+MaFOJVcQo3rU3BUvmuOcIkgkE5IhdzkmzPXH6yfA=="], 372 266 373 - "@typescript-eslint/types": ["@typescript-eslint/types@8.54.0", "", {}, "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA=="], 267 + "@tanstack/start-plugin-core": ["@tanstack/start-plugin-core@1.163.2", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/core": "^7.28.5", "@babel/types": "^7.28.5", "@rolldown/pluginutils": "1.0.0-beta.40", "@tanstack/router-core": "1.163.2", "@tanstack/router-generator": "1.163.2", "@tanstack/router-plugin": "1.163.2", "@tanstack/router-utils": "1.161.4", "@tanstack/start-client-core": "1.163.2", "@tanstack/start-server-core": "1.163.2", "cheerio": "^1.0.0", "exsolve": "^1.0.7", "pathe": "^2.0.3", "picomatch": "^4.0.3", "source-map": "^0.7.6", "srvx": "^0.11.7", "tinyglobby": "^0.2.15", "ufo": "^1.5.4", "vitefu": "^1.1.1", "xmlbuilder2": "^4.0.3", "zod": "^3.24.2" }, "peerDependencies": { "vite": ">=7.0.0" } }, "sha512-y+ransqRMyvTCLd4xU7feo57YsFT8oX++gKfh8pjNgzZTmkbySKBGv8UmO2mvwaNMkZmPwABYOPz60zd3Ls5DQ=="], 374 268 375 - "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.54.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.54.0", "@typescript-eslint/tsconfig-utils": "8.54.0", "@typescript-eslint/types": "8.54.0", "@typescript-eslint/visitor-keys": "8.54.0", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA=="], 269 + "@tanstack/start-server-core": ["@tanstack/start-server-core@1.163.2", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/router-core": "1.163.2", "@tanstack/start-client-core": "1.163.2", "@tanstack/start-storage-context": "1.163.2", "h3-v2": "npm:h3@2.0.1-rc.14", "seroval": "^1.4.2", "tiny-invariant": "^1.3.3" } }, "sha512-2BJzeIuBuHSSQL119vx4MlXktorwOiRqCUaeQOi84LQMbcM78vVl1vWwgU/e/Hjzwz44dwcs5KHaFgfPeRNduA=="], 376 270 377 - "@typescript-eslint/utils": ["@typescript-eslint/utils@8.54.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.54.0", "@typescript-eslint/types": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA=="], 271 + "@tanstack/start-storage-context": ["@tanstack/start-storage-context@1.163.2", "", { "dependencies": { "@tanstack/router-core": "1.163.2" } }, "sha512-zBCn0XqrcS0SDjmRjnnxVMLNCpOx6tveCzKvsoI3xwnJ4iIEMyo2HAS4i3qw+EbAaBam2g5skN9OLd46VEY4uw=="], 378 272 379 - "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.54.0", "", { "dependencies": { "@typescript-eslint/types": "8.54.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA=="], 273 + "@tanstack/store": ["@tanstack/store@0.9.1", "", {}, "sha512-+qcNkOy0N1qSGsP7omVCW0SDrXtaDcycPqBDE726yryiA5eTDFpjBReaYjghVJwNf1pcPMyzIwTGlYjCSQR0Fg=="], 380 274 381 - "@vitest/browser": ["@vitest/browser@4.0.18", "", { "dependencies": { "@vitest/mocker": "4.0.18", "@vitest/utils": "4.0.18", "magic-string": "^0.30.21", "pixelmatch": "7.1.0", "pngjs": "^7.0.0", "sirv": "^3.0.2", "tinyrainbow": "^3.0.3", "ws": "^8.18.3" }, "peerDependencies": { "vitest": "4.0.18" } }, "sha512-gVQqh7paBz3gC+ZdcCmNSWJMk70IUjDeVqi+5m5vYpEHsIwRgw3Y545jljtajhkekIpIp5Gg8oK7bctgY0E2Ng=="], 275 + "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.161.4", "", {}, "sha512-42WoRePf8v690qG8yGRe/YOh+oHni9vUaUUfoqlS91U2scd3a5rkLtVsc6b7z60w3RogH0I00vdrC5AaeiZ18w=="], 382 276 383 - "@vitest/browser-playwright": ["@vitest/browser-playwright@4.0.18", "", { "dependencies": { "@vitest/browser": "4.0.18", "@vitest/mocker": "4.0.18", "tinyrainbow": "^3.0.3" }, "peerDependencies": { "playwright": "*", "vitest": "4.0.18" } }, "sha512-gfajTHVCiwpxRj1qh0Sh/5bbGLG4F/ZH/V9xvFVoFddpITfMta9YGow0W6ZpTTORv2vdJuz9TnrNSmjKvpOf4g=="], 277 + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], 384 278 385 - "@vitest/expect": ["@vitest/expect@4.0.18", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ=="], 279 + "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], 386 280 387 - "@vitest/mocker": ["@vitest/mocker@4.0.18", "", { "dependencies": { "@vitest/spy": "4.0.18", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ=="], 281 + "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], 388 282 389 - "@vitest/pretty-format": ["@vitest/pretty-format@4.0.18", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw=="], 283 + "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], 390 284 391 - "@vitest/runner": ["@vitest/runner@4.0.18", "", { "dependencies": { "@vitest/utils": "4.0.18", "pathe": "^2.0.3" } }, "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw=="], 285 + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 392 286 393 - "@vitest/snapshot": ["@vitest/snapshot@4.0.18", "", { "dependencies": { "@vitest/pretty-format": "4.0.18", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA=="], 287 + "@types/node": ["@types/node@25.3.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A=="], 394 288 395 - "@vitest/spy": ["@vitest/spy@4.0.18", "", {}, "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw=="], 289 + "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], 396 290 397 - "@vitest/utils": ["@vitest/utils@4.0.18", "", { "dependencies": { "@vitest/pretty-format": "4.0.18", "tinyrainbow": "^3.0.3" } }, "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA=="], 291 + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], 398 292 399 - "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], 293 + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.4", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA=="], 400 294 401 - "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], 295 + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], 402 296 403 - "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], 297 + "ansis": ["ansis@4.2.0", "", {}, "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig=="], 404 298 405 - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 299 + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], 406 300 407 301 "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], 408 302 409 - "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], 410 - 411 - "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], 412 - 413 - "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], 414 - 415 - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], 303 + "ast-types": ["ast-types@0.16.1", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg=="], 416 304 417 - "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], 305 + "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], 418 306 419 - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], 307 + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="], 420 308 421 - "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], 309 + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], 422 310 423 - "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], 311 + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], 424 312 425 - "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], 313 + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], 426 314 427 - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], 315 + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], 428 316 429 - "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], 317 + "caniuse-lite": ["caniuse-lite@1.0.30001774", "", {}, "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA=="], 430 318 431 - "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 319 + "cheerio": ["cheerio@1.2.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="], 432 320 433 - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 321 + "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], 434 322 435 - "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 323 + "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], 436 324 437 - "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], 325 + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], 438 326 439 - "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], 327 + "cookie-es": ["cookie-es@2.0.0", "", {}, "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg=="], 440 328 441 - "cross-fetch": ["cross-fetch@4.1.0", "", { "dependencies": { "node-fetch": "^2.7.0" } }, "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw=="], 329 + "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], 442 330 443 - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], 331 + "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], 444 332 445 333 "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], 446 334 447 - "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], 335 + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], 448 336 449 337 "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], 450 338 451 - "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], 452 - 453 - "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], 454 - 455 - "detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], 456 - 457 - "devalue": ["devalue@5.6.2", "", {}, "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg=="], 458 - 459 - "drizzle-kit": ["drizzle-kit@0.31.8", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg=="], 460 - 461 - "drizzle-orm": ["drizzle-orm@0.45.1", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA=="], 339 + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 462 340 463 - "enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="], 464 - 465 - "error-stack-parser-es": ["error-stack-parser-es@1.0.5", "", {}, "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA=="], 466 - 467 - "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], 468 - 469 - "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], 470 - 471 - "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], 472 - 473 - "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], 474 - 475 - "eslint": ["eslint@9.39.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.2", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw=="], 476 - 477 - "eslint-config-prettier": ["eslint-config-prettier@10.1.8", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w=="], 478 - 479 - "eslint-plugin-svelte": ["eslint-plugin-svelte@3.14.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.4.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-Isw0GvaMm0yHxAj71edAdGFh28ufYs+6rk2KlbbZphnqZAzrH3Se3t12IFh2H9+1F/jlDhBBL4oiOJmLqmYX0g=="], 480 - 481 - "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], 482 - 483 - "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], 341 + "diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], 484 342 485 - "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 343 + "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], 486 344 487 - "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], 345 + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], 488 346 489 - "esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="], 347 + "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], 490 348 491 - "esrap": ["esrap@2.2.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-zA6497ha+qKvoWIK+WM9NAh5ni17sKZKhbS5B3PoYbBvaYHZWoS33zmFybmyqpn07RLUxSmn+RCls2/XF+d0oQ=="], 349 + "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], 492 350 493 - "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], 351 + "electron-to-chromium": ["electron-to-chromium@1.5.302", "", {}, "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg=="], 494 352 495 - "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], 353 + "encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="], 496 354 497 - "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], 355 + "enhanced-resolve": ["enhanced-resolve@5.19.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg=="], 498 356 499 - "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], 357 + "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], 500 358 501 - "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], 359 + "esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], 502 360 503 - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], 361 + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], 504 362 505 - "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], 363 + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], 506 364 507 - "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], 365 + "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], 508 366 509 367 "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 510 368 511 - "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], 512 - 513 - "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], 514 - 515 - "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], 369 + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], 516 370 517 - "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], 371 + "framer-motion": ["framer-motion@12.34.3", "", { "dependencies": { "motion-dom": "^12.34.3", "motion-utils": "^12.29.2", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-v81ecyZKYO/DfpTwHivqkxSUBzvceOpoI+wLfgCgoUIKxlFKEXdg0oR9imxwXumT4SFy8vRk9xzJ5l3/Du/55Q=="], 518 372 519 - "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], 373 + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 520 374 521 - "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], 375 + "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], 522 376 523 - "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], 377 + "get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="], 524 378 525 - "get-tsconfig": ["get-tsconfig@4.13.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w=="], 379 + "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 526 380 527 - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], 528 - 529 - "globals": ["globals@17.3.0", "", {}, "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw=="], 381 + "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], 530 382 531 383 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 532 384 533 - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], 385 + "h3-v2": ["h3@2.0.1-rc.14", "", { "dependencies": { "rou3": "^0.7.12", "srvx": "^0.11.2" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"], "bin": { "h3": "bin/h3.mjs" } }, "sha512-163qbGmTr/9rqQRNuqMqtgXnOUAkE4KTdauiC9y0E5iG1I65kte9NyfWvZw5RTDMt6eY+DtyoNzrQ9wA2BfvGQ=="], 534 386 535 - "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], 387 + "htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="], 536 388 537 - "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], 389 + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], 538 390 539 - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], 391 + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], 540 392 541 393 "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 542 394 543 395 "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 544 396 545 - "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], 397 + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], 546 398 547 - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], 399 + "isbot": ["isbot@5.1.35", "", {}, "sha512-waFfC72ZNfwLLuJ2iLaoVaqcNo+CAaLR7xCpAn0Y5WfGzkNHv7ZN39Vbi1y+kb+Zs46XHOX3tZNExroFUPX+Kg=="], 548 400 549 401 "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 550 402 551 - "js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="], 403 + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], 552 404 553 405 "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], 554 406 555 - "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], 407 + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], 556 408 557 - "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], 409 + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], 558 410 559 - "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], 411 + "lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="], 560 412 561 - "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], 413 + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="], 562 414 563 - "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], 564 - 565 - "known-css-properties": ["known-css-properties@0.37.0", "", {}, "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ=="], 415 + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="], 566 416 567 - "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], 568 - 569 - "libsql": ["libsql@0.5.22", "", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.5.22", "@libsql/darwin-x64": "0.5.22", "@libsql/linux-arm-gnueabihf": "0.5.22", "@libsql/linux-arm-musleabihf": "0.5.22", "@libsql/linux-arm64-gnu": "0.5.22", "@libsql/linux-arm64-musl": "0.5.22", "@libsql/linux-x64-gnu": "0.5.22", "@libsql/linux-x64-musl": "0.5.22", "@libsql/win32-x64-msvc": "0.5.22" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "arm", "x64", "arm64", ] }, "sha512-NscWthMQt7fpU8lqd7LXMvT9pi+KhhmTHAJWUB/Lj6MWa0MKFv0F2V4C6WKKpjCVZl0VwcDz4nOI3CyaT1DDiA=="], 570 - 571 - "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], 572 - 573 - "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="], 574 - 575 - "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="], 576 - 577 - "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="], 578 - 579 - "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="], 580 - 581 - "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="], 582 - 583 - "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="], 417 + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="], 584 418 585 - "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="], 419 + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="], 586 420 587 - "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="], 421 + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="], 588 422 589 - "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="], 423 + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="], 590 424 591 - "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="], 425 + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="], 592 426 593 - "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], 427 + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA=="], 594 428 595 - "lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], 429 + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="], 596 430 597 - "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], 431 + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="], 598 432 599 - "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], 433 + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="], 600 434 601 - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], 435 + "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], 602 436 603 437 "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 604 438 605 439 "mini-svg-data-uri": ["mini-svg-data-uri@1.4.4", "", { "bin": { "mini-svg-data-uri": "cli.js" } }, "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg=="], 606 440 607 - "miniflare": ["miniflare@4.20260131.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.18.2", "workerd": "1.20260131.0", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-CtObRzlAzOUpCFH+MgImykxmDNKthrgIYtC+oLC3UGpve6bGLomKUW4u4EorTvzlQFHe66/9m/+AYbBbpzG0mQ=="], 608 - 609 - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], 441 + "motion": ["motion@12.34.3", "", { "dependencies": { "framer-motion": "^12.34.3", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-xZIkBGO7v/Uvm+EyaqYd+9IpXu0sZqLywVlGdCFrrMiaO9JI4Kx51mO9KlHSWwll+gZUVY5OJsWgYI5FywJ/tw=="], 610 442 611 - "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], 443 + "motion-dom": ["motion-dom@12.34.3", "", { "dependencies": { "motion-utils": "^12.29.2" } }, "sha512-sYgFe+pR9aIM7o4fhs2aXtOI+oqlUd33N9Yoxcgo1Fv7M20sRkHtCmzE/VRNIcq7uNJ+qio+Xubt1FXH3pQ+eQ=="], 612 444 613 - "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], 445 + "motion-utils": ["motion-utils@12.29.2", "", {}, "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A=="], 614 446 615 447 "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 616 448 617 449 "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 618 450 619 - "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], 620 - 621 - "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], 451 + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], 622 452 623 - "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], 453 + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], 624 454 625 - "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], 455 + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], 626 456 627 - "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], 457 + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], 628 458 629 - "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], 459 + "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="], 630 460 631 - "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], 632 - 633 - "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], 634 - 635 - "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], 636 - 637 - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], 638 - 639 - "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], 461 + "parse5-parser-stream": ["parse5-parser-stream@7.1.2", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="], 640 462 641 463 "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], 642 464 ··· 644 466 645 467 "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 646 468 647 - "pixelmatch": ["pixelmatch@7.1.0", "", { "dependencies": { "pngjs": "^7.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng=="], 648 - 649 - "playwright": ["playwright@1.58.1", "", { "dependencies": { "playwright-core": "1.58.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-+2uTZHxSCcxjvGc5C891LrS1/NlxglGxzrC4seZiVjcYVQfUa87wBL6rTDqzGjuoWNjnBzRqKmF6zRYGMvQUaQ=="], 650 - 651 - "playwright-core": ["playwright-core@1.58.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg=="], 652 - 653 - "pngjs": ["pngjs@7.0.0", "", {}, "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow=="], 654 - 655 469 "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], 656 470 657 - "postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="], 658 - 659 - "postcss-safe-parser": ["postcss-safe-parser@7.0.1", "", { "peerDependencies": { "postcss": "^8.4.31" } }, "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A=="], 660 - 661 - "postcss-scss": ["postcss-scss@4.0.9", "", { "peerDependencies": { "postcss": "^8.4.29" } }, "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A=="], 662 - 663 471 "postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], 664 472 665 - "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], 666 - 667 473 "prettier": ["prettier@3.8.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg=="], 668 474 669 - "prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.1", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-xL49LCloMoZRvSwa6IEdN2GV6cq2IqpYGstYtMT+5wmml1/dClEoI0MZR78MiVPpu6BdQFfN0/y73yO6+br5Pg=="], 475 + "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], 670 476 671 - "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.2", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA=="], 477 + "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], 672 478 673 - "promise-limit": ["promise-limit@2.7.0", "", {}, "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw=="], 479 + "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], 674 480 675 - "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], 481 + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], 676 482 677 - "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 483 + "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], 678 484 679 - "regexparam": ["regexparam@3.0.0", "", {}, "sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q=="], 680 - 681 - "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], 485 + "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], 682 486 683 487 "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], 684 488 685 - "rollup": ["rollup@4.57.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.57.1", "@rollup/rollup-android-arm64": "4.57.1", "@rollup/rollup-darwin-arm64": "4.57.1", "@rollup/rollup-darwin-x64": "4.57.1", "@rollup/rollup-freebsd-arm64": "4.57.1", "@rollup/rollup-freebsd-x64": "4.57.1", "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", "@rollup/rollup-linux-arm-musleabihf": "4.57.1", "@rollup/rollup-linux-arm64-gnu": "4.57.1", "@rollup/rollup-linux-arm64-musl": "4.57.1", "@rollup/rollup-linux-loong64-gnu": "4.57.1", "@rollup/rollup-linux-loong64-musl": "4.57.1", "@rollup/rollup-linux-ppc64-gnu": "4.57.1", "@rollup/rollup-linux-ppc64-musl": "4.57.1", "@rollup/rollup-linux-riscv64-gnu": "4.57.1", "@rollup/rollup-linux-riscv64-musl": "4.57.1", "@rollup/rollup-linux-s390x-gnu": "4.57.1", "@rollup/rollup-linux-x64-gnu": "4.57.1", "@rollup/rollup-linux-x64-musl": "4.57.1", "@rollup/rollup-openbsd-x64": "4.57.1", "@rollup/rollup-openharmony-arm64": "4.57.1", "@rollup/rollup-win32-arm64-msvc": "4.57.1", "@rollup/rollup-win32-ia32-msvc": "4.57.1", "@rollup/rollup-win32-x64-gnu": "4.57.1", "@rollup/rollup-win32-x64-msvc": "4.57.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A=="], 686 - 687 - "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], 489 + "rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="], 688 490 689 - "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], 690 - 691 - "set-cookie-parser": ["set-cookie-parser@3.0.1", "", {}, "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q=="], 491 + "rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="], 692 492 693 - "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], 493 + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], 694 494 695 - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], 495 + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], 696 496 697 - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], 497 + "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], 698 498 699 - "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], 499 + "seroval": ["seroval@1.5.0", "", {}, "sha512-OE4cvmJ1uSPrKorFIH9/w/Qwuvi/IMcGbv5RKgcJ/zjA/IohDLU6SVaxFN9FwajbP7nsX0dQqMDes1whk3y+yw=="], 700 500 701 - "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], 501 + "seroval-plugins": ["seroval-plugins@1.5.0", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-EAHqADIQondwRZIdeW2I636zgsODzoBDwb3PT/+7TLDWyw1Dy/Xv7iGUIEXXav7usHDE9HVhOU61irI3EnyyHA=="], 702 502 703 - "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], 503 + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], 704 504 705 505 "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 706 506 707 - "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], 507 + "srvx": ["srvx@0.11.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-2n9t0YnAXPJjinytvxccNgs7rOA5gmE7Wowt/8Dy2dx2fDC6sBhfBpbrCvjYKALlVukPS/Uq3QwkolKNa7P/2Q=="], 708 508 709 - "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], 509 + "tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="], 710 510 711 - "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], 712 - 713 - "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], 714 - 715 - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], 716 - 717 - "svelte": ["svelte@5.49.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "devalue": "^5.6.2", "esm-env": "^1.2.1", "esrap": "^2.2.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-jj95WnbKbXsXXngYj28a4zx8jeZx50CN/J4r0CEeax2pbfdsETv/J1K8V9Hbu3DCXnpHz5qAikICuxEooi7eNQ=="], 718 - 719 - "svelte-check": ["svelte-check@4.3.6", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-uBkz96ElE3G4pt9E1Tw0xvBfIUQkeH794kDQZdAUk795UVMr+NJZpuFSS62vcmO/DuSalK83LyOwhgWq8YGU1Q=="], 720 - 721 - "svelte-eslint-parser": ["svelte-eslint-parser@1.4.1", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-1eqkfQ93goAhjAXxZiu1SaKI9+0/sxp4JIWQwUpsz7ybehRE5L8dNuz7Iry7K22R47p5/+s9EM+38nHV2OlgXA=="], 722 - 723 - "tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], 511 + "tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="], 724 512 725 513 "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], 726 514 727 - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], 515 + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], 728 516 729 - "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], 517 + "tiny-warning": ["tiny-warning@1.0.3", "", {}, "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="], 730 518 731 519 "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 732 520 733 - "tinyrainbow": ["tinyrainbow@3.0.3", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="], 521 + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 734 522 735 - "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], 736 - 737 - "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], 738 - 739 - "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], 523 + "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], 740 524 741 525 "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 742 526 743 - "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], 527 + "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], 744 528 745 529 "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 746 530 747 - "typescript-eslint": ["typescript-eslint@8.54.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", "@typescript-eslint/typescript-estree": "8.54.0", "@typescript-eslint/utils": "8.54.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ=="], 531 + "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], 748 532 749 - "undici": ["undici@7.18.2", "", {}, "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw=="], 533 + "undici": ["undici@7.22.0", "", {}, "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg=="], 750 534 751 - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], 535 + "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], 752 536 753 - "unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="], 537 + "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], 754 538 755 - "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], 539 + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], 540 + 541 + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], 756 542 757 543 "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], 758 - 759 - "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], 760 544 761 545 "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], 762 546 763 - "vite-plugin-devtools-json": ["vite-plugin-devtools-json@1.0.0", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-MobvwqX76Vqt/O4AbnNMNWoXWGrKUqZbphCUle/J2KXH82yKQiunOeKnz/nqEPosPsoWWPP9FtNuPBSYpiiwkw=="], 764 - 765 - "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], 766 - 767 - "vitest": ["vitest@4.0.18", "", { "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", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.18", "@vitest/browser-preview": "4.0.18", "@vitest/browser-webdriverio": "4.0.18", "@vitest/ui": "4.0.18", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ=="], 768 - 769 - "vitest-browser-svelte": ["vitest-browser-svelte@2.0.2", "", { "dependencies": { "@testing-library/svelte-core": "^1.0.0" }, "peerDependencies": { "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", "vitest": "^4.0.0" } }, "sha512-OLJVYoIYflwToFIy3s41pZ9mVp6dwXfYd8IIsWoc57g8DyN3SxsNJ5GB1xWFPxLFlKM+1MPExjPxLaqdELrfRQ=="], 770 - 771 - "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], 772 - 773 - "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], 774 - 775 - "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], 776 - 777 - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], 778 - 779 - "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], 780 - 781 - "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], 782 - 783 - "workerd": ["workerd@1.20260131.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260131.0", "@cloudflare/workerd-darwin-arm64": "1.20260131.0", "@cloudflare/workerd-linux-64": "1.20260131.0", "@cloudflare/workerd-linux-arm64": "1.20260131.0", "@cloudflare/workerd-windows-64": "1.20260131.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-4zZxOdWeActbRfydQQlj7vZ2ay01AjjNC4K3stjmWC3xZHeXeN3EAROwsWE83SZHhtw4rn18srrhtXoQvQMw3Q=="], 784 - 785 - "worktop": ["worktop@0.8.0-next.18", "", { "dependencies": { "mrmime": "^2.0.0", "regexparam": "^3.0.0" } }, "sha512-+TvsA6VAVoMC3XDKR5MoC/qlLqDixEfOBysDEKnPIPou/NvoPWCAuXHXMsswwlvmEuvX56lQjvELLyLuzTKvRw=="], 786 - 787 - "wrangler": ["wrangler@4.62.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.12.0", "blake3-wasm": "2.1.5", "esbuild": "0.27.0", "miniflare": "4.20260131.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260131.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260131.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-DogP9jifqw85g33BqwF6m21YBW5J7+Ep9IJLgr6oqHU0RkA79JMN5baeWXdmnIWZl+VZh6bmtNtR+5/Djd32tg=="], 788 - 789 - "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], 790 - 791 - "yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], 547 + "vite-tsconfig-paths": ["vite-tsconfig-paths@6.1.1", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg=="], 792 548 793 - "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 549 + "vitefu": ["vitefu@1.1.2", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw=="], 794 550 795 - "youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="], 551 + "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], 796 552 797 - "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], 553 + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], 798 554 799 - "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], 555 + "whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], 800 556 801 - "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], 557 + "xmlbuilder2": ["xmlbuilder2@4.0.3", "", { "dependencies": { "@oozcitak/dom": "^2.0.2", "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0", "js-yaml": "^4.1.1" } }, "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA=="], 802 558 803 - "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], 559 + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], 804 560 805 - "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], 806 - 807 - "@eslint/config-helpers/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], 808 - 809 - "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], 810 - 811 - "@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], 812 - 813 - "@poppinss/dumper/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], 561 + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], 814 562 815 563 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], 816 564 ··· 824 572 825 573 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 826 574 827 - "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], 828 - 829 - "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], 830 - 831 - "cross-fetch/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], 832 - 833 - "eslint/@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], 834 - 835 - "eslint-plugin-svelte/globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="], 836 - 837 - "lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 838 - 839 - "miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], 840 - 841 - "rollup/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 842 - 843 - "sharp/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 844 - 845 - "svelte-eslint-parser/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], 846 - 847 - "vite/esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], 848 - 849 - "vite/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 850 - 851 - "wrangler/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="], 852 - 853 - "wrangler/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 854 - 855 - "youch/cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], 856 - 857 - "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], 858 - 859 - "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], 860 - 861 - "@esbuild-kit/core-utils/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.18.20", "", { "os": "android", "cpu": "x64" }, "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg=="], 862 - 863 - "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.18.20", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA=="], 864 - 865 - "@esbuild-kit/core-utils/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.18.20", "", { "os": "darwin", "cpu": "x64" }, "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ=="], 866 - 867 - "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.18.20", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw=="], 868 - 869 - "@esbuild-kit/core-utils/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.18.20", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ=="], 870 - 871 - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.18.20", "", { "os": "linux", "cpu": "arm" }, "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg=="], 872 - 873 - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.18.20", "", { "os": "linux", "cpu": "arm64" }, "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA=="], 874 - 875 - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.18.20", "", { "os": "linux", "cpu": "ia32" }, "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA=="], 876 - 877 - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg=="], 878 - 879 - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ=="], 880 - 881 - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.18.20", "", { "os": "linux", "cpu": "ppc64" }, "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA=="], 882 - 883 - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.18.20", "", { "os": "linux", "cpu": "none" }, "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A=="], 884 - 885 - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.18.20", "", { "os": "linux", "cpu": "s390x" }, "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ=="], 886 - 887 - "@esbuild-kit/core-utils/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.18.20", "", { "os": "linux", "cpu": "x64" }, "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w=="], 888 - 889 - "@esbuild-kit/core-utils/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.18.20", "", { "os": "none", "cpu": "x64" }, "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A=="], 890 - 891 - "@esbuild-kit/core-utils/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.18.20", "", { "os": "openbsd", "cpu": "x64" }, "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg=="], 892 - 893 - "@esbuild-kit/core-utils/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.18.20", "", { "os": "sunos", "cpu": "x64" }, "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ=="], 894 - 895 - "@esbuild-kit/core-utils/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.18.20", "", { "os": "win32", "cpu": "arm64" }, "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg=="], 896 - 897 - "@esbuild-kit/core-utils/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.18.20", "", { "os": "win32", "cpu": "ia32" }, "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g=="], 898 - 899 - "@esbuild-kit/core-utils/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.18.20", "", { "os": "win32", "cpu": "x64" }, "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ=="], 900 - 901 - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], 902 - 903 - "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], 904 - 905 - "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="], 906 - 907 - "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="], 908 - 909 - "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="], 910 - 911 - "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="], 912 - 913 - "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="], 914 - 915 - "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="], 916 - 917 - "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="], 918 - 919 - "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="], 920 - 921 - "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="], 922 - 923 - "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="], 924 - 925 - "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="], 926 - 927 - "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="], 928 - 929 - "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="], 930 - 931 - "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="], 932 - 933 - "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="], 934 - 935 - "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="], 936 - 937 - "vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="], 938 - 939 - "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="], 940 - 941 - "vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="], 942 - 943 - "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="], 944 - 945 - "vite/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="], 946 - 947 - "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="], 948 - 949 - "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="], 950 - 951 - "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="], 952 - 953 - "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], 954 - 955 - "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="], 956 - 957 - "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.0", "", { "os": "android", "cpu": "arm" }, "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ=="], 958 - 959 - "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.0", "", { "os": "android", "cpu": "arm64" }, "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ=="], 960 - 961 - "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.0", "", { "os": "android", "cpu": "x64" }, "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q=="], 962 - 963 - "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg=="], 964 - 965 - "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g=="], 966 - 967 - "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw=="], 968 - 969 - "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g=="], 970 - 971 - "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ=="], 972 - 973 - "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ=="], 974 - 975 - "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw=="], 976 - 977 - "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg=="], 575 + "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], 978 576 979 - "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg=="], 577 + "@tanstack/start-plugin-core/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.40", "", {}, "sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w=="], 980 578 981 - "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA=="], 579 + "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 982 580 983 - "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ=="], 581 + "htmlparser2/entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], 984 582 985 - "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w=="], 583 + "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], 986 584 987 - "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw=="], 585 + "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 988 586 989 - "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w=="], 990 - 991 - "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.0", "", { "os": "none", "cpu": "x64" }, "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA=="], 992 - 993 - "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ=="], 994 - 995 - "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A=="], 996 - 997 - "wrangler/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA=="], 998 - 999 - "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA=="], 1000 - 1001 - "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg=="], 1002 - 1003 - "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ=="], 1004 - 1005 - "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="], 587 + "recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], 1006 588 } 1007 589 }
-11
drizzle.config.ts
··· 1 - import { defineConfig } from 'drizzle-kit'; 2 - 3 - if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set'); 4 - 5 - export default defineConfig({ 6 - schema: './src/lib/server/db/schema.ts', 7 - dialect: 'sqlite', 8 - dbCredentials: { url: process.env.DATABASE_URL }, 9 - verbose: true, 10 - strict: true 11 - });
-39
eslint.config.js
··· 1 - import prettier from 'eslint-config-prettier'; 2 - import path from 'node:path'; 3 - import { includeIgnoreFile } from '@eslint/compat'; 4 - import js from '@eslint/js'; 5 - import svelte from 'eslint-plugin-svelte'; 6 - import { defineConfig } from 'eslint/config'; 7 - import globals from 'globals'; 8 - import ts from 'typescript-eslint'; 9 - import svelteConfig from './svelte.config.js'; 10 - 11 - const gitignorePath = path.resolve(import.meta.dirname, '.gitignore'); 12 - 13 - export default defineConfig( 14 - includeIgnoreFile(gitignorePath), 15 - js.configs.recommended, 16 - ...ts.configs.recommended, 17 - ...svelte.configs.recommended, 18 - prettier, 19 - ...svelte.configs.prettier, 20 - { 21 - languageOptions: { globals: { ...globals.browser, ...globals.node } }, 22 - rules: { 23 - // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. 24 - // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors 25 - 'no-undef': 'off' 26 - } 27 - }, 28 - { 29 - files: ['**/*.svelte', '**/*.svelte.ts', '**/*.svelte.js'], 30 - languageOptions: { 31 - parserOptions: { 32 - projectService: true, 33 - extraFileExtensions: ['.svelte'], 34 - parser: ts.parser, 35 - svelteConfig 36 - } 37 - } 38 - } 39 - );
+16 -41
package.json
··· 5 5 "type": "module", 6 6 "scripts": { 7 7 "dev": "vite dev", 8 - "build": "vite build", 9 - "preview": "wrangler dev .svelte-kit/cloudflare/_worker.js --port 4173", 10 - "prepare": "svelte-kit sync || echo ''", 11 - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 12 - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 13 - "lint": "prettier --check . && eslint .", 14 - "format": "prettier --write .", 15 - "test:unit": "vitest", 16 - "test": "npm run test:unit -- --run", 17 - "gen": "wrangler types", 18 - "db:push": "drizzle-kit push", 19 - "db:generate": "drizzle-kit generate", 20 - "db:migrate": "drizzle-kit migrate", 21 - "db:studio": "drizzle-kit studio" 8 + "build": "vite build" 9 + }, 10 + "dependencies": { 11 + "@base-ui-components/react": "^1.0.0-rc.0", 12 + "@tanstack/react-router": "^1.163.2", 13 + "@tanstack/react-start": "^1.163.2", 14 + "motion": "^12.34.3", 15 + "react": "^19.2.4", 16 + "react-dom": "^19.2.4" 22 17 }, 23 18 "devDependencies": { 24 - "@eslint/compat": "^2.0.1", 25 - "@eslint/js": "^9.39.2", 26 - "@libsql/client": "^0.17.0", 27 - "@sveltejs/adapter-cloudflare": "^7.2.6", 28 - "@sveltejs/kit": "^2.50.1", 29 - "@sveltejs/vite-plugin-svelte": "^6.2.4", 30 19 "@tailwindcss/forms": "^0.5.11", 31 20 "@tailwindcss/typography": "^0.5.19", 32 - "@tailwindcss/vite": "^4.1.18", 33 - "@types/node": "^24", 34 - "@vitest/browser-playwright": "^4.0.18", 35 - "drizzle-kit": "^0.31.8", 36 - "drizzle-orm": "^0.45.1", 37 - "eslint": "^9.39.2", 38 - "eslint-config-prettier": "^10.1.8", 39 - "eslint-plugin-svelte": "^3.14.0", 40 - "globals": "^17.1.0", 41 - "playwright": "^1.58.0", 42 - "prettier": "^3.8.1", 43 - "prettier-plugin-svelte": "^3.4.1", 44 - "prettier-plugin-tailwindcss": "^0.7.2", 45 - "svelte": "^5.48.2", 46 - "svelte-check": "^4.3.5", 47 - "tailwindcss": "^4.1.18", 21 + "@tailwindcss/vite": "^4.2.1", 22 + "@types/node": "^25.3.0", 23 + "@types/react": "^19.2.14", 24 + "@types/react-dom": "^19.2.3", 25 + "@vitejs/plugin-react": "^5.1.4", 26 + "tailwindcss": "^4.2.1", 48 27 "typescript": "^5.9.3", 49 - "typescript-eslint": "^8.53.1", 50 28 "vite": "^7.3.1", 51 - "vite-plugin-devtools-json": "^1.0.0", 52 - "vitest": "^4.0.18", 53 - "vitest-browser-svelte": "^2.0.2", 54 - "wrangler": "^4.60.0" 29 + "vite-tsconfig-paths": "^6.1.1" 55 30 } 56 31 }
-20
src/app.d.ts
··· 1 - // See https://svelte.dev/docs/kit/types#app.d.ts 2 - // for information about these interfaces 3 - declare global { 4 - namespace App { 5 - interface Platform { 6 - env: Env; 7 - ctx: ExecutionContext; 8 - caches: CacheStorage; 9 - cf?: IncomingRequestCfProperties; 10 - } 11 - 12 - // interface Error {} 13 - // interface Locals {} 14 - // interface PageData {} 15 - // interface PageState {} 16 - // interface Platform {} 17 - } 18 - } 19 - 20 - export {};
-11
src/app.html
··· 1 - <!doctype html> 2 - <html lang="en"> 3 - <head> 4 - <meta charset="utf-8" /> 5 - <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 - %sveltekit.head% 7 - </head> 8 - <body data-sveltekit-preload-data="hover"> 9 - <div style="display: contents">%sveltekit.body%</div> 10 - </body> 11 - </html>
+21
src/components/HeaderBar.tsx
··· 1 + export function HeaderBar() { 2 + return ( 3 + <nav className="fixed top-0 inset-x-0 h-14 z-30 bg-bg-page flex items-center justify-between px-4"> 4 + <div className="flex items-center gap-6"> 5 + <span className="text-text-tertiary text-sm font-medium">Discover</span> 6 + <span className="text-text-tertiary text-sm font-medium">Consume</span> 7 + <span className="text-text-tertiary text-sm font-medium">Create</span> 8 + </div> 9 + 10 + <div className="flex items-center gap-4"> 11 + <span className="text-text-tertiary text-sm">12:00</span> 12 + <span className="text-text-tertiary text-sm">Messages</span> 13 + </div> 14 + 15 + <div className="flex items-center gap-4"> 16 + <div className="w-6 h-6 rounded-full bg-text-tertiary/20"></div> 17 + <div className="w-6 h-6 rounded-full bg-text-tertiary/20"></div> 18 + </div> 19 + </nav> 20 + ) 21 + }
-7
src/demo.spec.ts
··· 1 - import { describe, it, expect } from 'vitest'; 2 - 3 - describe('sum test', () => { 4 - it('adds 1 + 2 to equal 3', () => { 5 - expect(1 + 2).toBe(3); 6 - }); 7 - });
-1
src/lib/assets/favicon.svg
··· 1 - <svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
-17
src/lib/components/HeaderBar.svelte
··· 1 - <nav class="fixed top-0 inset-x-0 h-14 z-30 bg-bg-page flex items-center justify-between px-4"> 2 - <div class="flex items-center gap-6"> 3 - <span class="text-text-tertiary text-sm font-medium">Discover</span> 4 - <span class="text-text-tertiary text-sm font-medium">Consume</span> 5 - <span class="text-text-tertiary text-sm font-medium">Create</span> 6 - </div> 7 - 8 - <div class="flex items-center gap-4"> 9 - <span class="text-text-tertiary text-sm">12:00</span> 10 - <span class="text-text-tertiary text-sm">Messages</span> 11 - </div> 12 - 13 - <div class="flex items-center gap-4"> 14 - <div class="w-6 h-6 rounded-full bg-text-tertiary/20"></div> 15 - <div class="w-6 h-6 rounded-full bg-text-tertiary/20"></div> 16 - </div> 17 - </nav>
-1
src/lib/index.ts
··· 1 - // place files you want to import through the `$lib` alias in this folder.
-10
src/lib/server/db/index.ts
··· 1 - import { drizzle } from 'drizzle-orm/libsql'; 2 - import { createClient } from '@libsql/client'; 3 - import * as schema from './schema'; 4 - import { env } from '$env/dynamic/private'; 5 - 6 - if (!env.DATABASE_URL) throw new Error('DATABASE_URL is not set'); 7 - 8 - const client = createClient({ url: env.DATABASE_URL }); 9 - 10 - export const db = drizzle(client, { schema });
-8
src/lib/server/db/schema.ts
··· 1 - import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'; 2 - 3 - export const user = sqliteTable('user', { 4 - id: text('id') 5 - .primaryKey() 6 - .$defaultFn(() => crypto.randomUUID()), 7 - age: integer('age') 8 - });
+68
src/routeTree.gen.ts
··· 1 + /* eslint-disable */ 2 + 3 + // @ts-nocheck 4 + 5 + // noinspection JSUnusedGlobalSymbols 6 + 7 + // This file was automatically generated by TanStack Router. 8 + // You should NOT make any changes in this file as it will be overwritten. 9 + // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. 10 + 11 + import { Route as rootRouteImport } from './routes/__root' 12 + import { Route as IndexRouteImport } from './routes/index' 13 + 14 + const IndexRoute = IndexRouteImport.update({ 15 + id: '/', 16 + path: '/', 17 + getParentRoute: () => rootRouteImport, 18 + } as any) 19 + 20 + export interface FileRoutesByFullPath { 21 + '/': typeof IndexRoute 22 + } 23 + export interface FileRoutesByTo { 24 + '/': typeof IndexRoute 25 + } 26 + export interface FileRoutesById { 27 + __root__: typeof rootRouteImport 28 + '/': typeof IndexRoute 29 + } 30 + export interface FileRouteTypes { 31 + fileRoutesByFullPath: FileRoutesByFullPath 32 + fullPaths: '/' 33 + fileRoutesByTo: FileRoutesByTo 34 + to: '/' 35 + id: '__root__' | '/' 36 + fileRoutesById: FileRoutesById 37 + } 38 + export interface RootRouteChildren { 39 + IndexRoute: typeof IndexRoute 40 + } 41 + 42 + declare module '@tanstack/react-router' { 43 + interface FileRoutesByPath { 44 + '/': { 45 + id: '/' 46 + path: '/' 47 + fullPath: '/' 48 + preLoaderRoute: typeof IndexRouteImport 49 + parentRoute: typeof rootRouteImport 50 + } 51 + } 52 + } 53 + 54 + const rootRouteChildren: RootRouteChildren = { 55 + IndexRoute: IndexRoute, 56 + } 57 + export const routeTree = rootRouteImport 58 + ._addFileChildren(rootRouteChildren) 59 + ._addFileTypes<FileRouteTypes>() 60 + 61 + import type { getRouter } from './router.tsx' 62 + import type { createStart } from '@tanstack/react-start' 63 + declare module '@tanstack/react-start' { 64 + interface Register { 65 + ssr: true 66 + router: Awaited<ReturnType<typeof getRouter>> 67 + } 68 + }
+16
src/router.tsx
··· 1 + import { createRouter } from '@tanstack/react-router' 2 + import { routeTree } from './routeTree.gen' 3 + 4 + export function getRouter() { 5 + const router = createRouter({ 6 + routeTree, 7 + scrollRestoration: true, 8 + }) 9 + return router 10 + } 11 + 12 + declare module '@tanstack/react-router' { 13 + interface Register { 14 + router: ReturnType<typeof getRouter> 15 + } 16 + }
-25
src/routes/+layout.svelte
··· 1 - <script lang="ts"> 2 - import favicon from '$lib/assets/favicon.svg'; 3 - import HeaderBar from '$lib/components/HeaderBar.svelte'; 4 - import './layout.css'; 5 - 6 - let { children } = $props(); 7 - </script> 8 - 9 - <svelte:head><link rel="icon" href={favicon} /></svelte:head> 10 - 11 - <div> 12 - <!-- Fixed card background (just the visual shape) --> 13 - <div class="fixed top-14 right-2 bottom-2 left-2 -z-10 rounded-2xl bg-bg-front-1"></div> 14 - 15 - <!-- Fixed nav bar --> 16 - <HeaderBar /> 17 - 18 - <!-- Fixed bottom mask (covers area below card) --> 19 - <div class="fixed inset-x-0 bottom-0 z-10 h-2 bg-bg-page"></div> 20 - 21 - <!-- Scrollable content wrapper (normal document flow) --> 22 - <main class="px-8 pt-32 pb-4"> 23 - {@render children()} 24 - </main> 25 - </div>
-32
src/routes/+page.svelte
··· 1 - <!-- Fixed sub-header (calendar + filters) --> 2 - <div class="fixed top-14 left-2 right-2 z-20 rounded-t-2xl bg-bg-front-1 px-6 py-4"> 3 - <div class="flex items-center justify-between"> 4 - <div class="flex items-center gap-4"> 5 - <span class="text-text-secondary text-sm">Mon 3</span> 6 - <span class="text-text-secondary text-sm">Tue 4</span> 7 - <span class="text-text-primary text-sm font-semibold">Wed 5</span> 8 - <span class="text-text-secondary text-sm">Thu 6</span> 9 - <span class="text-text-secondary text-sm">Fri 7</span> 10 - </div> 11 - <div class="flex items-center gap-2"> 12 - <button class="px-3 py-1 text-xs rounded-full bg-bg-front-2 text-text-primary border border-border-1">All</button> 13 - <button class="px-3 py-1 text-xs rounded-full text-text-secondary border border-border-1">Events</button> 14 - <button class="px-3 py-1 text-xs rounded-full text-text-secondary border border-border-1">Tasks</button> 15 - </div> 16 - </div> 17 - </div> 18 - 19 - <!-- Content (normal flow, scrolls with page) --> 20 - <div class="flex flex-col gap-3"> 21 - {#each Array(20) as _, i} 22 - <div class="rounded-xl bg-bg-front-2 border border-border-1 p-4"> 23 - <div class="flex items-center justify-between"> 24 - <div> 25 - <p class="text-text-primary text-sm font-medium">Placeholder item {i + 1}</p> 26 - <p class="text-text-secondary text-xs mt-1">Some description text here</p> 27 - </div> 28 - <span class="text-text-secondary text-xs">9:00 AM</span> 29 - </div> 30 - </div> 31 - {/each} 32 - </div>
+47
src/routes/__root.tsx
··· 1 + import { 2 + createRootRoute, 3 + HeadContent, 4 + Outlet, 5 + Scripts, 6 + } from '@tanstack/react-router' 7 + import { HeaderBar } from '../components/HeaderBar' 8 + import appCss from '../styles.css?url' 9 + 10 + export const Route = createRootRoute({ 11 + head: () => ({ 12 + links: [{ rel: 'stylesheet', href: appCss }], 13 + meta: [ 14 + { charSet: 'utf-8' }, 15 + { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 16 + ], 17 + }), 18 + component: RootLayout, 19 + }) 20 + 21 + function RootLayout() { 22 + return ( 23 + <html lang="en"> 24 + <head> 25 + <HeadContent /> 26 + </head> 27 + <body> 28 + <div style={{ isolation: 'isolate' }}> 29 + {/* Fixed card background (just the visual shape) */} 30 + <div className="fixed top-14 right-2 bottom-2 left-2 -z-10 rounded-2xl bg-bg-front-1" /> 31 + 32 + {/* Fixed nav bar */} 33 + <HeaderBar /> 34 + 35 + {/* Fixed bottom mask (covers area below card) */} 36 + <div className="fixed inset-x-0 bottom-0 z-10 h-2 bg-bg-page" /> 37 + 38 + {/* Scrollable content wrapper (normal document flow) */} 39 + <main className="px-8 pt-32 pb-4"> 40 + <Outlet /> 41 + </main> 42 + </div> 43 + <Scripts /> 44 + </body> 45 + </html> 46 + ) 47 + }
+57
src/routes/index.tsx
··· 1 + import { createFileRoute } from '@tanstack/react-router' 2 + 3 + export const Route = createFileRoute('/')({ 4 + component: HomePage, 5 + }) 6 + 7 + function HomePage() { 8 + return ( 9 + <> 10 + {/* Fixed sub-header (calendar + filters) */} 11 + <div className="fixed top-14 left-2 right-2 z-20 rounded-t-2xl bg-bg-front-1 px-6 py-4"> 12 + <div className="flex items-center justify-between"> 13 + <div className="flex items-center gap-4"> 14 + <span className="text-text-secondary text-sm">Mon 3</span> 15 + <span className="text-text-secondary text-sm">Tue 4</span> 16 + <span className="text-text-primary text-sm font-semibold">Wed 5</span> 17 + <span className="text-text-secondary text-sm">Thu 6</span> 18 + <span className="text-text-secondary text-sm">Fri 7</span> 19 + </div> 20 + <div className="flex items-center gap-2"> 21 + <button className="px-3 py-1 text-xs rounded-full bg-bg-front-2 text-text-primary border border-border-1"> 22 + All 23 + </button> 24 + <button className="px-3 py-1 text-xs rounded-full text-text-secondary border border-border-1"> 25 + Events 26 + </button> 27 + <button className="px-3 py-1 text-xs rounded-full text-text-secondary border border-border-1"> 28 + Tasks 29 + </button> 30 + </div> 31 + </div> 32 + </div> 33 + 34 + {/* Content (normal flow, scrolls with page) */} 35 + <div className="flex flex-col gap-3"> 36 + {Array.from({ length: 20 }, (_, i) => ( 37 + <div 38 + key={i} 39 + className="rounded-xl bg-bg-front-2 border border-border-1 p-4" 40 + > 41 + <div className="flex items-center justify-between"> 42 + <div> 43 + <p className="text-text-primary text-sm font-medium"> 44 + Placeholder item {i + 1} 45 + </p> 46 + <p className="text-text-secondary text-xs mt-1"> 47 + Some description text here 48 + </p> 49 + </div> 50 + <span className="text-text-secondary text-xs">9:00 AM</span> 51 + </div> 52 + </div> 53 + ))} 54 + </div> 55 + </> 56 + ) 57 + }
+4 -3
src/routes/layout.css src/styles.css
··· 1 - @import 'tailwindcss'; 2 - @plugin '@tailwindcss/forms'; 3 - @plugin '@tailwindcss/typography'; 1 + @import "tailwindcss"; 2 + @plugin "@tailwindcss/forms"; 3 + @plugin "@tailwindcss/typography"; 4 4 5 5 body { 6 6 background-color: var(--color-bg-page); 7 + position: relative; /* Base UI iOS Safari fix */ 7 8 } 8 9 9 10 @theme {
-13
src/routes/page.svelte.spec.ts
··· 1 - import { page } from 'vitest/browser'; 2 - import { describe, expect, it } from 'vitest'; 3 - import { render } from 'vitest-browser-svelte'; 4 - import Page from './+page.svelte'; 5 - 6 - describe('/+page.svelte', () => { 7 - it('should render h1', async () => { 8 - render(Page); 9 - 10 - const heading = page.getByRole('heading', { level: 1 }); 11 - await expect.element(heading).toBeInTheDocument(); 12 - }); 13 - });
-3
static/robots.txt
··· 1 - # allow crawling everything by default 2 - User-agent: * 3 - Disallow:
-6
svelte.config.js
··· 1 - import adapter from '@sveltejs/adapter-cloudflare'; 2 - 3 - /** @type {import('@sveltejs/kit').Config} */ 4 - const config = { kit: { adapter: adapter() } }; 5 - 6 - export default config;
+5 -11
tsconfig.json
··· 1 1 { 2 - "extends": "./.svelte-kit/tsconfig.json", 3 2 "compilerOptions": { 4 - "rewriteRelativeImportExtensions": true, 5 - "allowJs": true, 6 - "checkJs": true, 7 - "esModuleInterop": true, 8 - "forceConsistentCasingInFileNames": true, 9 - "resolveJsonModule": true, 3 + "jsx": "react-jsx", 4 + "moduleResolution": "Bundler", 5 + "module": "ESNext", 6 + "target": "ES2022", 10 7 "skipLibCheck": true, 11 - "sourceMap": true, 12 - "strict": true, 13 - "moduleResolution": "bundler", 14 - "types": ["./worker-configuration.d.ts"] 8 + "strictNullChecks": true 15 9 } 16 10 }
+13 -35
vite.config.ts
··· 1 - import devtoolsJson from 'vite-plugin-devtools-json'; 2 - import tailwindcss from '@tailwindcss/vite'; 3 - import { defineConfig } from 'vitest/config'; 4 - import { playwright } from '@vitest/browser-playwright'; 5 - import { sveltekit } from '@sveltejs/kit/vite'; 1 + import { defineConfig } from 'vite' 2 + import tsConfigPaths from 'vite-tsconfig-paths' 3 + import { tanstackStart } from '@tanstack/react-start/plugin/vite' 4 + import tailwindcss from '@tailwindcss/vite' 5 + import viteReact from '@vitejs/plugin-react' 6 6 7 7 export default defineConfig({ 8 - plugins: [tailwindcss(), sveltekit(), devtoolsJson()], 9 - test: { 10 - expect: { requireAssertions: true }, 11 - projects: [ 12 - { 13 - extends: './vite.config.ts', 14 - test: { 15 - name: 'client', 16 - browser: { 17 - enabled: true, 18 - provider: playwright(), 19 - instances: [{ browser: 'chromium', headless: true }] 20 - }, 21 - include: ['src/**/*.svelte.{test,spec}.{js,ts}'], 22 - exclude: ['src/lib/server/**'] 23 - } 24 - }, 25 - 26 - { 27 - extends: './vite.config.ts', 28 - test: { 29 - name: 'server', 30 - environment: 'node', 31 - include: ['src/**/*.{test,spec}.{js,ts}'], 32 - exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'] 33 - } 34 - } 35 - ] 36 - } 37 - }); 8 + server: { port: 3000 }, 9 + plugins: [ 10 + tailwindcss(), 11 + tsConfigPaths(), 12 + tanstackStart(), 13 + viteReact(), // must come after tanstackStart 14 + ], 15 + })
-13
wrangler.jsonc
··· 1 - { 2 - "$schema": "./node_modules/wrangler/config-schema.json", 3 - "name": "morgenblau", 4 - "compatibility_date": "2026-02-04", 5 - "compatibility_flags": ["nodejs_als"], 6 - "main": ".svelte-kit/cloudflare/_worker.js", 7 - "assets": { 8 - "binding": "ASSETS", 9 - "directory": ".svelte-kit/cloudflare" 10 - }, 11 - "workers_dev": true, 12 - "preview_urls": true 13 - }