Skip to content

Commit d37ffc1

Browse files
authored
Merge pull request #4856 from udecode/test/suite
2 parents f1ed09f + a160f0b commit d37ffc1

File tree

421 files changed

+12548
-20179
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

421 files changed

+12548
-20179
lines changed

.claude/.skiller.json

Lines changed: 150 additions & 78 deletions
Large diffs are not rendered by default.

.claude/AGENTS.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,30 @@
1515
- Dirty workspace: Never pause to ask about unrelated local changes. Continue work and ignore unrelated diffs.
1616
- Proactively use Skill(tdd) when it adds value; skip TDD for high-friction tests (hard setup or slow React/UI flows).
1717

18-
## Browser Testing
18+
## Skill Overrides
1919

20-
## Browser Testing
20+
When using the following skills, override the default behavior.
21+
22+
`planning-with-files`:
23+
24+
- Do not create `task_plan.md`, `findings.md`, or `progress.md` at repo root. Merge that content into one file under `.claude/docs/plans/`. Example: `.claude/docs/plans/2026-02-07-fix-schema.md`
25+
26+
`agent-browser`:
2127

2228
- Never close agent-browser
2329
- Use `--headed` only you failed to test and need manual input from human.
2430
- Port 3000 for main app
2531
- Use `agent-browser` instead of Do NOT use next-devtools `browser_eval` (overlaps with agent-browser)
26-
- Use `bun convex:logs` to watch the Convex logs
32+
- If `agent-browser` gets blocked or loops on the same step, stop and ask the user to unblock. After the unblock works:
33+
- [Add browser learning]
2734

28-
## Compound Engineering Overrides
35+
`workflows:*`:
2936

3037
- **Git:** Never git add, commit, push, or create PR unless the user explicitly asks.
3138
- **PR:** Before creating or updating a PR, run `bun check`. If it fails, stop and fix it or report the blocker. Do not open a PR with failing `bun check` unless the user explicitly says to.
3239
- **plan:** Include test-browser in acceptance criteria for browser features
3340
- **deepen-plan:** Context7 only when not covered by skills
3441
- **work:** UI tasks require test-browser BEFORE marking complete. Never guess.
35-
- **review:** Skip kieran-rails, dhh-rails, rails-turbo. Trust user input (internal). Keep simple.
3642

3743
## Commands
3844

.claude/commands/sync-testing-skill.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Scan package tests, update @.claude/rules/testing.mdc with new patterns. Follow writing-skills: DRY, ultra-concise, token-efficient.
1+
Scan package tests, update @.claude/skills/testing/testing.mdc with new patterns. Follow writing-skills: DRY, ultra-concise, token-efficient.
22

33
**Process:**
44

@@ -11,6 +11,7 @@ Scan package tests, update @.claude/rules/testing.mdc with new patterns. Follow
1111
5. Report: `Found X patterns → Updated Y sections`
1212

1313
**Critical:**
14+
1415
- Only actual codebase patterns. No theoretical examples. No duplication.
1516
- Test globals (`describe`, `it`, `expect`, etc.) are global via `tooling/config/global.d.ts` - NO imports needed
1617
- ONLY import `mock` and `spyOn` when used

.claude/docs/plans/2026-01-23-fix-vercel-caching-costs-plan.md

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ deepened: 2026-01-23
1313
**Sources:** Vercel React Best Practices, 3 reviewer agents (DHH, Kieran, Simplicity)
1414

1515
### Key Improvements
16+
1617
1. Expanded scope to include ALL locale-dependent files (18 files, not 3)
1718
2. Added Vercel-specific caching guidance (LRU vs React.cache)
1819
3. Fixed `generateStaticParams` issues for CN route
1920
4. Added client-side utility migrations
2021

2122
### Critical Findings from Reviewers
23+
2224
- Home page ALSO uses `searchParams` - must fix
2325
- `useLocale.ts` uses `useSearchParams()` - needs migration to `usePathname()`
2426
- Dual encoding (`/cn/...?locale=cn`) is redundant - remove query params entirely
@@ -32,17 +34,19 @@ Docs pages have **0% cache hit rate** causing $50+ Vercel overage. Every request
3234
## Problem Statement
3335

3436
**Symptoms from Vercel dashboard:**
37+
3538
- 0% cache hit rate on `/docs/[[...slug]]`
3639
- $22.84 Fluid Active CPU (178 hours)
3740
- $20.43 Fast Origin Transfer (338 GB)
3841
- 99.5% traffic from single region (iad1)
3942
- Spike started ~Jan 7
4043

4144
**Root Cause:**
45+
4246
```tsx
4347
// apps/www/src/app/(app)/docs/[[...slug]]/page.tsx:35-37
4448
type DocPageProps = {
45-
searchParams: Promise<{ locale: string }>; // <-- THIS FORCES DYNAMIC RENDERING
49+
searchParams: Promise<{ locale: string }>; // <-- THIS FORCES DYNAMIC RENDERING
4650
};
4751
```
4852

@@ -55,6 +59,7 @@ From Vercel's React Best Practices guide (Section 3.3 - Cross-Request LRU Cachin
5559
> "React.cache() only works within one request. For data shared across sequential requests, use an LRU cache."
5660
5761
However, for **static documentation sites**, the better approach is:
62+
5863
1. **Static Generation** via `generateStaticParams()` - already exists but bypassed
5964
2. **Remove dynamic triggers** - `searchParams` forces dynamic mode
6065
3. **Vercel Edge CDN** will cache static pages automatically
@@ -66,6 +71,7 @@ The current `React.cache()` calls in [registry-cache.ts](apps/www/src/lib/regist
6671
Move locale from query param (`?locale=cn`) to path segment (`/cn/docs/...`).
6772

6873
**Why this approach:**
74+
6975
- Enables full static generation + CDN caching
7076
- Better SEO (separate URLs for each locale)
7177
- Follows Next.js i18n best practices
@@ -75,16 +81,16 @@ Move locale from query param (`?locale=cn`) to path segment (`/cn/docs/...`).
7581

7682
### Files to Modify (Complete List)
7783

78-
| File | Change |
79-
|------|--------|
80-
| `apps/www/src/app/(app)/docs/[[...slug]]/page.tsx` | Remove `searchParams`, add locale prop |
81-
| `apps/www/src/app/(app)/page.tsx` | Remove `searchParams` (home page!) |
82-
| `apps/www/src/app/cn/docs/[[...slug]]/page.tsx` | NEW - CN docs route |
83-
| `apps/www/src/app/cn/page.tsx` | NEW - CN home page |
84-
| `apps/www/src/hooks/useLocale.ts` | Use `usePathname()` not `useSearchParams()` |
85-
| `apps/www/src/lib/withLocale.ts` | Remove `?locale=cn` suffix |
86-
| `apps/www/src/components/languages-dropdown-menu.tsx` | Remove query param setting |
87-
| `apps/www/next.config.ts` | Replace rewrites with redirects |
84+
| File | Change |
85+
| ----------------------------------------------------- | ------------------------------------------- |
86+
| `apps/www/src/app/(app)/docs/[[...slug]]/page.tsx` | Remove `searchParams`, add locale prop |
87+
| `apps/www/src/app/(app)/page.tsx` | Remove `searchParams` (home page!) |
88+
| `apps/www/src/app/cn/docs/[[...slug]]/page.tsx` | NEW - CN docs route |
89+
| `apps/www/src/app/cn/page.tsx` | NEW - CN home page |
90+
| `apps/www/src/hooks/useLocale.ts` | Use `usePathname()` not `useSearchParams()` |
91+
| `apps/www/src/lib/withLocale.ts` | Remove `?locale=cn` suffix |
92+
| `apps/www/src/components/languages-dropdown-menu.tsx` | Remove query param setting |
93+
| `apps/www/next.config.ts` | Replace rewrites with redirects |
8894

8995
### Step 1: Fix English Docs Page
9096

@@ -96,22 +102,22 @@ Remove `searchParams` from page props:
96102
// BEFORE
97103
type DocPageProps = {
98104
params: Promise<{ slug: string[] }>;
99-
searchParams: Promise<{ locale: string }>; // DELETE THIS
105+
searchParams: Promise<{ locale: string }>; // DELETE THIS
100106
};
101107

102108
// AFTER
103109
type DocPageProps = {
104110
params: Promise<{ slug: string[] }>;
105-
locale?: 'en' | 'cn'; // Optional prop, defaults to 'en'
111+
locale?: "en" | "cn"; // Optional prop, defaults to 'en'
106112
};
107113

108-
export const dynamic = 'force-static';
114+
export const dynamic = "force-static";
109115

110116
// Update getDocFromParams
111-
async function getDocFromParams({ params, locale = 'en' }: DocPageProps) {
117+
async function getDocFromParams({ params, locale = "en" }: DocPageProps) {
112118
const slugParam = (await params).slug;
113119
// Use locale prop instead of searchParams
114-
if (locale === 'cn') {
120+
if (locale === "cn") {
115121
// Chinese logic...
116122
}
117123
// ...
@@ -122,22 +128,26 @@ async function getDocFromParams({ params, locale = 'en' }: DocPageProps) {
122128

123129
```tsx
124130
// apps/www/src/app/cn/docs/[[...slug]]/page.tsx
125-
import { DocContent } from '@/app/(app)/docs/[[...slug]]/doc-content';
126-
import { allDocs } from 'contentlayer/generated';
131+
import { DocContent } from "@/app/(app)/docs/[[...slug]]/doc-content";
132+
import { allDocs } from "contentlayer/generated";
127133
// ... other imports from main page
128134

129-
export const dynamic = 'force-static';
135+
export const dynamic = "force-static";
130136

131137
// IMPORTANT: Generate params for CN docs specifically
132138
export function generateStaticParams() {
133139
return allDocs
134-
.filter((doc) => doc._raw.sourceFileName?.endsWith('.cn.mdx'))
140+
.filter((doc) => doc._raw.sourceFileName?.endsWith(".cn.mdx"))
135141
.map((doc) => ({
136-
slug: doc.slugAsParams.replace(/\.cn$/, '').split('/').slice(1),
142+
slug: doc.slugAsParams.replace(/\.cn$/, "").split("/").slice(1),
137143
}));
138144
}
139145

140-
export default async function CNDocPage({ params }: { params: Promise<{ slug: string[] }> }) {
146+
export default async function CNDocPage({
147+
params,
148+
}: {
149+
params: Promise<{ slug: string[] }>;
150+
}) {
141151
// Render with locale='cn'
142152
// ... (copy rendering logic with locale hardcoded to 'cn')
143153
}
@@ -149,20 +159,20 @@ export default async function CNDocPage({ params }: { params: Promise<{ slug: st
149159
// apps/www/src/hooks/useLocale.ts
150160

151161
// BEFORE - forces client-side hydration issues
152-
import { useSearchParams } from 'next/navigation';
162+
import { useSearchParams } from "next/navigation";
153163

154164
export const useLocale = () => {
155165
const searchParams = useSearchParams();
156-
const locale = searchParams?.get('locale') || 'en';
166+
const locale = searchParams?.get("locale") || "en";
157167
return locale;
158168
};
159169

160170
// AFTER - derive from pathname
161-
import { usePathname } from 'next/navigation';
171+
import { usePathname } from "next/navigation";
162172

163173
export const useLocale = () => {
164174
const pathname = usePathname();
165-
return pathname?.startsWith('/cn') ? 'cn' : 'en';
175+
return pathname?.startsWith("/cn") ? "cn" : "en";
166176
};
167177
```
168178

@@ -173,15 +183,15 @@ export const useLocale = () => {
173183

174184
// BEFORE - adds redundant query param
175185
export const hrefWithLocale = (href: string, locale: string) => {
176-
if (locale === 'cn') {
177-
return `/cn${href}?locale=${locale}`; // Redundant!
186+
if (locale === "cn") {
187+
return `/cn${href}?locale=${locale}`; // Redundant!
178188
}
179189
return href;
180190
};
181191

182192
// AFTER - path only
183193
export const hrefWithLocale = (href: string, locale: string) => {
184-
if (locale === 'cn') {
194+
if (locale === "cn") {
185195
return `/cn${href}`;
186196
}
187197
return href;
@@ -234,15 +244,15 @@ export default async function IndexPage({
234244
}: {
235245
searchParams: SearchParams;
236246
}) {
237-
const locale = ((await searchParams).locale || 'en') as keyof typeof i18n;
247+
const locale = ((await searchParams).locale || "en") as keyof typeof i18n;
238248
// ...
239249
}
240250

241251
// AFTER - remove searchParams, default to 'en'
242-
export const dynamic = 'force-static';
252+
export const dynamic = "force-static";
243253

244254
export default async function IndexPage() {
245-
const locale = 'en'; // English home page
255+
const locale = "en"; // English home page
246256
// ...
247257
}
248258

@@ -273,12 +283,12 @@ export default async function IndexPage() {
273283

274284
## Alternative Approaches Considered
275285

276-
| Approach | Pros | Cons |
277-
|----------|------|------|
278-
| Path segments (chosen) | Full caching, SEO-friendly | Route restructuring needed |
279-
| Cookies for locale | No URL changes | Still dynamic, no caching |
280-
| ISR with short TTL | Quick fix | Still hits origin frequently |
281-
| Remove CN support | Simplest | Loses Chinese users |
286+
| Approach | Pros | Cons |
287+
| ---------------------- | -------------------------- | ---------------------------- |
288+
| Path segments (chosen) | Full caching, SEO-friendly | Route restructuring needed |
289+
| Cookies for locale | No URL changes | Still dynamic, no caching |
290+
| ISR with short TTL | Quick fix | Still hits origin frequently |
291+
| Remove CN support | Simplest | Loses Chinese users |
282292

283293
## Risk Analysis
284294

@@ -289,13 +299,15 @@ export default async function IndexPage() {
289299
## References
290300

291301
### Internal Files
292-
- [apps/www/src/app/(app)/docs/[[...slug]]/page.tsx](apps/www/src/app/(app)/docs/[[...slug]]/page.tsx) - Docs page
293-
- [apps/www/src/app/(app)/page.tsx](apps/www/src/app/(app)/page.tsx) - Home page
302+
303+
- [apps/www/src/app/(app)/docs/[[...slug]]/page.tsx](<apps/www/src/app/(app)/docs/[[...slug]]/page.tsx>) - Docs page
304+
- [apps/www/src/app/(app)/page.tsx](<apps/www/src/app/(app)/page.tsx>) - Home page
294305
- [apps/www/src/hooks/useLocale.ts](apps/www/src/hooks/useLocale.ts) - Client locale hook
295306
- [apps/www/src/lib/withLocale.ts](apps/www/src/lib/withLocale.ts) - Locale link helper
296307
- [apps/www/next.config.ts](apps/www/next.config.ts) - Next.js config
297308

298309
### External Documentation
310+
299311
- [Next.js i18n Routing](https://nextjs.org/docs/app/building-your-application/routing/internationalization)
300-
- [Vercel React Best Practices - Server Caching](/.claude/rules/vercel-react-best-practices/AGENTS.md#33-cross-request-lru-caching)
312+
- [Vercel React Best Practices - Server Caching](/.claude/skills/vercel-react-best-practices/AGENTS.md#33-cross-request-lru-caching)
301313
- [Next.js Static Generation](https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default)

0 commit comments

Comments
 (0)