skillstore-cli 1.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 +95 -0
- package/data/bundles/devflow-complete.json +19 -0
- package/data/free-skills/devflow-agile/manifest.json +19 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/retro.md +23 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/review.md +21 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/sprint.md +30 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/standup.md +20 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile.md +35 -0
- package/data/free-skills/devflow-agile/plugin/commands/devflow.md +42 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/SKILL.md +93 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/assets/sample-output.md +182 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/clean-architecture.md +361 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/clean-code-guide.md +207 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/debugging-methodology.md +191 -0
- package/data/free-skills/devflow-agile/template/agents/agile-coach.md +76 -0
- package/data/free-skills/devflow-agile/template/workflows/agile-sprint-workflow.md +81 -0
- package/data/free-skills/devflow-bootstrap/manifest.json +8 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/bootstrap/auto.md +31 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/bootstrap.md +38 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/devflow.md +20 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/SKILL.md +56 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/assets/sample-output.md +216 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/references/architecture-decisions.md +254 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/references/stack-templates.md +400 -0
- package/data/free-skills/devflow-bootstrap/template/agents/bootstrap-specialist.md +56 -0
- package/data/free-skills/devflow-bootstrap/template/workflows/bootstrap-workflow.md +70 -0
- package/data/free-skills/devflow-docs/manifest.json +8 -0
- package/data/free-skills/devflow-docs/plugin/commands/devflow.md +20 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs/generate.md +17 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs/parse.md +19 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs.md +26 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/SKILL.md +59 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/assets/sample-output.md +114 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/references/extraction-techniques.md +115 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/references/ocr-strategies.md +167 -0
- package/data/free-skills/devflow-docs/template/agents/docs-specialist.md +35 -0
- package/data/free-skills/devflow-docs/template/workflows/docs-workflow.md +70 -0
- package/data/free-skills/devflow-postproject/manifest.json +13 -0
- package/data/free-skills/devflow-postproject/plugin/commands/devflow.md +34 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/handover.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/retro.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/support.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject.md +32 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/SKILL.md +70 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/assets/sample-output.md +79 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/facilitation-techniques.md +178 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/lessons-learned-template.md +118 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/retro-techniques.md +100 -0
- package/data/free-skills/devflow-postproject/template/agents/transition-manager.md +71 -0
- package/data/free-skills/devflow-postproject/template/workflows/transition-workflow.md +72 -0
- package/data/free-skills/devflow-presale/manifest.json +15 -0
- package/data/free-skills/devflow-presale/plugin/commands/devflow.md +47 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/analyze.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/estimate.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/price.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/propose.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale.md +42 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/SKILL.md +63 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/assets/sample-output.md +129 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/references/extraction-framework.md +140 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/references/output-template.md +132 -0
- package/data/free-skills/devflow-presale/template/agents/presale-lead.md +83 -0
- package/data/free-skills/devflow-presale/template/agents/proposal-reviewer.md +63 -0
- package/data/free-skills/devflow-presale/template/workflows/presale-workflow.md +70 -0
- package/data/registry/categories.json +7 -0
- package/data/registry/packages.json +184 -0
- package/data/shared/framework/agents/brainstormer.md +74 -0
- package/data/shared/framework/agents/code-reviewer.md +87 -0
- package/data/shared/framework/agents/debugger.md +84 -0
- package/data/shared/framework/agents/docs-manager.md +55 -0
- package/data/shared/framework/agents/git-manager.md +59 -0
- package/data/shared/framework/agents/planner.md +68 -0
- package/data/shared/framework/agents/researcher.md +66 -0
- package/data/shared/framework/agents/tester.md +65 -0
- package/data/shared/framework/commands/cook/auto.md +27 -0
- package/data/shared/framework/commands/cook.md +45 -0
- package/data/shared/framework/commands/fix/ci.md +21 -0
- package/data/shared/framework/commands/fix/test.md +26 -0
- package/data/shared/framework/commands/fix/types.md +29 -0
- package/data/shared/framework/commands/fix.md +26 -0
- package/data/shared/framework/commands/git/cm.md +37 -0
- package/data/shared/framework/commands/git/pr.md +40 -0
- package/data/shared/framework/config/CLAUDE.md.template +26 -0
- package/data/shared/framework/config/settings.json +41 -0
- package/data/shared/framework/config/skillstore.config.json +29 -0
- package/data/shared/framework/hooks/discord-notify.sh +85 -0
- package/data/shared/framework/hooks/docs-sync.sh +53 -0
- package/data/shared/framework/hooks/modularization-hook.js +103 -0
- package/data/shared/framework/hooks/notification.js +94 -0
- package/data/shared/framework/hooks/quality-gate.js +109 -0
- package/data/shared/framework/hooks/scout-block.js +77 -0
- package/data/shared/framework/hooks/telegram-notify.sh +77 -0
- package/data/shared/framework/protocols/error-recovery.md +80 -0
- package/data/shared/framework/protocols/orchestration-protocol.md +112 -0
- package/data/shared/framework/quality/review-protocol.md +76 -0
- package/data/shared/framework/quality/verification-protocol.md +66 -0
- package/data/shared/framework/rules/development-rules.md +75 -0
- package/data/shared/framework/skills/backend-development/SKILL.md +77 -0
- package/data/shared/framework/skills/backend-development/assets/sample-output.md +175 -0
- package/data/shared/framework/skills/backend-development/references/advanced-patterns.md +180 -0
- package/data/shared/framework/skills/backend-development/references/api-design-guide.md +160 -0
- package/data/shared/framework/skills/backend-development/references/architecture-patterns.md +183 -0
- package/data/shared/framework/skills/backend-development/references/observability-resilience.md +155 -0
- package/data/shared/framework/skills/backend-development/references/troubleshooting.md +199 -0
- package/data/shared/framework/skills/codebase-analysis/SKILL.md +72 -0
- package/data/shared/framework/skills/codebase-analysis/assets/sample-output.md +263 -0
- package/data/shared/framework/skills/codebase-analysis/references/analysis-techniques.md +241 -0
- package/data/shared/framework/skills/codebase-analysis/references/dependency-mapping.md +280 -0
- package/data/shared/framework/skills/codebase-analysis/references/tech-debt-assessment.md +208 -0
- package/data/shared/framework/skills/databases/SKILL.md +72 -0
- package/data/shared/framework/skills/databases/assets/sample-output.md +212 -0
- package/data/shared/framework/skills/databases/references/advanced-data-patterns.md +259 -0
- package/data/shared/framework/skills/databases/references/query-optimization.md +214 -0
- package/data/shared/framework/skills/databases/references/schema-design.md +159 -0
- package/data/shared/framework/skills/databases/references/troubleshooting.md +214 -0
- package/data/shared/framework/skills/debugging-investigation/SKILL.md +84 -0
- package/data/shared/framework/skills/debugging-investigation/assets/sample-output.md +314 -0
- package/data/shared/framework/skills/debugging-investigation/references/systematic-debugging.md +197 -0
- package/data/shared/framework/skills/debugging-investigation/references/tool-specific-guides.md +202 -0
- package/data/shared/framework/skills/debugging-investigation/references/troubleshooting-patterns.md +196 -0
- package/data/shared/framework/skills/frontend-development/SKILL.md +67 -0
- package/data/shared/framework/skills/frontend-development/assets/sample-output.md +110 -0
- package/data/shared/framework/skills/frontend-development/references/component-patterns.md +112 -0
- package/data/shared/framework/skills/frontend-development/references/performance-guide.md +169 -0
- package/data/shared/framework/skills/frontend-development/references/routing-forms-realtime.md +374 -0
- package/data/shared/framework/skills/frontend-development/references/ssr-rsc-patterns.md +284 -0
- package/data/shared/framework/skills/frontend-development/references/troubleshooting.md +154 -0
- package/data/shared/framework/skills/mobile-development/SKILL.md +67 -0
- package/data/shared/framework/skills/mobile-development/assets/sample-output.md +382 -0
- package/data/shared/framework/skills/mobile-development/references/mobile-patterns.md +681 -0
- package/data/shared/framework/skills/mobile-development/references/mobile-performance.md +524 -0
- package/data/shared/framework/skills/mobile-development/references/troubleshooting.md +158 -0
- package/data/shared/framework/skills/security-audit/SKILL.md +83 -0
- package/data/shared/framework/skills/security-audit/assets/sample-output.md +451 -0
- package/data/shared/framework/skills/security-audit/references/owasp-checklist.md +580 -0
- package/data/shared/framework/skills/security-audit/references/secure-coding-patterns.md +433 -0
- package/data/shared/framework/skills/security-audit/references/vulnerability-remediation.md +331 -0
- package/data/shared/framework/skills/ui-generation/SKILL.md +70 -0
- package/data/shared/framework/skills/ui-generation/assets/sample-output.md +139 -0
- package/data/shared/framework/skills/ui-generation/references/accessibility-responsive.md +127 -0
- package/data/shared/framework/skills/ui-generation/references/compound-components.md +252 -0
- package/data/shared/framework/skills/ui-generation/references/generation-patterns.md +110 -0
- package/data/shared/framework/skills/ui-generation/references/storybook-design-system.md +278 -0
- package/data/shared/framework/skills/ui-generation/references/troubleshooting.md +198 -0
- package/data/shared/framework/workflows/documentation-management.md +58 -0
- package/data/shared/framework/workflows/primary-workflow.md +88 -0
- package/dist/commands/activate.d.ts +3 -0
- package/dist/commands/activate.d.ts.map +1 -0
- package/dist/commands/activate.js +34 -0
- package/dist/commands/activate.js.map +1 -0
- package/dist/commands/bundle.d.ts +3 -0
- package/dist/commands/bundle.d.ts.map +1 -0
- package/dist/commands/bundle.js +64 -0
- package/dist/commands/bundle.js.map +1 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +99 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +37 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +30 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +35 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +68 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/download/cache.d.ts +3 -0
- package/dist/download/cache.d.ts.map +1 -0
- package/dist/download/cache.js +18 -0
- package/dist/download/cache.js.map +1 -0
- package/dist/download/client.d.ts +2 -0
- package/dist/download/client.d.ts.map +1 -0
- package/dist/download/client.js +58 -0
- package/dist/download/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/file-copier.d.ts +6 -0
- package/dist/installer/file-copier.d.ts.map +1 -0
- package/dist/installer/file-copier.js +32 -0
- package/dist/installer/file-copier.js.map +1 -0
- package/dist/installer/plugin-installer.d.ts +12 -0
- package/dist/installer/plugin-installer.d.ts.map +1 -0
- package/dist/installer/plugin-installer.js +33 -0
- package/dist/installer/plugin-installer.js.map +1 -0
- package/dist/installer/template-installer.d.ts +12 -0
- package/dist/installer/template-installer.d.ts.map +1 -0
- package/dist/installer/template-installer.js +45 -0
- package/dist/installer/template-installer.js.map +1 -0
- package/dist/license/crypto.d.ts +16 -0
- package/dist/license/crypto.d.ts.map +1 -0
- package/dist/license/crypto.js +50 -0
- package/dist/license/crypto.js.map +1 -0
- package/dist/license/license-store.d.ts +19 -0
- package/dist/license/license-store.d.ts.map +1 -0
- package/dist/license/license-store.js +99 -0
- package/dist/license/license-store.js.map +1 -0
- package/dist/license/validator.d.ts +32 -0
- package/dist/license/validator.d.ts.map +1 -0
- package/dist/license/validator.js +81 -0
- package/dist/license/validator.js.map +1 -0
- package/dist/registry/loader.d.ts +30 -0
- package/dist/registry/loader.d.ts.map +1 -0
- package/dist/registry/loader.js +22 -0
- package/dist/registry/loader.js.map +1 -0
- package/dist/registry/search-engine.d.ts +9 -0
- package/dist/registry/search-engine.d.ts.map +1 -0
- package/dist/registry/search-engine.js +30 -0
- package/dist/registry/search-engine.js.map +1 -0
- package/dist/utils/config.d.ts +14 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +28 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +22 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +20 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +79 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +54 -0
package/data/shared/framework/skills/frontend-development/references/routing-forms-realtime.md
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# Routing, Forms & Real-Time Data Patterns
|
|
2
|
+
|
|
3
|
+
## File-Based Routing vs Config-Based Routing
|
|
4
|
+
|
|
5
|
+
### File-Based (Next.js App Router)
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
app/
|
|
9
|
+
├── layout.tsx → root layout
|
|
10
|
+
├── page.tsx → /
|
|
11
|
+
├── blog/
|
|
12
|
+
│ ├── page.tsx → /blog
|
|
13
|
+
│ └── [slug]/
|
|
14
|
+
│ └── page.tsx → /blog/:slug
|
|
15
|
+
├── (marketing)/ → route group (no URL segment)
|
|
16
|
+
│ ├── about/page.tsx → /about
|
|
17
|
+
│ └── pricing/page.tsx → /pricing
|
|
18
|
+
└── api/
|
|
19
|
+
└── webhook/route.ts → /api/webhook
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### File-Based (Nuxt 3)
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
pages/
|
|
26
|
+
├── index.vue → /
|
|
27
|
+
├── blog/
|
|
28
|
+
│ ├── index.vue → /blog
|
|
29
|
+
│ └── [slug].vue → /blog/:slug
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### File-Based (SvelteKit)
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
src/routes/
|
|
36
|
+
├── +page.svelte → /
|
|
37
|
+
├── blog/
|
|
38
|
+
│ ├── +page.svelte → /blog
|
|
39
|
+
│ └── [slug]/
|
|
40
|
+
│ └── +page.svelte → /blog/:slug
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Config-Based (React Router v7)
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
import { createBrowserRouter, RouterProvider } from 'react-router';
|
|
47
|
+
|
|
48
|
+
const router = createBrowserRouter([
|
|
49
|
+
{
|
|
50
|
+
path: '/',
|
|
51
|
+
element: <RootLayout />,
|
|
52
|
+
children: [
|
|
53
|
+
{ index: true, element: <Home /> },
|
|
54
|
+
{ path: 'blog', element: <Blog /> },
|
|
55
|
+
{ path: 'blog/:slug', element: <BlogPost />, loader: postLoader },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
export default function App() {
|
|
61
|
+
return <RouterProvider router={router} />;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Nested Layouts, Parallel Routes & Intercepting Routes (Next.js)
|
|
68
|
+
|
|
69
|
+
### Nested Layouts
|
|
70
|
+
|
|
71
|
+
```tsx
|
|
72
|
+
// app/dashboard/layout.tsx — persists across all /dashboard/* pages
|
|
73
|
+
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
74
|
+
return (
|
|
75
|
+
<div className="flex">
|
|
76
|
+
<Sidebar />
|
|
77
|
+
<main className="flex-1">{children}</main>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Parallel Routes
|
|
84
|
+
|
|
85
|
+
Use `@slot` folders to render multiple pages simultaneously in the same layout.
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
app/
|
|
89
|
+
├── layout.tsx → receives { analytics, team } as props
|
|
90
|
+
├── @analytics/page.tsx → rendered in the analytics slot
|
|
91
|
+
├── @team/page.tsx → rendered in the team slot
|
|
92
|
+
└── page.tsx → default page
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// app/layout.tsx
|
|
97
|
+
export default function Layout({
|
|
98
|
+
children,
|
|
99
|
+
analytics,
|
|
100
|
+
team,
|
|
101
|
+
}: {
|
|
102
|
+
children: React.ReactNode;
|
|
103
|
+
analytics: React.ReactNode;
|
|
104
|
+
team: React.ReactNode;
|
|
105
|
+
}) {
|
|
106
|
+
return (
|
|
107
|
+
<div>
|
|
108
|
+
{children}
|
|
109
|
+
<div className="grid grid-cols-2">
|
|
110
|
+
{analytics}
|
|
111
|
+
{team}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Intercepting Routes
|
|
119
|
+
|
|
120
|
+
Show a modal on soft navigation while preserving the full page on hard navigation.
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
app/
|
|
124
|
+
├── feed/
|
|
125
|
+
│ └── page.tsx
|
|
126
|
+
├── photo/[id]/
|
|
127
|
+
│ └── page.tsx → full page on direct visit
|
|
128
|
+
└── @modal/
|
|
129
|
+
└── (.)photo/[id]/
|
|
130
|
+
└── page.tsx → intercepted as modal from /feed
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## React Hook Form + Zod Validation
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
'use client';
|
|
139
|
+
import { useForm } from 'react-hook-form';
|
|
140
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
141
|
+
import { z } from 'zod';
|
|
142
|
+
|
|
143
|
+
const schema = z.object({
|
|
144
|
+
email: z.string().email('Invalid email address'),
|
|
145
|
+
password: z.string().min(8, 'Must be at least 8 characters'),
|
|
146
|
+
role: z.enum(['admin', 'user'], { required_error: 'Select a role' }),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
type FormValues = z.infer<typeof schema>;
|
|
150
|
+
|
|
151
|
+
export function SignUpForm() {
|
|
152
|
+
const {
|
|
153
|
+
register,
|
|
154
|
+
handleSubmit,
|
|
155
|
+
formState: { errors, isSubmitting },
|
|
156
|
+
} = useForm<FormValues>({
|
|
157
|
+
resolver: zodResolver(schema),
|
|
158
|
+
defaultValues: { email: '', password: '', role: 'user' },
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const onSubmit = async (data: FormValues) => {
|
|
162
|
+
const res = await fetch('/api/signup', {
|
|
163
|
+
method: 'POST',
|
|
164
|
+
body: JSON.stringify(data),
|
|
165
|
+
});
|
|
166
|
+
if (!res.ok) throw new Error('Signup failed');
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
return (
|
|
170
|
+
<form onSubmit={handleSubmit(onSubmit)} noValidate>
|
|
171
|
+
<input {...register('email')} type="email" />
|
|
172
|
+
{errors.email && <p>{errors.email.message}</p>}
|
|
173
|
+
|
|
174
|
+
<input {...register('password')} type="password" />
|
|
175
|
+
{errors.password && <p>{errors.password.message}</p>}
|
|
176
|
+
|
|
177
|
+
<select {...register('role')}>
|
|
178
|
+
<option value="user">User</option>
|
|
179
|
+
<option value="admin">Admin</option>
|
|
180
|
+
</select>
|
|
181
|
+
{errors.role && <p>{errors.role.message}</p>}
|
|
182
|
+
|
|
183
|
+
<button type="submit" disabled={isSubmitting}>Sign Up</button>
|
|
184
|
+
</form>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## VeeValidate + Yup for Vue
|
|
192
|
+
|
|
193
|
+
```vue
|
|
194
|
+
<script setup lang="ts">
|
|
195
|
+
import { useForm } from 'vee-validate';
|
|
196
|
+
import * as yup from 'yup';
|
|
197
|
+
|
|
198
|
+
const schema = yup.object({
|
|
199
|
+
email: yup.string().required().email(),
|
|
200
|
+
password: yup.string().required().min(8),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const { defineField, handleSubmit, errors } = useForm({ validationSchema: schema });
|
|
204
|
+
|
|
205
|
+
const [email, emailAttrs] = defineField('email');
|
|
206
|
+
const [password, passwordAttrs] = defineField('password');
|
|
207
|
+
|
|
208
|
+
const onSubmit = handleSubmit(async (values) => {
|
|
209
|
+
await $fetch('/api/signup', { method: 'POST', body: values });
|
|
210
|
+
});
|
|
211
|
+
</script>
|
|
212
|
+
|
|
213
|
+
<template>
|
|
214
|
+
<form @submit="onSubmit">
|
|
215
|
+
<input v-model="email" v-bind="emailAttrs" type="email" />
|
|
216
|
+
<span>{{ errors.email }}</span>
|
|
217
|
+
<input v-model="password" v-bind="passwordAttrs" type="password" />
|
|
218
|
+
<span>{{ errors.password }}</span>
|
|
219
|
+
<button type="submit">Sign Up</button>
|
|
220
|
+
</form>
|
|
221
|
+
</template>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Real-Time Data Patterns
|
|
227
|
+
|
|
228
|
+
### WebSocket Integration (React)
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
'use client';
|
|
232
|
+
import { useEffect, useRef, useState } from 'react';
|
|
233
|
+
|
|
234
|
+
export function useWebSocket<T>(url: string) {
|
|
235
|
+
const ws = useRef<WebSocket | null>(null);
|
|
236
|
+
const [messages, setMessages] = useState<T[]>([]);
|
|
237
|
+
const [status, setStatus] = useState<'connecting' | 'open' | 'closed'>('connecting');
|
|
238
|
+
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
ws.current = new WebSocket(url);
|
|
241
|
+
ws.current.onopen = () => setStatus('open');
|
|
242
|
+
ws.current.onclose = () => setStatus('closed');
|
|
243
|
+
ws.current.onmessage = (e) => {
|
|
244
|
+
const data: T = JSON.parse(e.data);
|
|
245
|
+
setMessages((prev) => [...prev, data]);
|
|
246
|
+
};
|
|
247
|
+
return () => ws.current?.close();
|
|
248
|
+
}, [url]);
|
|
249
|
+
|
|
250
|
+
const send = (data: unknown) => ws.current?.send(JSON.stringify(data));
|
|
251
|
+
return { messages, status, send };
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Server-Sent Events (SSE)
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
'use client';
|
|
259
|
+
import { useEffect, useState } from 'react';
|
|
260
|
+
|
|
261
|
+
export function useSSE<T>(url: string) {
|
|
262
|
+
const [data, setData] = useState<T | null>(null);
|
|
263
|
+
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
const source = new EventSource(url);
|
|
266
|
+
source.onmessage = (e) => setData(JSON.parse(e.data));
|
|
267
|
+
source.onerror = () => source.close();
|
|
268
|
+
return () => source.close();
|
|
269
|
+
}, [url]);
|
|
270
|
+
|
|
271
|
+
return data;
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Polling with TanStack Query
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
import { useQuery } from '@tanstack/react-query';
|
|
279
|
+
|
|
280
|
+
function Notifications() {
|
|
281
|
+
const { data } = useQuery({
|
|
282
|
+
queryKey: ['notifications'],
|
|
283
|
+
queryFn: () => fetch('/api/notifications').then(r => r.json()),
|
|
284
|
+
refetchInterval: 5_000, // poll every 5 seconds
|
|
285
|
+
refetchIntervalInBackground: false,
|
|
286
|
+
});
|
|
287
|
+
return <ul>{data?.map((n: any) => <li key={n.id}>{n.text}</li>)}</ul>;
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Optimistic UI Updates with TanStack Query
|
|
294
|
+
|
|
295
|
+
```tsx
|
|
296
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
297
|
+
|
|
298
|
+
function useToggleLike(postId: string) {
|
|
299
|
+
const qc = useQueryClient();
|
|
300
|
+
|
|
301
|
+
return useMutation({
|
|
302
|
+
mutationFn: () => fetch(`/api/posts/${postId}/like`, { method: 'POST' }),
|
|
303
|
+
onMutate: async () => {
|
|
304
|
+
await qc.cancelQueries({ queryKey: ['post', postId] });
|
|
305
|
+
const prev = qc.getQueryData<Post>(['post', postId]);
|
|
306
|
+
qc.setQueryData<Post>(['post', postId], (old) =>
|
|
307
|
+
old ? { ...old, liked: !old.liked, likes: old.likes + (old.liked ? -1 : 1) } : old,
|
|
308
|
+
);
|
|
309
|
+
return { prev };
|
|
310
|
+
},
|
|
311
|
+
onError: (_err, _vars, ctx) => {
|
|
312
|
+
if (ctx?.prev) qc.setQueryData(['post', postId], ctx.prev);
|
|
313
|
+
},
|
|
314
|
+
onSettled: () => {
|
|
315
|
+
qc.invalidateQueries({ queryKey: ['post', postId] });
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## tRPC End-to-End Type Safety
|
|
324
|
+
|
|
325
|
+
### Server Setup
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
// server/trpc.ts
|
|
329
|
+
import { initTRPC } from '@trpc/server';
|
|
330
|
+
import { z } from 'zod';
|
|
331
|
+
|
|
332
|
+
const t = initTRPC.create();
|
|
333
|
+
|
|
334
|
+
export const appRouter = t.router({
|
|
335
|
+
user: t.router({
|
|
336
|
+
byId: t.procedure
|
|
337
|
+
.input(z.object({ id: z.string() }))
|
|
338
|
+
.query(async ({ input }) => {
|
|
339
|
+
return db.user.findUnique({ where: { id: input.id } });
|
|
340
|
+
}),
|
|
341
|
+
create: t.procedure
|
|
342
|
+
.input(z.object({ name: z.string(), email: z.string().email() }))
|
|
343
|
+
.mutation(async ({ input }) => {
|
|
344
|
+
return db.user.create({ data: input });
|
|
345
|
+
}),
|
|
346
|
+
}),
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
export type AppRouter = typeof appRouter;
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Client Usage (React)
|
|
353
|
+
|
|
354
|
+
```tsx
|
|
355
|
+
// utils/trpc.ts
|
|
356
|
+
import { createTRPCReact } from '@trpc/react-query';
|
|
357
|
+
import type { AppRouter } from '@/server/trpc';
|
|
358
|
+
|
|
359
|
+
export const trpc = createTRPCReact<AppRouter>();
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
```tsx
|
|
363
|
+
// components/user-profile.tsx
|
|
364
|
+
'use client';
|
|
365
|
+
import { trpc } from '@/utils/trpc';
|
|
366
|
+
|
|
367
|
+
export function UserProfile({ userId }: { userId: string }) {
|
|
368
|
+
const { data: user, isLoading } = trpc.user.byId.useQuery({ id: userId });
|
|
369
|
+
const createUser = trpc.user.create.useMutation();
|
|
370
|
+
|
|
371
|
+
if (isLoading) return <p>Loading...</p>;
|
|
372
|
+
return <div>{user?.name}</div>;
|
|
373
|
+
}
|
|
374
|
+
```
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# SSR & React Server Components Patterns
|
|
2
|
+
|
|
3
|
+
## Next.js App Router: Server Components vs Client Components
|
|
4
|
+
|
|
5
|
+
### Decision Tree
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Does the component need...
|
|
9
|
+
├── Browser APIs (window, document, localStorage)? → 'use client'
|
|
10
|
+
├── Event handlers (onClick, onChange, onSubmit)? → 'use client'
|
|
11
|
+
├── React hooks (useState, useEffect, useRef)? → 'use client'
|
|
12
|
+
├── Third-party client libs (framer-motion, etc.)? → 'use client'
|
|
13
|
+
└── None of the above? → Server Component (default)
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Key principle: keep `'use client'` as far down the tree as possible. Wrap only the
|
|
17
|
+
interactive leaf, not the entire page.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
// app/dashboard/page.tsx — Server Component (default, no directive)
|
|
21
|
+
import { db } from '@/lib/db';
|
|
22
|
+
import { InteractiveChart } from './interactive-chart';
|
|
23
|
+
|
|
24
|
+
export default async function DashboardPage() {
|
|
25
|
+
const stats = await db.stats.findMany(); // runs on server, zero client JS
|
|
26
|
+
return (
|
|
27
|
+
<section>
|
|
28
|
+
<h1>Dashboard</h1>
|
|
29
|
+
<InteractiveChart data={stats} /> {/* only this ships JS */}
|
|
30
|
+
</section>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
// app/dashboard/interactive-chart.tsx
|
|
37
|
+
'use client';
|
|
38
|
+
import { useState } from 'react';
|
|
39
|
+
|
|
40
|
+
export function InteractiveChart({ data }: { data: Stat[] }) {
|
|
41
|
+
const [range, setRange] = useState<'7d' | '30d'>('7d');
|
|
42
|
+
return <canvas /* ... */ />;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Server Actions — Form Submissions & Mutations
|
|
49
|
+
|
|
50
|
+
Server Actions run on the server and can be called directly from Client Components
|
|
51
|
+
or forms without creating an API route.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
// app/contacts/actions.ts
|
|
55
|
+
'use server';
|
|
56
|
+
import { revalidatePath } from 'next/cache';
|
|
57
|
+
import { z } from 'zod';
|
|
58
|
+
|
|
59
|
+
const ContactSchema = z.object({
|
|
60
|
+
name: z.string().min(1),
|
|
61
|
+
email: z.string().email(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export async function createContact(formData: FormData) {
|
|
65
|
+
const parsed = ContactSchema.safeParse(Object.fromEntries(formData));
|
|
66
|
+
if (!parsed.success) {
|
|
67
|
+
return { errors: parsed.error.flatten().fieldErrors };
|
|
68
|
+
}
|
|
69
|
+
await db.contacts.create({ data: parsed.data });
|
|
70
|
+
revalidatePath('/contacts');
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
// app/contacts/page.tsx — progressive enhancement: works without JS
|
|
76
|
+
import { createContact } from './actions';
|
|
77
|
+
|
|
78
|
+
export default function ContactsPage() {
|
|
79
|
+
return (
|
|
80
|
+
<form action={createContact}>
|
|
81
|
+
<input name="name" required />
|
|
82
|
+
<input name="email" type="email" required />
|
|
83
|
+
<button type="submit">Save</button>
|
|
84
|
+
</form>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Streaming + Suspense for Data Fetching
|
|
92
|
+
|
|
93
|
+
Use `loading.tsx` for route-level streaming, or `<Suspense>` for granular control.
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
app/
|
|
97
|
+
├── dashboard/
|
|
98
|
+
│ ├── page.tsx ← main layout, instant shell
|
|
99
|
+
│ ├── loading.tsx ← shown while page.tsx streams
|
|
100
|
+
│ └── components/
|
|
101
|
+
│ └── revenue.tsx ← slow data, wrapped in Suspense
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// app/dashboard/page.tsx
|
|
106
|
+
import { Suspense } from 'react';
|
|
107
|
+
import { RevenueSummary } from './components/revenue';
|
|
108
|
+
import { Skeleton } from '@/components/ui/skeleton';
|
|
109
|
+
|
|
110
|
+
export default function DashboardPage() {
|
|
111
|
+
return (
|
|
112
|
+
<div>
|
|
113
|
+
<h1>Dashboard</h1>
|
|
114
|
+
<Suspense fallback={<Skeleton className="h-48 w-full" />}>
|
|
115
|
+
<RevenueSummary /> {/* async Server Component */}
|
|
116
|
+
</Suspense>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
// app/dashboard/components/revenue.tsx — async Server Component
|
|
124
|
+
export async function RevenueSummary() {
|
|
125
|
+
const data = await fetch('https://api.example.com/revenue', {
|
|
126
|
+
next: { revalidate: 60 },
|
|
127
|
+
}).then(r => r.json());
|
|
128
|
+
return <div>{/* render data */}</div>;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Static vs Dynamic Rendering
|
|
135
|
+
|
|
136
|
+
| Strategy | When to use | How |
|
|
137
|
+
|----------|-------------|-----|
|
|
138
|
+
| **Static (SSG)** | Content rarely changes, same for all users | Default in App Router if no dynamic APIs used |
|
|
139
|
+
| **Dynamic (SSR)** | Per-request data (auth, cookies, search params) | Use `cookies()`, `headers()`, or `searchParams` |
|
|
140
|
+
| **ISR** | Mostly static but needs periodic refresh | `fetch(..., { next: { revalidate: 60 } })` |
|
|
141
|
+
|
|
142
|
+
### ISR — Incremental Static Regeneration
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
// Revalidate every 5 minutes
|
|
146
|
+
export const revalidate = 300;
|
|
147
|
+
|
|
148
|
+
export default async function BlogPage() {
|
|
149
|
+
const posts = await fetch('https://cms.example.com/posts', {
|
|
150
|
+
next: { revalidate: 300 },
|
|
151
|
+
}).then(r => r.json());
|
|
152
|
+
return <PostList posts={posts} />;
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
On-demand revalidation via route handler:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
// app/api/revalidate/route.ts
|
|
160
|
+
import { revalidatePath, revalidateTag } from 'next/cache';
|
|
161
|
+
import { NextRequest } from 'next/server';
|
|
162
|
+
|
|
163
|
+
export async function POST(req: NextRequest) {
|
|
164
|
+
const { tag, path } = await req.json();
|
|
165
|
+
if (tag) revalidateTag(tag);
|
|
166
|
+
if (path) revalidatePath(path);
|
|
167
|
+
return Response.json({ revalidated: true, now: Date.now() });
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Nuxt 3 Patterns
|
|
174
|
+
|
|
175
|
+
### Server Routes & Universal Rendering
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
// server/api/users.get.ts — auto-registered at /api/users
|
|
179
|
+
export default defineEventHandler(async (event) => {
|
|
180
|
+
const users = await db.user.findMany();
|
|
181
|
+
return users;
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### useFetch / useAsyncData
|
|
186
|
+
|
|
187
|
+
```vue
|
|
188
|
+
<script setup lang="ts">
|
|
189
|
+
// useFetch — shorthand, auto-dedupes and SSR-safe
|
|
190
|
+
const { data: users, pending, error } = await useFetch('/api/users');
|
|
191
|
+
|
|
192
|
+
// useAsyncData — more control, custom key
|
|
193
|
+
const { data: profile } = await useAsyncData('profile', () => {
|
|
194
|
+
return $fetch(`/api/users/${route.params.id}`);
|
|
195
|
+
});
|
|
196
|
+
</script>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## SvelteKit Patterns
|
|
202
|
+
|
|
203
|
+
### Load Functions & Form Actions
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
// src/routes/posts/+page.server.ts
|
|
207
|
+
import type { PageServerLoad, Actions } from './$types';
|
|
208
|
+
|
|
209
|
+
export const load: PageServerLoad = async ({ locals }) => {
|
|
210
|
+
const posts = await locals.db.post.findMany();
|
|
211
|
+
return { posts };
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
export const actions: Actions = {
|
|
215
|
+
create: async ({ request, locals }) => {
|
|
216
|
+
const data = await request.formData();
|
|
217
|
+
const title = data.get('title') as string;
|
|
218
|
+
await locals.db.post.create({ data: { title } });
|
|
219
|
+
return { success: true };
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```svelte
|
|
225
|
+
<!-- src/routes/posts/+page.svelte -->
|
|
226
|
+
<script lang="ts">
|
|
227
|
+
import type { PageData } from './$types';
|
|
228
|
+
export let data: PageData;
|
|
229
|
+
</script>
|
|
230
|
+
|
|
231
|
+
<form method="POST" action="?/create">
|
|
232
|
+
<input name="title" required />
|
|
233
|
+
<button>Create</button>
|
|
234
|
+
</form>
|
|
235
|
+
|
|
236
|
+
{#each data.posts as post}
|
|
237
|
+
<article>{post.title}</article>
|
|
238
|
+
{/each}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Middleware Patterns
|
|
244
|
+
|
|
245
|
+
### Next.js Middleware (auth redirects, rewrites, geolocation)
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
// middleware.ts (project root)
|
|
249
|
+
import { NextResponse } from 'next/server';
|
|
250
|
+
import type { NextRequest } from 'next/server';
|
|
251
|
+
|
|
252
|
+
export function middleware(request: NextRequest) {
|
|
253
|
+
const token = request.cookies.get('session')?.value;
|
|
254
|
+
|
|
255
|
+
// Auth redirect
|
|
256
|
+
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
|
|
257
|
+
return NextResponse.redirect(new URL('/login', request.url));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Geolocation-based rewrite
|
|
261
|
+
const country = request.geo?.country ?? 'US';
|
|
262
|
+
if (request.nextUrl.pathname === '/') {
|
|
263
|
+
return NextResponse.rewrite(new URL(`/${country.toLowerCase()}`, request.url));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return NextResponse.next();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export const config = {
|
|
270
|
+
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
|
|
271
|
+
};
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Nuxt Middleware
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
// middleware/auth.global.ts — runs on every route
|
|
278
|
+
export default defineNuxtRouteMiddleware((to) => {
|
|
279
|
+
const { loggedIn } = useUserSession();
|
|
280
|
+
if (!loggedIn.value && to.path.startsWith('/dashboard')) {
|
|
281
|
+
return navigateTo('/login');
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
```
|