Skip to content

Commit 3bf7280

Browse files
committed
chore: simplify guardrails and clear lint warnings
1 parent 6c0dbf0 commit 3bf7280

33 files changed

Lines changed: 433 additions & 237 deletions

docs/source-code-audit-2026-06-23.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ These are quick, high-leverage patches because later fixes can reuse them instea
1717
- [done 2026-06-24: `pnpm run test:unit`, `pnpm test`] Make `pnpm test` honest about TypeScript tests, or split script names so validation expectations are clear.
1818
- [done 2026-06-24: `pnpm run test:tsc`, `pnpm run test:lint`] Default non-submit buttons/select triggers to `type="button"`, fix the shared pagination label/id contract, and clean up tooltip child prop merging.
1919
- [done 2026-06-24: `pnpm run test:unit`, `pnpm run test:tsc`, `pnpm run test:lint`] Add tiny helpers for response filenames, package slug encoding, and row-local ids.
20-
- Add tiny helpers for external URL normalization, internal route classification, and guarded storage.
21-
- Fix custom `useMutation` stale callbacks, clipboard/copied-state timers, docs sidebar timers, popup blocked-state handling, and simple page-visibility/reduced-motion helpers.
22-
- Normalize easy static data contracts: maintainer bare-domain URLs, Scarf id validation, public-library selector, partner analytics seed buckets, blog image URLs, host cache purge response policy.
20+
- [done 2026-06-24: `pnpm test`, 60-route local smoke] Add tiny helpers for external URL normalization, internal route classification, and guarded storage.
21+
- [done 2026-06-24: `pnpm test`, 60-route local smoke] Share low-risk clipboard/copy-state timers and auth popup centering.
22+
- Fix custom `useMutation` stale callbacks, docs sidebar timers, remaining popup blocked-state handling, and remaining page-visibility/reduced-motion call sites.
23+
- [done 2026-06-24: `pnpm test`, 60-route local smoke] Normalize maintainer bare-domain URLs, add Scarf/social URL contract tests, and centralize the public-library selector.
24+
- Normalize partner analytics seed buckets, blog image URLs, and host cache purge response policy.
2325

2426
### 2. One-off cleanup that lowers noise
2527

@@ -94,6 +96,7 @@ Treat these as tracked initiatives, not normal cleanup PRs.
9496
- 2026-06-24: Added shared response filename/content-disposition helpers, package route slug encoding helpers, and row-local id helpers; wired them into builder/docs downloads, Intent registry links, and moderation note inputs.
9597
- 2026-06-24: Hardened shared UI primitives: defaulted shop buttons and shared select triggers to non-submit buttons, gave pagination a unique page-size label/id pair plus non-submit controls, and made Tooltip merge trigger handlers/refs without `any`.
9698
- 2026-06-24: Added `test:unit` and wired it into `pnpm test` so existing TypeScript assertion tests run in the default validation path.
99+
- 2026-06-24: Added guarded browser storage/effect helpers, migrated low-risk clipboard/timer/popup/storage/reduced-motion call sites, centralized public-library selection, normalized maintainer URLs, and added static data contract tests.
97100
- 2026-06-23: Audit created and ordered.
98101

99102
## Highest Priority Findings
@@ -2185,7 +2188,7 @@ This is a strong candidate for a reusable TanStack docs-site cache helper or at
21852188

21862189
### Good pattern: slim data modules with explicit heavy-data escape hatches
21872190

2188-
`src/libraries/libraries.ts:1-2` keeps base library data lightweight, while `src/libraries/index.tsx:34-44` explicitly avoids re-exporting extended per-library landing-page projects that contain React nodes and heavier assets. This is the right shape for global navigation/docs data: a slim default import path plus opt-in heavy modules for routes that actually render the full experience.
2191+
`src/libraries/libraries.ts:1-2` keeps base library data lightweight, while `src/libraries/index.tsx:34-44` explicitly avoids re-exporting extended per-library landing-page projects that contain React nodes and heavier assets. `publicLibraries` and `isPublicLibrary` now give navigation/search/API callers one shared public catalog boundary. This is the right shape for global navigation/docs data: a slim default import path plus opt-in heavy modules for routes that actually render the full experience.
21892192

21902193
### Good pattern: lazy activation for expensive navbar features
21912194

@@ -2208,6 +2211,7 @@ This is a strong candidate for a reusable TanStack docs-site cache helper or at
22082211
- Builder definition schema shared between client UI, validate, compile, download, deploy, and MCP.
22092212
- A deploy-dialog controller shared by builder-generated projects and docs example deployment.
22102213
- React/browser effect helpers for timed flags, popup polling, clipboard writes, guarded storage, outside-click behavior, and drag-resize listeners.
2214+
- Static data contract tests for public catalogs, maintainer/social URLs, package metadata IDs, and other small hand-authored data tables.
22112215
- An OAuth PKCE helper with atomic code consumption patterns for Drizzle/Postgres.
22122216
- A first-party OAuth callback helper that validates provider params, clears transient cookies on every exit, and normalizes provider token/profile responses.
22132217
- A React Query query-options linter/helper that asserts query keys include every query-function input.

src/auth/client.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77

88
import type { OAuthProvider } from './types'
9+
import { openCenteredPopupWindow } from '~/utils/browser-effects'
910

1011
// ============================================================================
1112
// Auth Client
@@ -36,15 +37,13 @@ export const authClient = {
3637
* Initiate OAuth sign-in in a popup window (for modal-based login)
3738
*/
3839
socialPopup: ({ provider }: { provider: OAuthProvider }) => {
39-
const width = 500
40-
const height = 600
41-
const left = window.screenX + (window.outerWidth - width) / 2
42-
const top = window.screenY + (window.outerHeight - height) / 2
43-
return window.open(
44-
`/auth/${provider}/start?popup=true`,
45-
'tanstack-oauth',
46-
`width=${width},height=${height},left=${left},top=${top},popup=yes`,
47-
)
40+
return openCenteredPopupWindow({
41+
url: `/auth/${provider}/start?popup=true`,
42+
target: 'tanstack-oauth',
43+
width: 500,
44+
height: 600,
45+
features: ['popup=yes'],
46+
})
4847
},
4948
},
5049

src/components/BrandContextMenu.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
DropdownItem,
1010
DropdownSeparator,
1111
} from './Dropdown'
12+
import { copyTextToClipboard } from '~/utils/browser-effects'
1213

1314
interface BrandContextMenuProps extends React.HTMLAttributes<HTMLDivElement> {
1415
children: React.ReactNode
@@ -32,7 +33,7 @@ export function BrandContextMenu({ children, ...rest }: BrandContextMenuProps) {
3233

3334
const copyText = async (text: string) => {
3435
try {
35-
await navigator.clipboard.writeText(text)
36+
await copyTextToClipboard(text)
3637
notify(
3738
<div>
3839
<div className="font-medium">Copied to clipboard</div>
Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,16 @@
11
'use client'
2-
import { type MouseEventHandler, useEffect, useRef, useState } from 'react'
2+
import { type MouseEventHandler } from 'react'
3+
import { useTemporaryFlag } from '~/utils/browser-effects'
34

45
export function useCopyButton(
56
onCopy: () => void | Promise<void>,
67
): [checked: boolean, onClick: MouseEventHandler] {
7-
const [checked, setChecked] = useState(false)
8-
const timeoutRef = useRef<number | null>(null)
8+
const copied = useTemporaryFlag(1500)
99

1010
const onClick: MouseEventHandler = async () => {
11-
if (timeoutRef.current) window.clearTimeout(timeoutRef.current)
12-
const res = Promise.resolve(onCopy())
13-
14-
void res.then(() => {
15-
setChecked(true)
16-
timeoutRef.current = window.setTimeout(() => {
17-
setChecked(false)
18-
}, 1500)
19-
})
11+
await onCopy()
12+
copied.trigger()
2013
}
2114

22-
// avoid updates after being unmounted
23-
useEffect(() => {
24-
return () => {
25-
if (timeoutRef.current) window.clearTimeout(timeoutRef.current)
26-
}
27-
}, [])
28-
29-
return [checked, onClick]
15+
return [copied.active, onClick]
3016
}

src/components/CopyPageDropdown.tsx

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ import {
1111
DropdownItem,
1212
} from './Dropdown'
1313
import { getPackageManager } from '~/utils/markdown/installCommand'
14+
import {
15+
copyTextToClipboard,
16+
openPopupWindow,
17+
useTemporaryFlag,
18+
} from '~/utils/browser-effects'
19+
import { getLocalStorageItem } from '~/utils/browser-storage'
1420

1521
// Markdown icon component matching the screenshot
1622
function MarkdownIcon({ className }: { className?: string }) {
@@ -110,25 +116,26 @@ export function CopyPageDropdown({
110116
label = 'Copy page',
111117
}: CopyPageDropdownProps = {}) {
112118
const [open, setOpen] = React.useState(false)
113-
const [copied, setCopied] = React.useState(false)
119+
const copied = useTemporaryFlag()
114120
const { notify } = useToast()
115121

116122
// For docs pages in this repo, prefer the site's markdown endpoint so requests stay behind site caching.
117123
const pageMarkdownUrl = (() => {
118-
const base = `${typeof window !== 'undefined' ? window.location.origin : ''}${typeof window !== 'undefined' ? window.location.pathname.replace(/\/$/, '') : ''}.md`
119-
const params =
120-
typeof window !== 'undefined'
121-
? new URLSearchParams(window.location.search)
122-
: new URLSearchParams()
124+
if (typeof window === 'undefined') {
125+
return '.md'
126+
}
127+
128+
const base = `${window.location.origin}${window.location.pathname.replace(/\/$/, '')}.md`
129+
const params = new URLSearchParams(window.location.search)
123130
if (currentFramework) {
124131
params.set('framework', currentFramework)
125132
}
133+
126134
// Read package manager from localStorage (same key as PackageManagerTabs)
127-
if (typeof localStorage !== 'undefined') {
128-
const pm = localStorage.getItem('packageManager')
129-
const validPm = getPackageManager(pm)
130-
params.set('pm', validPm)
131-
}
135+
const pm = getLocalStorageItem('packageManager')
136+
const validPm = getPackageManager(pm)
137+
params.set('pm', validPm)
138+
132139
const queryString = params.toString()
133140
return queryString ? `${base}?${queryString}` : base
134141
})()
@@ -142,9 +149,8 @@ export function CopyPageDropdown({
142149

143150
const handleCopyPage = async () => {
144151
if (rawContent) {
145-
await navigator.clipboard.writeText(rawContent)
146-
setCopied(true)
147-
setTimeout(() => setCopied(false), 2000)
152+
await copyTextToClipboard(rawContent)
153+
copied.trigger()
148154
notify(
149155
<div>
150156
<div className="font-medium">Copied to clipboard</div>
@@ -157,9 +163,8 @@ export function CopyPageDropdown({
157163
const cached = markdownCache.get(urlToFetch)
158164

159165
const copyContent = async (content: string, source: string) => {
160-
await navigator.clipboard.writeText(content)
161-
setCopied(true)
162-
setTimeout(() => setCopied(false), 2000)
166+
await copyTextToClipboard(content)
167+
copied.trigger()
163168
notify(
164169
<div>
165170
<div className="font-medium">Copied to clipboard</div>
@@ -194,9 +199,8 @@ export function CopyPageDropdown({
194199
await copyContent(pageContent, 'Copied rendered page content')
195200
} else {
196201
// Last resort: copy the URL
197-
await navigator.clipboard.writeText(window.location.href)
198-
setCopied(true)
199-
setTimeout(() => setCopied(false), 2000)
202+
await copyTextToClipboard(window.location.href)
203+
copied.trigger()
200204
notify(
201205
<div>
202206
<div className="font-medium">Copied to clipboard</div>
@@ -211,33 +215,32 @@ export function CopyPageDropdown({
211215

212216
const handleViewMarkdown = () => {
213217
const url = sourceMarkdownUrl
214-
window.open(url, '_blank')
218+
openPopupWindow(url)
215219
}
216220

217221
const handleOpenInClaude = () => {
218222
const pageUrl = window.location.href
219223
const prompt = encodeURIComponent(
220224
`Read from this URL: ${pageUrl} and explain it to me`,
221225
)
222-
window.open(`https://claude.ai/new?q=${prompt}`, '_blank')
226+
openPopupWindow(`https://claude.ai/new?q=${prompt}`)
223227
}
224228

225229
const handleOpenInChatGPT = () => {
226230
const pageUrl = window.location.href
227231
const prompt = encodeURIComponent(
228232
`Read from this URL: ${pageUrl} and explain it to me`,
229233
)
230-
window.open(`https://chatgpt.com/?q=${prompt}`, '_blank')
234+
openPopupWindow(`https://chatgpt.com/?q=${prompt}`)
231235
}
232236

233237
const handleOpenInCursor = () => {
234238
const pageUrl = window.location.href
235239
const prompt = `Read from this URL:\n${pageUrl}\nand explain it to me`
236-
window.open(
240+
openPopupWindow(
237241
`cursor://anysphere.cursor-deeplink/prompt?text=${encodeURIComponent(
238242
prompt,
239243
)}`,
240-
'_blank',
241244
)
242245
}
243246

@@ -246,7 +249,7 @@ export function CopyPageDropdown({
246249
const prompt = encodeURIComponent(
247250
`Read from this URL: ${pageUrl} and explain it to me`,
248251
)
249-
window.open(`https://t3.chat/new?q=${prompt}`, '_blank')
252+
openPopupWindow(`https://t3.chat/new?q=${prompt}`)
250253
}
251254

252255
const menuItems = [
@@ -289,13 +292,14 @@ export function CopyPageDropdown({
289292
return (
290293
<ButtonGroup>
291294
<Button
295+
type="button"
292296
variant="ghost"
293297
size="xs"
294298
rounded="none"
295299
className="border-0"
296300
onClick={handleCopyPage}
297301
>
298-
{copied ? (
302+
{copied.active ? (
299303
<>
300304
<Check className="w-3 h-3" />
301305
Copied!
@@ -310,6 +314,7 @@ export function CopyPageDropdown({
310314
<Dropdown open={open} onOpenChange={setOpen}>
311315
<DropdownTrigger>
312316
<Button
317+
type="button"
313318
variant="ghost"
314319
size="xs"
315320
rounded="none"

src/components/FrameworkCard.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
getFrameworkDocsHash,
1111
getFrameworkDocsPath,
1212
} from '~/libraries/frameworkSupport'
13+
import { copyTextToClipboard } from '~/utils/browser-effects'
1314

1415
export function FrameworkCard({
1516
framework,
@@ -28,7 +29,7 @@ export function FrameworkCard({
2829
}) {
2930
const { notify } = useToast()
3031
const [copied, onCopyClick] = useCopyButton(async () => {
31-
await navigator.clipboard.writeText(packageName)
32+
await copyTextToClipboard(packageName)
3233
notify(
3334
<div>
3435
<div className="font-medium">Copied package name</div>

src/components/FrameworkSelect.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import {
1010
useCurrentUserQuery,
1111
} from '~/hooks/useCurrentUser'
1212
import { updateLastUsedFramework } from '~/utils/users.functions'
13+
import {
14+
getLocalStorageItem,
15+
setLocalStorageItem,
16+
} from '~/utils/browser-storage'
1317

1418
function persistFrameworkToServer(framework: string) {
1519
void updateLastUsedFramework({ data: { framework } }).catch(() => {
@@ -51,12 +55,9 @@ export const useLocalCurrentFramework = create<{
5155
currentFramework?: string
5256
setCurrentFramework: (framework: string) => void
5357
}>((set) => ({
54-
currentFramework:
55-
typeof document !== 'undefined'
56-
? localStorage.getItem('framework') || undefined
57-
: undefined,
58+
currentFramework: getLocalStorageItem('framework') || undefined,
5859
setCurrentFramework: (framework: string) => {
59-
localStorage.setItem('framework', framework)
60+
setLocalStorageItem('framework', framework)
6061
set({ currentFramework: framework })
6162
},
6263
}))
@@ -66,8 +67,7 @@ export const useLocalCurrentFramework = create<{
6667
* Safe to call during SSR (returns undefined).
6768
*/
6869
export function getStoredFrameworkPreference(): string | undefined {
69-
if (typeof window === 'undefined') return undefined
70-
return localStorage.getItem('framework') || undefined
70+
return getLocalStorageItem('framework') || undefined
7171
}
7272

7373
/**

src/components/LibrariesWidget.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import { Link } from '@tanstack/react-router'
2-
import { libraries } from '~/libraries'
2+
import { publicLibraries } from '~/libraries'
33

4-
const featuredLibraries = libraries
5-
.filter((library) => library.visible !== false)
6-
.slice(0, 8)
7-
.map((library) => ({
8-
id: library.id,
9-
name: library.name,
10-
}))
4+
const featuredLibraries = publicLibraries.slice(0, 8).map((library) => ({
5+
id: library.id,
6+
name: library.name,
7+
}))
118

129
export function LibrariesWidget() {
1310
return (

src/components/LibraryLayout.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,19 +1263,12 @@ export function LibraryLayout({
12631263
const docsTabs = (
12641264
<div className="sticky top-[var(--navbar-height)] z-30 border-b border-gray-500/20 bg-white/90 dark:bg-black/80 backdrop-blur-lg">
12651265
<div className="flex items-stretch">
1266-
<label
1267-
htmlFor={mobileMenuToggleId}
1268-
role="button"
1269-
tabIndex={0}
1266+
<button
1267+
type="button"
12701268
aria-label="Documentation menu"
12711269
aria-expanded={mobileMenuOpen ? true : undefined}
12721270
aria-controls="docs-mobile-menu"
1273-
onKeyDown={(event) => {
1274-
if (event.key === 'Enter' || event.key === ' ') {
1275-
event.preventDefault()
1276-
setMobileMenuOpen((prev) => !prev)
1277-
}
1278-
}}
1271+
onClick={() => setMobileMenuOpen((prev) => !prev)}
12791272
data-docs-mobile-trigger
12801273
className="min-[900px]:hidden flex items-center gap-1.5 shrink-0 px-3 border-r border-gray-500/20 text-slate-600 dark:text-slate-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-current"
12811274
>
@@ -1284,7 +1277,7 @@ export function LibraryLayout({
12841277
<span className="text-xs font-medium max-[479.98px]:sr-only">
12851278
Menu
12861279
</span>
1287-
</label>
1280+
</button>
12881281
<button
12891282
ref={largeMenuTriggerRef}
12901283
type="button"

0 commit comments

Comments
 (0)