Files
tailscale-custom/.agent/.shared/ui-ux-pro-max/data/react-performance.csv
T
huanld 2fb067ecbf
checklocks / checklocks (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled
natlab-integrationtest / natlab-integrationtest (push) Has been cancelled
CI / gomod-cache (push) Has been cancelled
CI / race-root-integration (1/4) (push) Has been cancelled
CI / race-root-integration (2/4) (push) Has been cancelled
CI / race-root-integration (3/4) (push) Has been cancelled
CI / race-root-integration (4/4) (push) Has been cancelled
CI / test (-race, amd64, 1/3) (push) Has been cancelled
CI / test (-race, amd64, 2/3) (push) Has been cancelled
CI / test (-race, amd64, 3/3) (push) Has been cancelled
CI / test (386) (push) Has been cancelled
CI / test (amd64) (push) Has been cancelled
CI / Windows (benchmarks) (push) Has been cancelled
CI / Windows (1/2) (push) Has been cancelled
CI / Windows (2/2) (push) Has been cancelled
CI / macos (push) Has been cancelled
CI / privileged (push) Has been cancelled
CI / vm (push) Has been cancelled
CI / cross (386, linux) (push) Has been cancelled
CI / cross (amd64, darwin) (push) Has been cancelled
CI / cross (amd64, freebsd) (push) Has been cancelled
CI / cross (amd64, openbsd) (push) Has been cancelled
CI / cross (amd64, windows) (push) Has been cancelled
CI / cross (arm, 5, linux) (push) Has been cancelled
CI / cross (arm, 7, linux) (push) Has been cancelled
CI / cross (arm64, darwin) (push) Has been cancelled
CI / cross (arm64, linux) (push) Has been cancelled
CI / cross (arm64, windows) (push) Has been cancelled
CI / cross (loong64, linux) (push) Has been cancelled
CI / ios (push) Has been cancelled
CI / crossmin (amd64, illumos) (push) Has been cancelled
CI / crossmin (amd64, plan9) (push) Has been cancelled
CI / crossmin (amd64, solaris) (push) Has been cancelled
CI / crossmin (ppc64, aix) (push) Has been cancelled
CI / android (push) Has been cancelled
CI / wasm (push) Has been cancelled
CI / tailscale_go (push) Has been cancelled
CI / fuzz (push) Has been cancelled
CI / depaware (push) Has been cancelled
CI / go_generate (push) Has been cancelled
CI / make_tidy (push) Has been cancelled
CI / licenses (push) Has been cancelled
CI / staticcheck (macOS) (push) Has been cancelled
CI / staticcheck (Linux) (push) Has been cancelled
CI / staticcheck (Windows) (push) Has been cancelled
CI / staticcheck (Portable (1/4)) (push) Has been cancelled
CI / staticcheck (Portable (2/4)) (push) Has been cancelled
CI / staticcheck (Portable (3/4)) (push) Has been cancelled
CI / staticcheck (Portable (4/4)) (push) Has been cancelled
CI / notify_slack (push) Has been cancelled
CI / merge_blocker (push) Has been cancelled
CI / check_mergeability_strict (push) Has been cancelled
CI / check_mergeability (push) Has been cancelled
Dockerfile build / deploy (push) Has been cancelled
test installer.sh / test (curl, alpine:3.21) (push) Has been cancelled
test installer.sh / test (curl, alpine:edge) (push) Has been cancelled
test installer.sh / test (curl, alpine:latest) (push) Has been cancelled
test installer.sh / test (curl, amazonlinux:latest) (push) Has been cancelled
test installer.sh / test (curl, archlinux:latest) (push) Has been cancelled
test installer.sh / test (curl, debian:oldstable-slim) (push) Has been cancelled
test installer.sh / test (curl, debian:sid-slim) (push) Has been cancelled
test installer.sh / test (curl, debian:stable-slim, 1.80.0) (push) Has been cancelled
test installer.sh / test (curl, debian:testing-slim) (push) Has been cancelled
test installer.sh / test (curl, elementary/docker:stable) (push) Has been cancelled
test installer.sh / test (curl, elementary/docker:unstable) (push) Has been cancelled
test installer.sh / test (curl, fedora:latest, 1.80.0) (push) Has been cancelled
test installer.sh / test (curl, kalilinux/kali-dev) (push) Has been cancelled
test installer.sh / test (curl, kalilinux/kali-rolling) (push) Has been cancelled
test installer.sh / test (curl, opensuse/leap:latest) (push) Has been cancelled
test installer.sh / test (curl, opensuse/tumbleweed:latest) (push) Has been cancelled
test installer.sh / test (curl, oraclelinux:8) (push) Has been cancelled
test installer.sh / test (curl, oraclelinux:9) (push) Has been cancelled
test installer.sh / test (curl, parrotsec/core:latest) (push) Has been cancelled
test installer.sh / test (curl, rockylinux:8.7) (push) Has been cancelled
test installer.sh / test (curl, rockylinux:9) (push) Has been cancelled
test installer.sh / test (curl, ubuntu:20.04) (push) Has been cancelled
test installer.sh / test (curl, ubuntu:22.04) (push) Has been cancelled
test installer.sh / test (curl, ubuntu:24.04, 1.80.0) (push) Has been cancelled
test installer.sh / test (wget, debian:oldstable-slim) (push) Has been cancelled
test installer.sh / test (wget, debian:sid-slim) (push) Has been cancelled
update-flake / update-flake (push) Has been cancelled
tailscale.com/cmd/vet / vet (push) Has been cancelled
test installer.sh / notify-slack (push) Has been cancelled
feat: security hardening, production roadmap, admin panel v1
Client security fixes (cmd/tailscale-tray/main.go):
- SSRF protection in Add Server dialog (validateControlURL): reject
  private/loopback/link-local/cloud-metadata IPs via DNS resolution
- RCE gate on AuthURL/BrowseToURL exec paths (validateAuthURL)
- Sanitized URL logging (sanitizeURLForLog drops query auth tokens)
- Error handling on exec.Command with user-facing showError()

Admin panel security (web-admin):
- Bcrypt password hashing (replaces SHA256)
- Rate limiting: 5 failed logins → 15-min lockout
- Session + login attempt cleanup goroutine (hourly)
- url.QueryEscape / encodeURIComponent for all API params
- Fail-hard startup when no TLS and non-loopback bind
- ADMIN_PASSWORD required (no default), password min 12 chars
- Username regex whitelist

Installer hardening (Setup.wxs):
- util:PermissionEx restricts SCM access: only Administrators +
  SYSTEM can start/stop/reconfigure service. Authenticated Users
  limited to QueryStatus/QueryConfig/Interrogate
- Vital="yes" on ServiceInstall

Docs & roadmap:
- PRODUCTION_ROADMAP.md: 5-milestone plan (security + features +
  distribution + ops) with granular tasks, effort, done-when
- CLIENT_SECURITY_AUDIT.md, SECURITY_FIXES.md, DEPLOYMENT.md
- AI assistant rules (.cursorrules, .antigravityrules, etc.)

Build & distribution:
- build-msi.ps1, deploy-and-sign.ps1, sign-release.ps1
- redeploy.ps1, tray-deploy.ps1, test-msi.ps1
- installer/msi/ alternative WXS setup
- Restored .github/workflows/ removed in mirror cleanup

.gitignore hardened: *.pfx, *.p12, *.key, *.pem, .env*
2026-04-22 15:18:11 +07:00

14 KiB

1NoCategoryIssueKeywordsPlatformDescriptionDoDon'tCode Example GoodCode Example BadSeverity
21Async WaterfallDefer Awaitasync await defer branchReact/Next.jsMove await into branches where actually used to avoid blocking unused code pathsMove await operations into branches where they're neededAwait at top of function blocking all branchesif (skip) return { skipped: true }; const data = await fetch()const data = await fetch(); if (skip) return { skipped: true }Critical
32Async WaterfallPromise.all Parallelpromise all parallel concurrentReact/Next.jsExecute independent async operations concurrently using Promise.all()Use Promise.all() for independent operationsSequential await for independent operationsconst [user, posts] = await Promise.all([fetchUser(), fetchPosts()])const user = await fetchUser(); const posts = await fetchPosts()Critical
43Async WaterfallDependency Parallelizationbetter-all dependency parallelReact/Next.jsUse better-all for operations with partial dependencies to maximize parallelismUse better-all to start each task at earliest possible momentWait for unrelated data before starting dependent fetchawait all({ user() {}, config() {}, profile() { return fetch((await this.$.user).id) } })const [user, config] = await Promise.all([...]); const profile = await fetchProfile(user.id)Critical
54Async WaterfallAPI Route Optimizationapi route waterfall promiseReact/Next.jsIn API routes start independent operations immediately even if not awaited yetStart promises early and await lateSequential awaits in API handlersconst sessionP = auth(); const configP = fetchConfig(); const session = await sessionPconst session = await auth(); const config = await fetchConfig()Critical
65Async WaterfallSuspense Boundariessuspense streaming boundaryReact/Next.jsUse Suspense to show wrapper UI faster while data loadsWrap async components in Suspense boundariesAwait data blocking entire page render<Suspense fallback={<Skeleton />}><DataDisplay /></Suspense>const data = await fetchData(); return <DataDisplay data={data} />High
76Bundle SizeBarrel Importsbarrel import direct pathReact/Next.jsImport directly from source files instead of barrel files to avoid loading unused modulesImport directly from source pathImport from barrel/index filesimport Check from 'lucide-react/dist/esm/icons/check'import { Check } from 'lucide-react'Critical
87Bundle SizeDynamic Importsdynamic import lazy nextReact/Next.jsUse next/dynamic to lazy-load large components not needed on initial renderUse dynamic() for heavy componentsImport heavy components at top levelconst Monaco = dynamic(() => import('./monaco'), { ssr: false })import { MonacoEditor } from './monaco-editor'Critical
98Bundle SizeDefer Third Partyanalytics defer third-partyReact/Next.jsLoad analytics and logging after hydration since they don't block interactionLoad non-critical scripts after hydrationInclude analytics in main bundleconst Analytics = dynamic(() => import('@vercel/analytics'), { ssr: false })import { Analytics } from '@vercel/analytics/react'Medium
109Bundle SizeConditional Loadingconditional module lazyReact/Next.jsLoad large data or modules only when a feature is activatedDynamic import when feature enabledImport large modules unconditionallyuseEffect(() => { if (enabled) import('./heavy.js') }, [enabled])import { heavyData } from './heavy.js'High
1110Bundle SizePreload Intentpreload hover focus intentReact/Next.jsPreload heavy bundles on hover/focus before they're neededPreload on user intent signalsLoad only on clickonMouseEnter={() => import('./editor')}onClick={() => import('./editor')}Medium
1211ServerReact.cache Dedupreact cache deduplicate requestReact/Next.jsUse React.cache() for server-side request deduplication within single requestWrap data fetchers with cache()Fetch same data multiple times in treeexport const getUser = cache(async () => await db.user.find())export async function getUser() { return await db.user.find() }Medium
1312ServerLRU Cache Cross-Requestlru cache cross requestReact/Next.jsUse LRU cache for data shared across sequential requestsUse LRU for cross-request cachingRefetch same data on every requestconst cache = new LRUCache({ max: 1000, ttl: 5*60*1000 })Always fetch from databaseHigh
1413ServerMinimize Serializationserialization rsc boundaryReact/Next.jsOnly pass fields that client actually uses across RSC boundariesPass only needed fields to client componentsPass entire objects to client<Profile name={user.name} /><Profile user={user} /> // 50 fields serializedHigh
1514ServerParallel Fetchingparallel fetch component compositionReact/Next.jsRestructure components to parallelize data fetching in RSCUse component composition for parallel fetchesSequential fetches in parent component<Header /><Sidebar /> // both fetch in parallelconst header = await fetchHeader(); return <><div>{header}</div><Sidebar /></>Critical
1615ServerAfter Non-blockingafter non-blocking loggingReact/Next.jsUse Next.js after() to schedule work after response is sentUse after() for logging/analyticsBlock response for non-critical operationsafter(async () => { await logAction() }); return Response.json(data)await logAction(); return Response.json(data)Medium
1716ClientSWR Deduplicationswr dedup cache revalidateReact/Next.jsUse SWR for automatic request deduplication and cachingUse useSWR for client data fetchingManual fetch in useEffectconst { data } = useSWR('/api/users', fetcher)useEffect(() => { fetch('/api/users').then(setUsers) }, [])Medium-High
1817ClientEvent Listener Dedupevent listener deduplicate globalReact/Next.jsShare global event listeners across component instancesUse useSWRSubscription for shared listenersRegister listener per component instanceuseSWRSubscription('global-keydown', () => { window.addEventListener... })useEffect(() => { window.addEventListener('keydown', handler) }, [])Low
1918RerenderDefer State Readsstate read callback subscriptionReact/Next.jsDon't subscribe to state only used in callbacksRead state on-demand in callbacksSubscribe to state used only in handlersconst handleClick = () => { const params = new URLSearchParams(location.search) }const params = useSearchParams(); const handleClick = () => { params.get('ref') }Medium
2019RerenderMemoized Componentsmemo extract expensiveReact/Next.jsExtract expensive work into memoized components for early returnsExtract to memo() componentsCompute expensive values before early returnconst UserAvatar = memo(({ user }) => ...); if (loading) return <Skeleton />const avatar = useMemo(() => compute(user)); if (loading) return <Skeleton />Medium
2120RerenderNarrow Dependencieseffect dependency primitiveReact/Next.jsSpecify primitive dependencies instead of objects in effectsUse primitive values in dependency arraysUse object references as dependenciesuseEffect(() => { console.log(user.id) }, [user.id])useEffect(() => { console.log(user.id) }, [user])Low
2221RerenderDerived Statederived boolean subscriptionReact/Next.jsSubscribe to derived booleans instead of continuous valuesUse derived boolean stateSubscribe to continuous valuesconst isMobile = useMediaQuery('(max-width: 767px)')const width = useWindowWidth(); const isMobile = width < 768Medium
2322RerenderFunctional setStatefunctional setstate callbackReact/Next.jsUse functional setState updates for stable callbacks and no stale closuresUse functional form: setState(curr => ...)Reference state directly in setStatesetItems(curr => [...curr, newItem])setItems([...items, newItem]) // items in depsMedium
2423RerenderLazy State Initusestate lazy initializationReact/Next.jsPass function to useState for expensive initial valuesUse function form for expensive initCompute expensive value directlyuseState(() => buildSearchIndex(items))useState(buildSearchIndex(items)) // runs every renderMedium
2524RerenderTransitionsstarttransition non-urgentReact/Next.jsMark frequent non-urgent state updates as transitionsUse startTransition for non-urgent updatesBlock UI on every state changestartTransition(() => setScrollY(window.scrollY))setScrollY(window.scrollY) // blocks on every scrollMedium
2625RenderingSVG Animation Wrappersvg animation wrapper divReact/Next.jsWrap SVG in div and animate wrapper for hardware accelerationAnimate div wrapper around SVGAnimate SVG element directly<div class='animate-spin'><svg>...</svg></div><svg class='animate-spin'>...</svg>Low
2726RenderingContent Visibilitycontent-visibility autoReact/Next.jsApply content-visibility: auto to defer off-screen renderingUse content-visibility for long listsRender all list items immediately.item { content-visibility: auto; contain-intrinsic-size: 0 80px }Render 1000 items without optimizationHigh
2827RenderingHoist Static JSXhoist static jsx elementReact/Next.jsExtract static JSX outside components to avoid re-creationHoist static elements to module scopeCreate static elements inside componentsconst skeleton = <div class='animate-pulse' />; function C() { return skeleton }function C() { return <div class='animate-pulse' /> }Low
2928RenderingHydration No Flickerhydration mismatch flickerReact/Next.jsUse inline script to set client-only data before hydrationInject sync script for client-only valuesUse useEffect causing flash<script dangerouslySetInnerHTML={{ __html: 'el.className = localStorage.theme' }} />useEffect(() => setTheme(localStorage.theme), []) // flickersMedium
3029RenderingConditional Renderconditional render ternaryReact/Next.jsUse ternary instead of && when condition can be 0 or NaNUse explicit ternary for conditionalsUse && with potentially falsy numbers{count > 0 ? <Badge>{count}</Badge> : null}{count && <Badge>{count}</Badge>} // renders '0'Low
3130RenderingActivity Componentactivity show hide preserveReact/Next.jsUse Activity component to preserve state/DOM for toggled componentsUse Activity for expensive toggle componentsUnmount/remount on visibility toggle<Activity mode={isOpen ? 'visible' : 'hidden'}><Menu /></Activity>{isOpen && <Menu />} // loses stateMedium
3231JS PerfBatch DOM CSSbatch dom css reflowReact/Next.jsGroup CSS changes via classes or cssText to minimize reflowsUse class toggle or cssTextChange styles one property at a timeelement.classList.add('highlighted')el.style.width='100px'; el.style.height='200px'Medium
3332JS PerfIndex Map Lookupmap index lookup findReact/Next.jsBuild Map for repeated lookups instead of multiple .find() callsBuild index Map for O(1) lookupsUse .find() in loopsconst byId = new Map(users.map(u => [u.id, u])); byId.get(id)users.find(u => u.id === order.userId) // O(n) each timeLow-Medium
3433JS PerfCache Property Accesscache property loopReact/Next.jsCache object property lookups in hot pathsCache values before loopsAccess nested properties in loopsconst val = obj.config.settings.value; for (...) process(val)for (...) process(obj.config.settings.value)Low-Medium
3534JS PerfCache Function Resultsmemoize cache functionReact/Next.jsUse module-level Map to cache repeated function resultsUse Map cache for repeated callsRecompute same values repeatedlyconst cache = new Map(); if (cache.has(x)) return cache.get(x)slugify(name) // called 100 times same inputMedium
3635JS PerfCache Storage APIlocalstorage cache readReact/Next.jsCache localStorage/sessionStorage reads in memoryCache storage reads in MapRead storage on every callif (!cache.has(key)) cache.set(key, localStorage.getItem(key))localStorage.getItem('theme') // every callLow-Medium
3736JS PerfCombine Iterationscombine filter map loopReact/Next.jsCombine multiple filter/map into single loopSingle loop for multiple categorizationsChain multiple filter() callsfor (u of users) { if (u.isAdmin) admins.push(u); if (u.isTester) testers.push(u) }users.filter(admin); users.filter(tester); users.filter(inactive)Low-Medium
3837JS PerfLength Check Firstlength check array compareReact/Next.jsCheck array lengths before expensive comparisonsEarly return if lengths differAlways run expensive comparisonif (a.length !== b.length) return true; // then comparea.sort().join() !== b.sort().join() // even when lengths differMedium-High
3938JS PerfEarly Returnearly return exit functionReact/Next.jsReturn early when result is determined to skip processingReturn immediately on first errorProcess all items then check errorsfor (u of users) { if (!u.email) return { error: 'Email required' } }let hasError; for (...) { if (!email) hasError=true }; if (hasError)...Low-Medium
4039JS PerfHoist RegExpregexp hoist moduleReact/Next.jsDon't create RegExp inside render - hoist or memoizeHoist RegExp to module scopeCreate RegExp every renderconst EMAIL_RE = /^[^@]+@[^@]+$/; function validate() { EMAIL_RE.test(x) }function C() { const re = new RegExp(pattern); re.test(x) }Low-Medium
4140JS PerfLoop Min Maxloop min max sortReact/Next.jsUse loop for min/max instead of sort - O(n) vs O(n log n)Single pass loop for min/maxSort array to find min/maxlet max = arr[0]; for (x of arr) if (x > max) max = xarr.sort((a,b) => b-a)[0] // O(n log n)Low
4241JS PerfSet Map Lookupsset map includes hasReact/Next.jsUse Set/Map for O(1) lookups instead of array.includes()Convert to Set for membership checksUse .includes() for repeated checksconst allowed = new Set(['a','b']); allowed.has(id)const allowed = ['a','b']; allowed.includes(id)Low-Medium
4342JS PerftoSorted Immutabletosorted sort immutableReact/Next.jsUse toSorted() instead of sort() to avoid mutating arraysUse toSorted() for immutabilityMutate arrays with sort()users.toSorted((a,b) => a.name.localeCompare(b.name))users.sort((a,b) => a.name.localeCompare(b.name)) // mutatesMedium-High
4443AdvancedEvent Handler Refsuseeffectevent ref handlerReact/Next.jsStore callbacks in refs for stable effect subscriptionsUse useEffectEvent for stable handlersRe-subscribe on every callback changeconst onEvent = useEffectEvent(handler); useEffect(() => { listen(onEvent) }, [])useEffect(() => { listen(handler) }, [handler]) // re-subscribesLow
4544AdvanceduseLatest Hookuselatest ref callbackReact/Next.jsAccess latest values in callbacks without adding to dependency arraysUse useLatest for fresh values in stable callbacksAdd callback to effect dependenciesconst cbRef = useLatest(cb); useEffect(() => { setTimeout(() => cbRef.current()) }, [])useEffect(() => { setTimeout(() => cb()) }, [cb]) // re-runsLow