cp-toolkit 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -0
- package/bin/cp-kit.js +72 -0
- package/package.json +46 -0
- package/src/commands/add.js +212 -0
- package/src/commands/doctor.js +149 -0
- package/src/commands/init.js +662 -0
- package/src/commands/list.js +128 -0
- package/src/index.js +13 -0
- package/templates/agents/backend-specialist.md +263 -0
- package/templates/agents/code-archaeologist.md +106 -0
- package/templates/agents/database-architect.md +226 -0
- package/templates/agents/debugger.md +225 -0
- package/templates/agents/devops-engineer.md +242 -0
- package/templates/agents/documentation-writer.md +104 -0
- package/templates/agents/explorer-agent.md +73 -0
- package/templates/agents/frontend-specialist.md +556 -0
- package/templates/agents/game-developer.md +162 -0
- package/templates/agents/mobile-developer.md +377 -0
- package/templates/agents/orchestrator.md +416 -0
- package/templates/agents/penetration-tester.md +188 -0
- package/templates/agents/performance-optimizer.md +187 -0
- package/templates/agents/product-manager.md +112 -0
- package/templates/agents/product-owner.md +95 -0
- package/templates/agents/project-planner.md +406 -0
- package/templates/agents/qa-automation-engineer.md +103 -0
- package/templates/agents/security-auditor.md +170 -0
- package/templates/agents/seo-specialist.md +111 -0
- package/templates/agents/test-engineer.md +158 -0
- package/templates/github/agents/backend-specialist.md +67 -0
- package/templates/github/agents/code-archaeologist.md +61 -0
- package/templates/github/agents/database-architect.md +73 -0
- package/templates/github/agents/debugger.md +71 -0
- package/templates/github/agents/devops-engineer.md +85 -0
- package/templates/github/agents/documentation-writer.md +107 -0
- package/templates/github/agents/explorer-agent.md +87 -0
- package/templates/github/agents/frontend-specialist.md +54 -0
- package/templates/github/agents/game-developer.md +94 -0
- package/templates/github/agents/mobile-developer.md +75 -0
- package/templates/github/agents/orchestrator.md +48 -0
- package/templates/github/agents/penetration-tester.md +87 -0
- package/templates/github/agents/performance-optimizer.md +70 -0
- package/templates/github/agents/product-manager.md +85 -0
- package/templates/github/agents/product-owner.md +77 -0
- package/templates/github/agents/project-planner.md +83 -0
- package/templates/github/agents/qa-automation-engineer.md +95 -0
- package/templates/github/agents/security-auditor.md +72 -0
- package/templates/github/agents/seo-specialist.md +78 -0
- package/templates/github/agents/test-engineer.md +79 -0
- package/templates/github/instructions/database.instructions.md +74 -0
- package/templates/github/instructions/python.instructions.md +76 -0
- package/templates/github/instructions/security.instructions.md +73 -0
- package/templates/github/instructions/typescript.instructions.md +50 -0
- package/templates/rules/GEMINI.md +273 -0
- package/templates/scripts/mcp-server.js +704 -0
- package/templates/skills/core/behavioral-modes/SKILL.md +242 -0
- package/templates/skills/core/brainstorming/SKILL.md +163 -0
- package/templates/skills/core/brainstorming/dynamic-questioning.md +350 -0
- package/templates/skills/core/clean-code/SKILL.md +201 -0
- package/templates/skills/core/intelligent-routing/SKILL.md +335 -0
- package/templates/skills/core/mcp-builder/SKILL.md +176 -0
- package/templates/skills/core/parallel-agents/SKILL.md +175 -0
- package/templates/skills/core/plan-writing/SKILL.md +152 -0
- package/templates/skills/optional/api-patterns/SKILL.md +81 -0
- package/templates/skills/optional/api-patterns/api-style.md +42 -0
- package/templates/skills/optional/api-patterns/auth.md +24 -0
- package/templates/skills/optional/api-patterns/documentation.md +26 -0
- package/templates/skills/optional/api-patterns/graphql.md +41 -0
- package/templates/skills/optional/api-patterns/rate-limiting.md +31 -0
- package/templates/skills/optional/api-patterns/response.md +37 -0
- package/templates/skills/optional/api-patterns/rest.md +40 -0
- package/templates/skills/optional/api-patterns/scripts/api_validator.py +211 -0
- package/templates/skills/optional/api-patterns/security-testing.md +122 -0
- package/templates/skills/optional/api-patterns/trpc.md +41 -0
- package/templates/skills/optional/api-patterns/versioning.md +22 -0
- package/templates/skills/optional/app-builder/SKILL.md +75 -0
- package/templates/skills/optional/app-builder/agent-coordination.md +71 -0
- package/templates/skills/optional/app-builder/feature-building.md +53 -0
- package/templates/skills/optional/app-builder/project-detection.md +34 -0
- package/templates/skills/optional/app-builder/scaffolding.md +118 -0
- package/templates/skills/optional/app-builder/tech-stack.md +40 -0
- package/templates/skills/optional/app-builder/templates/SKILL.md +39 -0
- package/templates/skills/optional/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/templates/skills/optional/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/templates/skills/optional/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/templates/skills/optional/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/templates/skills/optional/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/templates/skills/optional/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/templates/skills/optional/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/templates/skills/optional/app-builder/templates/nextjs-fullstack/TEMPLATE.md +82 -0
- package/templates/skills/optional/app-builder/templates/nextjs-saas/TEMPLATE.md +100 -0
- package/templates/skills/optional/app-builder/templates/nextjs-static/TEMPLATE.md +106 -0
- package/templates/skills/optional/app-builder/templates/nuxt-app/TEMPLATE.md +101 -0
- package/templates/skills/optional/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/templates/skills/optional/app-builder/templates/react-native-app/TEMPLATE.md +93 -0
- package/templates/skills/optional/architecture/SKILL.md +55 -0
- package/templates/skills/optional/architecture/context-discovery.md +43 -0
- package/templates/skills/optional/architecture/examples.md +94 -0
- package/templates/skills/optional/architecture/pattern-selection.md +68 -0
- package/templates/skills/optional/architecture/patterns-reference.md +50 -0
- package/templates/skills/optional/architecture/trade-off-analysis.md +77 -0
- package/templates/skills/optional/bash-linux/SKILL.md +199 -0
- package/templates/skills/optional/code-review-checklist/SKILL.md +109 -0
- package/templates/skills/optional/database-design/SKILL.md +52 -0
- package/templates/skills/optional/database-design/database-selection.md +43 -0
- package/templates/skills/optional/database-design/indexing.md +39 -0
- package/templates/skills/optional/database-design/migrations.md +48 -0
- package/templates/skills/optional/database-design/optimization.md +36 -0
- package/templates/skills/optional/database-design/orm-selection.md +30 -0
- package/templates/skills/optional/database-design/schema-design.md +56 -0
- package/templates/skills/optional/database-design/scripts/schema_validator.py +172 -0
- package/templates/skills/optional/deployment-procedures/SKILL.md +241 -0
- package/templates/skills/optional/documentation-templates/SKILL.md +194 -0
- package/templates/skills/optional/frontend-design/SKILL.md +418 -0
- package/templates/skills/optional/frontend-design/animation-guide.md +331 -0
- package/templates/skills/optional/frontend-design/color-system.md +311 -0
- package/templates/skills/optional/frontend-design/decision-trees.md +418 -0
- package/templates/skills/optional/frontend-design/motion-graphics.md +306 -0
- package/templates/skills/optional/frontend-design/scripts/accessibility_checker.py +183 -0
- package/templates/skills/optional/frontend-design/scripts/ux_audit.py +722 -0
- package/templates/skills/optional/frontend-design/typography-system.md +345 -0
- package/templates/skills/optional/frontend-design/ux-psychology.md +541 -0
- package/templates/skills/optional/frontend-design/visual-effects.md +383 -0
- package/templates/skills/optional/game-development/2d-games/SKILL.md +119 -0
- package/templates/skills/optional/game-development/3d-games/SKILL.md +135 -0
- package/templates/skills/optional/game-development/SKILL.md +167 -0
- package/templates/skills/optional/game-development/game-art/SKILL.md +185 -0
- package/templates/skills/optional/game-development/game-audio/SKILL.md +190 -0
- package/templates/skills/optional/game-development/game-design/SKILL.md +129 -0
- package/templates/skills/optional/game-development/mobile-games/SKILL.md +108 -0
- package/templates/skills/optional/game-development/multiplayer/SKILL.md +132 -0
- package/templates/skills/optional/game-development/pc-games/SKILL.md +144 -0
- package/templates/skills/optional/game-development/vr-ar/SKILL.md +123 -0
- package/templates/skills/optional/game-development/web-games/SKILL.md +150 -0
- package/templates/skills/optional/geo-fundamentals/SKILL.md +156 -0
- package/templates/skills/optional/geo-fundamentals/scripts/geo_checker.py +289 -0
- package/templates/skills/optional/i18n-localization/SKILL.md +154 -0
- package/templates/skills/optional/i18n-localization/scripts/i18n_checker.py +241 -0
- package/templates/skills/optional/lint-and-validate/SKILL.md +45 -0
- package/templates/skills/optional/lint-and-validate/scripts/lint_runner.py +172 -0
- package/templates/skills/optional/lint-and-validate/scripts/type_coverage.py +173 -0
- package/templates/skills/optional/mobile-design/SKILL.md +394 -0
- package/templates/skills/optional/mobile-design/decision-trees.md +516 -0
- package/templates/skills/optional/mobile-design/mobile-backend.md +491 -0
- package/templates/skills/optional/mobile-design/mobile-color-system.md +420 -0
- package/templates/skills/optional/mobile-design/mobile-debugging.md +122 -0
- package/templates/skills/optional/mobile-design/mobile-design-thinking.md +357 -0
- package/templates/skills/optional/mobile-design/mobile-navigation.md +458 -0
- package/templates/skills/optional/mobile-design/mobile-performance.md +767 -0
- package/templates/skills/optional/mobile-design/mobile-testing.md +356 -0
- package/templates/skills/optional/mobile-design/mobile-typography.md +433 -0
- package/templates/skills/optional/mobile-design/platform-android.md +666 -0
- package/templates/skills/optional/mobile-design/platform-ios.md +561 -0
- package/templates/skills/optional/mobile-design/scripts/mobile_audit.py +670 -0
- package/templates/skills/optional/mobile-design/touch-psychology.md +537 -0
- package/templates/skills/optional/nextjs-react-expert/1-async-eliminating-waterfalls.md +312 -0
- package/templates/skills/optional/nextjs-react-expert/2-bundle-bundle-size-optimization.md +240 -0
- package/templates/skills/optional/nextjs-react-expert/3-server-server-side-performance.md +490 -0
- package/templates/skills/optional/nextjs-react-expert/4-client-client-side-data-fetching.md +264 -0
- package/templates/skills/optional/nextjs-react-expert/5-rerender-re-render-optimization.md +581 -0
- package/templates/skills/optional/nextjs-react-expert/6-rendering-rendering-performance.md +432 -0
- package/templates/skills/optional/nextjs-react-expert/7-js-javascript-performance.md +684 -0
- package/templates/skills/optional/nextjs-react-expert/8-advanced-advanced-patterns.md +150 -0
- package/templates/skills/optional/nextjs-react-expert/SKILL.md +267 -0
- package/templates/skills/optional/nextjs-react-expert/scripts/convert_rules.py +222 -0
- package/templates/skills/optional/nextjs-react-expert/scripts/react_performance_checker.py +252 -0
- package/templates/skills/optional/nodejs-best-practices/SKILL.md +333 -0
- package/templates/skills/optional/performance-profiling/SKILL.md +143 -0
- package/templates/skills/optional/performance-profiling/scripts/lighthouse_audit.py +76 -0
- package/templates/skills/optional/powershell-windows/SKILL.md +167 -0
- package/templates/skills/optional/python-patterns/SKILL.md +441 -0
- package/templates/skills/optional/red-team-tactics/SKILL.md +199 -0
- package/templates/skills/optional/seo-fundamentals/SKILL.md +129 -0
- package/templates/skills/optional/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/templates/skills/optional/server-management/SKILL.md +161 -0
- package/templates/skills/optional/systematic-debugging/SKILL.md +109 -0
- package/templates/skills/optional/tailwind-patterns/SKILL.md +269 -0
- package/templates/skills/optional/tdd-workflow/SKILL.md +149 -0
- package/templates/skills/optional/testing-patterns/SKILL.md +178 -0
- package/templates/skills/optional/testing-patterns/scripts/test_runner.py +219 -0
- package/templates/skills/optional/vulnerability-scanner/SKILL.md +276 -0
- package/templates/skills/optional/vulnerability-scanner/checklists.md +121 -0
- package/templates/skills/optional/vulnerability-scanner/scripts/security_scan.py +458 -0
- package/templates/skills/optional/web-design-guidelines/SKILL.md +57 -0
- package/templates/skills/optional/webapp-testing/SKILL.md +187 -0
- package/templates/skills/optional/webapp-testing/scripts/playwright_runner.py +173 -0
- package/templates/workflows/brainstorm.md +113 -0
- package/templates/workflows/create.md +59 -0
- package/templates/workflows/debug.md +103 -0
- package/templates/workflows/deploy.md +176 -0
- package/templates/workflows/enhance.md +63 -0
- package/templates/workflows/orchestrate.md +237 -0
- package/templates/workflows/plan.md +89 -0
- package/templates/workflows/preview.md +81 -0
- package/templates/workflows/status.md +86 -0
- package/templates/workflows/test.md +144 -0
- package/templates/workflows/ui-ux-pro-max.md +296 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# 4. Client-Side Data Fetching
|
|
2
|
+
|
|
3
|
+
> **Impact:** MEDIUM-HIGH
|
|
4
|
+
> **Focus:** Automatic deduplication and efficient data fetching patterns reduce redundant network requests.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
This section contains **4 rules** focused on client-side data fetching.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Rule 4.1: Deduplicate Global Event Listeners
|
|
15
|
+
|
|
16
|
+
**Impact:** LOW
|
|
17
|
+
**Tags:** client, swr, event-listeners, subscription
|
|
18
|
+
|
|
19
|
+
## Deduplicate Global Event Listeners
|
|
20
|
+
|
|
21
|
+
Use `useSWRSubscription()` to share global event listeners across component instances.
|
|
22
|
+
|
|
23
|
+
**Incorrect (N instances = N listeners):**
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
function useKeyboardShortcut(key: string, callback: () => void) {
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const handler = (e: KeyboardEvent) => {
|
|
29
|
+
if (e.metaKey && e.key === key) {
|
|
30
|
+
callback()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
window.addEventListener('keydown', handler)
|
|
34
|
+
return () => window.removeEventListener('keydown', handler)
|
|
35
|
+
}, [key, callback])
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
When using the `useKeyboardShortcut` hook multiple times, each instance will register a new listener.
|
|
40
|
+
|
|
41
|
+
**Correct (N instances = 1 listener):**
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
import useSWRSubscription from 'swr/subscription'
|
|
45
|
+
|
|
46
|
+
// Module-level Map to track callbacks per key
|
|
47
|
+
const keyCallbacks = new Map<string, Set<() => void>>()
|
|
48
|
+
|
|
49
|
+
function useKeyboardShortcut(key: string, callback: () => void) {
|
|
50
|
+
// Register this callback in the Map
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!keyCallbacks.has(key)) {
|
|
53
|
+
keyCallbacks.set(key, new Set())
|
|
54
|
+
}
|
|
55
|
+
keyCallbacks.get(key)!.add(callback)
|
|
56
|
+
|
|
57
|
+
return () => {
|
|
58
|
+
const set = keyCallbacks.get(key)
|
|
59
|
+
if (set) {
|
|
60
|
+
set.delete(callback)
|
|
61
|
+
if (set.size === 0) {
|
|
62
|
+
keyCallbacks.delete(key)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}, [key, callback])
|
|
67
|
+
|
|
68
|
+
useSWRSubscription('global-keydown', () => {
|
|
69
|
+
const handler = (e: KeyboardEvent) => {
|
|
70
|
+
if (e.metaKey && keyCallbacks.has(e.key)) {
|
|
71
|
+
keyCallbacks.get(e.key)!.forEach(cb => cb())
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
window.addEventListener('keydown', handler)
|
|
75
|
+
return () => window.removeEventListener('keydown', handler)
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function Profile() {
|
|
80
|
+
// Multiple shortcuts will share the same listener
|
|
81
|
+
useKeyboardShortcut('p', () => { /* ... */ })
|
|
82
|
+
useKeyboardShortcut('k', () => { /* ... */ })
|
|
83
|
+
// ...
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Rule 4.2: Use Passive Event Listeners for Scrolling Performance
|
|
90
|
+
|
|
91
|
+
**Impact:** MEDIUM
|
|
92
|
+
**Tags:** client, event-listeners, scrolling, performance, touch, wheel
|
|
93
|
+
|
|
94
|
+
## Use Passive Event Listeners for Scrolling Performance
|
|
95
|
+
|
|
96
|
+
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.
|
|
97
|
+
|
|
98
|
+
**Incorrect:**
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
|
|
103
|
+
const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
|
|
104
|
+
|
|
105
|
+
document.addEventListener('touchstart', handleTouch)
|
|
106
|
+
document.addEventListener('wheel', handleWheel)
|
|
107
|
+
|
|
108
|
+
return () => {
|
|
109
|
+
document.removeEventListener('touchstart', handleTouch)
|
|
110
|
+
document.removeEventListener('wheel', handleWheel)
|
|
111
|
+
}
|
|
112
|
+
}, [])
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Correct:**
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
const handleTouch = (e: TouchEvent) => console.log(e.touches[0].clientX)
|
|
120
|
+
const handleWheel = (e: WheelEvent) => console.log(e.deltaY)
|
|
121
|
+
|
|
122
|
+
document.addEventListener('touchstart', handleTouch, { passive: true })
|
|
123
|
+
document.addEventListener('wheel', handleWheel, { passive: true })
|
|
124
|
+
|
|
125
|
+
return () => {
|
|
126
|
+
document.removeEventListener('touchstart', handleTouch)
|
|
127
|
+
document.removeEventListener('wheel', handleWheel)
|
|
128
|
+
}
|
|
129
|
+
}, [])
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Use passive when:** tracking/analytics, logging, any listener that doesn't call `preventDefault()`.
|
|
133
|
+
|
|
134
|
+
**Don't use passive when:** implementing custom swipe gestures, custom zoom controls, or any listener that needs `preventDefault()`.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Rule 4.3: Use SWR for Automatic Deduplication
|
|
139
|
+
|
|
140
|
+
**Impact:** MEDIUM-HIGH
|
|
141
|
+
**Tags:** client, swr, deduplication, data-fetching
|
|
142
|
+
|
|
143
|
+
## Use SWR for Automatic Deduplication
|
|
144
|
+
|
|
145
|
+
SWR enables request deduplication, caching, and revalidation across component instances.
|
|
146
|
+
|
|
147
|
+
**Incorrect (no deduplication, each instance fetches):**
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
function UserList() {
|
|
151
|
+
const [users, setUsers] = useState([])
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
fetch('/api/users')
|
|
154
|
+
.then(r => r.json())
|
|
155
|
+
.then(setUsers)
|
|
156
|
+
}, [])
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Correct (multiple instances share one request):**
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import useSWR from 'swr'
|
|
164
|
+
|
|
165
|
+
function UserList() {
|
|
166
|
+
const { data: users } = useSWR('/api/users', fetcher)
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**For immutable data:**
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { useImmutableSWR } from '@/lib/swr'
|
|
174
|
+
|
|
175
|
+
function StaticContent() {
|
|
176
|
+
const { data } = useImmutableSWR('/api/config', fetcher)
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**For mutations:**
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
import { useSWRMutation } from 'swr/mutation'
|
|
184
|
+
|
|
185
|
+
function UpdateButton() {
|
|
186
|
+
const { trigger } = useSWRMutation('/api/user', updateUser)
|
|
187
|
+
return <button onClick={() => trigger()}>Update</button>
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Reference: [https://swr.vercel.app](https://swr.vercel.app)
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Rule 4.4: Version and Minimize localStorage Data
|
|
196
|
+
|
|
197
|
+
**Impact:** MEDIUM
|
|
198
|
+
**Tags:** client, localStorage, storage, versioning, data-minimization
|
|
199
|
+
|
|
200
|
+
## Version and Minimize localStorage Data
|
|
201
|
+
|
|
202
|
+
Add version prefix to keys and store only needed fields. Prevents schema conflicts and accidental storage of sensitive data.
|
|
203
|
+
|
|
204
|
+
**Incorrect:**
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// No version, stores everything, no error handling
|
|
208
|
+
localStorage.setItem('userConfig', JSON.stringify(fullUserObject))
|
|
209
|
+
const data = localStorage.getItem('userConfig')
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Correct:**
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
const VERSION = 'v2'
|
|
216
|
+
|
|
217
|
+
function saveConfig(config: { theme: string; language: string }) {
|
|
218
|
+
try {
|
|
219
|
+
localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config))
|
|
220
|
+
} catch {
|
|
221
|
+
// Throws in incognito/private browsing, quota exceeded, or disabled
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function loadConfig() {
|
|
226
|
+
try {
|
|
227
|
+
const data = localStorage.getItem(`userConfig:${VERSION}`)
|
|
228
|
+
return data ? JSON.parse(data) : null
|
|
229
|
+
} catch {
|
|
230
|
+
return null
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Migration from v1 to v2
|
|
235
|
+
function migrate() {
|
|
236
|
+
try {
|
|
237
|
+
const v1 = localStorage.getItem('userConfig:v1')
|
|
238
|
+
if (v1) {
|
|
239
|
+
const old = JSON.parse(v1)
|
|
240
|
+
saveConfig({ theme: old.darkMode ? 'dark' : 'light', language: old.lang })
|
|
241
|
+
localStorage.removeItem('userConfig:v1')
|
|
242
|
+
}
|
|
243
|
+
} catch {}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Store minimal fields from server responses:**
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// User object has 20+ fields, only store what UI needs
|
|
251
|
+
function cachePrefs(user: FullUser) {
|
|
252
|
+
try {
|
|
253
|
+
localStorage.setItem('prefs:v1', JSON.stringify({
|
|
254
|
+
theme: user.preferences.theme,
|
|
255
|
+
notifications: user.preferences.notifications
|
|
256
|
+
}))
|
|
257
|
+
} catch {}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Always wrap in try-catch:** `getItem()` and `setItem()` throw in incognito/private browsing (Safari, Firefox), when quota exceeded, or when disabled.
|
|
262
|
+
|
|
263
|
+
**Benefits:** Schema evolution via versioning, reduced storage size, prevents storing tokens/PII/internal flags.
|
|
264
|
+
|