autoworkflow 3.1.5 → 3.6.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.
Files changed (124) hide show
  1. package/.claude/commands/analyze.md +19 -0
  2. package/.claude/commands/audit.md +26 -0
  3. package/.claude/commands/build.md +39 -0
  4. package/.claude/commands/commit.md +25 -0
  5. package/.claude/commands/fix.md +23 -0
  6. package/.claude/commands/plan.md +18 -0
  7. package/.claude/commands/suggest.md +23 -0
  8. package/.claude/commands/verify.md +18 -0
  9. package/.claude/hooks/post-bash-router.sh +20 -0
  10. package/.claude/hooks/post-commit.sh +140 -0
  11. package/.claude/hooks/post-edit.sh +190 -17
  12. package/.claude/hooks/pre-edit.sh +221 -0
  13. package/.claude/hooks/session-check.sh +90 -0
  14. package/.claude/settings.json +56 -6
  15. package/.claude/settings.local.json +5 -1
  16. package/.claude/skills/actix.md +337 -0
  17. package/.claude/skills/alembic.md +504 -0
  18. package/.claude/skills/angular.md +237 -0
  19. package/.claude/skills/api-design.md +187 -0
  20. package/.claude/skills/aspnet-core.md +377 -0
  21. package/.claude/skills/astro.md +245 -0
  22. package/.claude/skills/auth-clerk.md +327 -0
  23. package/.claude/skills/auth-firebase.md +367 -0
  24. package/.claude/skills/auth-nextauth.md +359 -0
  25. package/.claude/skills/auth-supabase.md +368 -0
  26. package/.claude/skills/axum.md +386 -0
  27. package/.claude/skills/blazor.md +456 -0
  28. package/.claude/skills/chi.md +348 -0
  29. package/.claude/skills/code-review.md +133 -0
  30. package/.claude/skills/csharp.md +296 -0
  31. package/.claude/skills/css-modules.md +325 -0
  32. package/.claude/skills/cypress.md +343 -0
  33. package/.claude/skills/debugging.md +133 -0
  34. package/.claude/skills/diesel.md +392 -0
  35. package/.claude/skills/django.md +301 -0
  36. package/.claude/skills/docker.md +319 -0
  37. package/.claude/skills/doctrine.md +473 -0
  38. package/.claude/skills/documentation.md +182 -0
  39. package/.claude/skills/dotnet.md +409 -0
  40. package/.claude/skills/drizzle.md +293 -0
  41. package/.claude/skills/echo.md +321 -0
  42. package/.claude/skills/eloquent.md +256 -0
  43. package/.claude/skills/emotion.md +426 -0
  44. package/.claude/skills/entity-framework.md +370 -0
  45. package/.claude/skills/express.md +316 -0
  46. package/.claude/skills/fastapi.md +329 -0
  47. package/.claude/skills/fastify.md +299 -0
  48. package/.claude/skills/fiber.md +315 -0
  49. package/.claude/skills/flask.md +322 -0
  50. package/.claude/skills/gin.md +342 -0
  51. package/.claude/skills/git.md +116 -0
  52. package/.claude/skills/github-actions.md +353 -0
  53. package/.claude/skills/go.md +377 -0
  54. package/.claude/skills/gorm.md +409 -0
  55. package/.claude/skills/graphql.md +478 -0
  56. package/.claude/skills/hibernate.md +379 -0
  57. package/.claude/skills/hono.md +306 -0
  58. package/.claude/skills/java.md +400 -0
  59. package/.claude/skills/jest.md +313 -0
  60. package/.claude/skills/jpa.md +282 -0
  61. package/.claude/skills/kotlin.md +347 -0
  62. package/.claude/skills/kubernetes.md +363 -0
  63. package/.claude/skills/laravel.md +414 -0
  64. package/.claude/skills/mcp-browser.md +320 -0
  65. package/.claude/skills/mcp-database.md +219 -0
  66. package/.claude/skills/mcp-fetch.md +241 -0
  67. package/.claude/skills/mcp-filesystem.md +204 -0
  68. package/.claude/skills/mcp-github.md +217 -0
  69. package/.claude/skills/mcp-memory.md +240 -0
  70. package/.claude/skills/mcp-search.md +218 -0
  71. package/.claude/skills/mcp-slack.md +262 -0
  72. package/.claude/skills/micronaut.md +388 -0
  73. package/.claude/skills/mongodb.md +319 -0
  74. package/.claude/skills/mongoose.md +355 -0
  75. package/.claude/skills/mysql.md +281 -0
  76. package/.claude/skills/nestjs.md +335 -0
  77. package/.claude/skills/nextjs-app-router.md +260 -0
  78. package/.claude/skills/nextjs-pages.md +172 -0
  79. package/.claude/skills/nuxt.md +202 -0
  80. package/.claude/skills/openapi.md +489 -0
  81. package/.claude/skills/performance.md +199 -0
  82. package/.claude/skills/php.md +398 -0
  83. package/.claude/skills/playwright.md +371 -0
  84. package/.claude/skills/postgresql.md +257 -0
  85. package/.claude/skills/prisma.md +293 -0
  86. package/.claude/skills/pydantic.md +304 -0
  87. package/.claude/skills/pytest.md +313 -0
  88. package/.claude/skills/python.md +272 -0
  89. package/.claude/skills/quarkus.md +377 -0
  90. package/.claude/skills/react.md +230 -0
  91. package/.claude/skills/redis.md +391 -0
  92. package/.claude/skills/refactoring.md +143 -0
  93. package/.claude/skills/remix.md +246 -0
  94. package/.claude/skills/rest-api.md +490 -0
  95. package/.claude/skills/rocket.md +366 -0
  96. package/.claude/skills/rust.md +341 -0
  97. package/.claude/skills/sass.md +380 -0
  98. package/.claude/skills/sea-orm.md +382 -0
  99. package/.claude/skills/security.md +167 -0
  100. package/.claude/skills/sequelize.md +395 -0
  101. package/.claude/skills/spring-boot.md +416 -0
  102. package/.claude/skills/sqlalchemy.md +269 -0
  103. package/.claude/skills/sqlx-rust.md +408 -0
  104. package/.claude/skills/state-jotai.md +346 -0
  105. package/.claude/skills/state-mobx.md +353 -0
  106. package/.claude/skills/state-pinia.md +431 -0
  107. package/.claude/skills/state-redux.md +337 -0
  108. package/.claude/skills/state-tanstack-query.md +434 -0
  109. package/.claude/skills/state-zustand.md +340 -0
  110. package/.claude/skills/styled-components.md +403 -0
  111. package/.claude/skills/svelte.md +238 -0
  112. package/.claude/skills/sveltekit.md +207 -0
  113. package/.claude/skills/symfony.md +437 -0
  114. package/.claude/skills/tailwind.md +279 -0
  115. package/.claude/skills/terraform.md +394 -0
  116. package/.claude/skills/testing-library.md +371 -0
  117. package/.claude/skills/trpc.md +426 -0
  118. package/.claude/skills/typeorm.md +368 -0
  119. package/.claude/skills/vitest.md +330 -0
  120. package/.claude/skills/vue.md +202 -0
  121. package/.claude/skills/warp.md +365 -0
  122. package/README.md +163 -52
  123. package/package.json +1 -1
  124. package/system/triggers.md +256 -17
@@ -0,0 +1,260 @@
1
+ # Next.js App Router Skill
2
+
3
+ ## File Conventions
4
+ \`\`\`
5
+ app/
6
+ ├── layout.tsx # Root layout (required)
7
+ ├── page.tsx # Home page (/)
8
+ ├── loading.tsx # Loading UI (Suspense boundary)
9
+ ├── error.tsx # Error boundary
10
+ ├── not-found.tsx # 404 page
11
+ ├── global-error.tsx # Root error boundary
12
+ ├── (auth)/ # Route group (doesn't affect URL)
13
+ │ ├── login/page.tsx # /login
14
+ │ └── register/page.tsx # /register
15
+ ├── dashboard/
16
+ │ ├── layout.tsx # Dashboard layout
17
+ │ ├── @modal/(.)edit/page.tsx # Parallel route + intercepting
18
+ │ └── [id]/page.tsx # /dashboard/123
19
+ └── api/
20
+ └── users/route.ts # API route handler
21
+ \`\`\`
22
+
23
+ ## Server vs Client Components
24
+ \`\`\`tsx
25
+ // ✅ SERVER COMPONENT (default)
26
+ // Can: fetch data, access backend, use async/await
27
+ // Cannot: useState, useEffect, onClick
28
+ export default async function ProductPage({ params }) {
29
+ const product = await db.product.findUnique({ where: { id: params.id } });
30
+ return <ProductDetails product={product} />;
31
+ }
32
+
33
+ // ✅ CLIENT COMPONENT - add 'use client'
34
+ 'use client';
35
+ export function AddToCartButton({ productId }) {
36
+ const [loading, setLoading] = useState(false);
37
+ return <button onClick={() => addToCart(productId)}>Add</button>;
38
+ }
39
+ \`\`\`
40
+
41
+ ## Metadata & SEO
42
+ \`\`\`tsx
43
+ // Static metadata
44
+ export const metadata: Metadata = {
45
+ title: 'My App',
46
+ description: 'App description',
47
+ openGraph: {
48
+ title: 'My App',
49
+ description: 'App description',
50
+ images: ['/og-image.png'],
51
+ },
52
+ };
53
+
54
+ // Dynamic metadata
55
+ export async function generateMetadata({ params }): Promise<Metadata> {
56
+ const product = await getProduct(params.id);
57
+ return {
58
+ title: product.name,
59
+ description: product.description,
60
+ openGraph: {
61
+ images: [product.image],
62
+ },
63
+ };
64
+ }
65
+
66
+ // JSON-LD structured data
67
+ export default function ProductPage({ product }) {
68
+ const jsonLd = {
69
+ '@context': 'https://schema.org',
70
+ '@type': 'Product',
71
+ name: product.name,
72
+ description: product.description,
73
+ };
74
+
75
+ return (
76
+ <>
77
+ <script
78
+ type="application/ld+json"
79
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
80
+ />
81
+ <ProductDetails product={product} />
82
+ </>
83
+ );
84
+ }
85
+ \`\`\`
86
+
87
+ ## Middleware
88
+ \`\`\`typescript
89
+ // middleware.ts (root of project)
90
+ import { NextResponse } from 'next/server';
91
+ import type { NextRequest } from 'next/server';
92
+
93
+ export function middleware(request: NextRequest) {
94
+ // Auth check
95
+ const token = request.cookies.get('token');
96
+ if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
97
+ return NextResponse.redirect(new URL('/login', request.url));
98
+ }
99
+
100
+ // Add headers
101
+ const response = NextResponse.next();
102
+ response.headers.set('x-request-id', crypto.randomUUID());
103
+
104
+ return response;
105
+ }
106
+
107
+ export const config = {
108
+ matcher: ['/dashboard/:path*', '/api/:path*'],
109
+ };
110
+ \`\`\`
111
+
112
+ ## Caching & Revalidation
113
+ \`\`\`tsx
114
+ // Time-based revalidation
115
+ async function getProducts() {
116
+ const res = await fetch('https://api.example.com/products', {
117
+ next: { revalidate: 3600 } // Revalidate every hour
118
+ });
119
+ return res.json();
120
+ }
121
+
122
+ // On-demand revalidation
123
+ import { revalidatePath, revalidateTag } from 'next/cache';
124
+
125
+ // In Server Action
126
+ 'use server';
127
+ export async function updateProduct(id: string, data: FormData) {
128
+ await db.product.update({ where: { id }, data });
129
+ revalidatePath('/products'); // Revalidate specific path
130
+ revalidateTag('products'); // Revalidate by tag
131
+ }
132
+
133
+ // Tag your fetches
134
+ fetch('https://api.example.com/products', {
135
+ next: { tags: ['products'] }
136
+ });
137
+ \`\`\`
138
+
139
+ ## Streaming with Suspense
140
+ \`\`\`tsx
141
+ import { Suspense } from 'react';
142
+
143
+ export default function Dashboard() {
144
+ return (
145
+ <div>
146
+ <h1>Dashboard</h1>
147
+
148
+ {/* Show skeleton while loading */}
149
+ <Suspense fallback={<StatsSkeleton />}>
150
+ <Stats />
151
+ </Suspense>
152
+
153
+ <Suspense fallback={<ChartSkeleton />}>
154
+ <RevenueChart />
155
+ </Suspense>
156
+ </div>
157
+ );
158
+ }
159
+
160
+ // Each component can be async and stream independently
161
+ async function Stats() {
162
+ const stats = await getStats(); // Slow query
163
+ return <StatsDisplay stats={stats} />;
164
+ }
165
+ \`\`\`
166
+
167
+ ## Error Handling
168
+ \`\`\`tsx
169
+ // error.tsx - catches errors in this segment
170
+ 'use client';
171
+
172
+ export default function Error({
173
+ error,
174
+ reset,
175
+ }: {
176
+ error: Error & { digest?: string };
177
+ reset: () => void;
178
+ }) {
179
+ useEffect(() => {
180
+ logError(error); // Send to error tracking
181
+ }, [error]);
182
+
183
+ return (
184
+ <div>
185
+ <h2>Something went wrong!</h2>
186
+ <button onClick={() => reset()}>Try again</button>
187
+ </div>
188
+ );
189
+ }
190
+
191
+ // not-found.tsx - custom 404
192
+ export default function NotFound() {
193
+ return (
194
+ <div>
195
+ <h2>Page Not Found</h2>
196
+ <Link href="/">Go home</Link>
197
+ </div>
198
+ );
199
+ }
200
+
201
+ // Trigger not-found programmatically
202
+ import { notFound } from 'next/navigation';
203
+
204
+ async function getUser(id: string) {
205
+ const user = await db.user.findUnique({ where: { id } });
206
+ if (!user) notFound();
207
+ return user;
208
+ }
209
+ \`\`\`
210
+
211
+ ## Server Actions
212
+ \`\`\`tsx
213
+ 'use server';
214
+
215
+ import { revalidatePath } from 'next/cache';
216
+ import { redirect } from 'next/navigation';
217
+
218
+ export async function createUser(formData: FormData) {
219
+ const data = {
220
+ name: formData.get('name') as string,
221
+ email: formData.get('email') as string,
222
+ };
223
+
224
+ // Validate
225
+ const result = userSchema.safeParse(data);
226
+ if (!result.success) {
227
+ return { error: result.error.flatten() };
228
+ }
229
+
230
+ // Create
231
+ await prisma.user.create({ data: result.data });
232
+
233
+ // Revalidate and redirect
234
+ revalidatePath('/users');
235
+ redirect('/users');
236
+ }
237
+
238
+ // Usage in form
239
+ <form action={createUser}>
240
+ <input name="name" required />
241
+ <input name="email" type="email" required />
242
+ <button type="submit">Create</button>
243
+ </form>
244
+ \`\`\`
245
+
246
+ ## ❌ DON'T
247
+ - Use 'use client' unless you need interactivity
248
+ - Use getServerSideProps (that's Pages Router)
249
+ - Put sensitive logic in client components
250
+ - Forget to handle loading/error states
251
+ - Over-fetch in Server Components (no deduplication)
252
+
253
+ ## ✅ DO
254
+ - Default to Server Components
255
+ - Keep client components small and at leaf nodes
256
+ - Use Server Actions for form submissions
257
+ - Add metadata for SEO
258
+ - Use Suspense for streaming
259
+ - Implement proper error boundaries
260
+ - Use revalidation strategies appropriately
@@ -0,0 +1,172 @@
1
+ # Next.js Pages Router Skill
2
+
3
+ ## Data Fetching
4
+ \`\`\`tsx
5
+ // getStaticProps - Build time (ISR with revalidate)
6
+ export async function getStaticProps() {
7
+ const posts = await getPosts();
8
+ return {
9
+ props: { posts },
10
+ revalidate: 60, // Regenerate every 60 seconds
11
+ notFound: false, // Return 404 if true
12
+ };
13
+ }
14
+
15
+ // getServerSideProps - Request time (SSR)
16
+ export async function getServerSideProps(context) {
17
+ const { params, req, res, query } = context;
18
+
19
+ // Access cookies/headers
20
+ const token = req.cookies.token;
21
+ if (!token) {
22
+ return { redirect: { destination: '/login', permanent: false } };
23
+ }
24
+
25
+ const user = await getUser(params.id);
26
+ if (!user) {
27
+ return { notFound: true };
28
+ }
29
+
30
+ return { props: { user } };
31
+ }
32
+
33
+ // getStaticPaths - Dynamic static pages
34
+ export async function getStaticPaths() {
35
+ const posts = await getAllPosts();
36
+ return {
37
+ paths: posts.map(p => ({ params: { id: p.id } })),
38
+ fallback: 'blocking' // 'false' | 'blocking' | true
39
+ };
40
+ }
41
+ \`\`\`
42
+
43
+ ## Custom _app.tsx
44
+ \`\`\`tsx
45
+ // pages/_app.tsx
46
+ import type { AppProps } from 'next/app';
47
+ import { SessionProvider } from 'next-auth/react';
48
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
49
+
50
+ const queryClient = new QueryClient();
51
+
52
+ export default function App({ Component, pageProps }: AppProps) {
53
+ // Use per-page layouts
54
+ const getLayout = Component.getLayout ?? ((page) => page);
55
+
56
+ return (
57
+ <SessionProvider session={pageProps.session}>
58
+ <QueryClientProvider client={queryClient}>
59
+ {getLayout(<Component {...pageProps} />)}
60
+ </QueryClientProvider>
61
+ </SessionProvider>
62
+ );
63
+ }
64
+ \`\`\`
65
+
66
+ ## Custom _document.tsx
67
+ \`\`\`tsx
68
+ // pages/_document.tsx
69
+ import { Html, Head, Main, NextScript } from 'next/document';
70
+
71
+ export default function Document() {
72
+ return (
73
+ <Html lang="en">
74
+ <Head>
75
+ <link rel="icon" href="/favicon.ico" />
76
+ <meta name="theme-color" content="#000000" />
77
+ </Head>
78
+ <body>
79
+ <Main />
80
+ <NextScript />
81
+ </body>
82
+ </Html>
83
+ );
84
+ }
85
+ \`\`\`
86
+
87
+ ## API Routes with Middleware
88
+ \`\`\`typescript
89
+ // pages/api/users.ts
90
+ import type { NextApiRequest, NextApiResponse } from 'next';
91
+
92
+ // Middleware wrapper
93
+ function withAuth(handler: Function) {
94
+ return async (req: NextApiRequest, res: NextApiResponse) => {
95
+ const token = req.headers.authorization?.split(' ')[1];
96
+ if (!token) {
97
+ return res.status(401).json({ error: 'Unauthorized' });
98
+ }
99
+
100
+ try {
101
+ req.user = verifyToken(token);
102
+ return handler(req, res);
103
+ } catch {
104
+ return res.status(401).json({ error: 'Invalid token' });
105
+ }
106
+ };
107
+ }
108
+
109
+ async function handler(req: NextApiRequest, res: NextApiResponse) {
110
+ switch (req.method) {
111
+ case 'GET':
112
+ const users = await prisma.user.findMany();
113
+ return res.status(200).json(users);
114
+ case 'POST':
115
+ const user = await prisma.user.create({ data: req.body });
116
+ return res.status(201).json(user);
117
+ default:
118
+ res.setHeader('Allow', ['GET', 'POST']);
119
+ return res.status(405).end();
120
+ }
121
+ }
122
+
123
+ export default withAuth(handler);
124
+ \`\`\`
125
+
126
+ ## Middleware (Edge)
127
+ \`\`\`typescript
128
+ // middleware.ts (root level)
129
+ import { NextResponse } from 'next/server';
130
+ import type { NextRequest } from 'next/server';
131
+
132
+ export function middleware(request: NextRequest) {
133
+ const token = request.cookies.get('token');
134
+
135
+ if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
136
+ return NextResponse.redirect(new URL('/login', request.url));
137
+ }
138
+
139
+ return NextResponse.next();
140
+ }
141
+
142
+ export const config = {
143
+ matcher: ['/dashboard/:path*', '/api/protected/:path*'],
144
+ };
145
+ \`\`\`
146
+
147
+ ## Per-Page Layouts
148
+ \`\`\`tsx
149
+ // pages/dashboard/index.tsx
150
+ import DashboardLayout from '@/layouts/DashboardLayout';
151
+
152
+ export default function DashboardPage() {
153
+ return <div>Dashboard Content</div>;
154
+ }
155
+
156
+ DashboardPage.getLayout = function getLayout(page: ReactElement) {
157
+ return <DashboardLayout>{page}</DashboardLayout>;
158
+ };
159
+ \`\`\`
160
+
161
+ ## ❌ DON'T
162
+ - Use getInitialProps (legacy, blocks optimization)
163
+ - Fetch data in useEffect when SSR/SSG works
164
+ - Put secrets in client-side code
165
+ - Forget fallback handling in getStaticPaths
166
+
167
+ ## ✅ DO
168
+ - Use getStaticProps with revalidate for cacheable data
169
+ - Use getServerSideProps only when needed (auth, real-time)
170
+ - Handle loading/error states for fallback pages
171
+ - Use middleware for auth/redirects
172
+ - Implement per-page layouts for complex UIs
@@ -0,0 +1,202 @@
1
+ # Nuxt 3 Skill
2
+
3
+ ## Directory Structure
4
+ \`\`\`
5
+ app.vue # Root component
6
+ nuxt.config.ts # Configuration
7
+ pages/ # File-based routing
8
+ index.vue # /
9
+ users/
10
+ index.vue # /users
11
+ [id].vue # /users/:id
12
+ components/ # Auto-imported
13
+ composables/ # Auto-imported (useX)
14
+ server/
15
+ api/ # API routes
16
+ middleware/ # Server middleware
17
+ middleware/ # Route middleware
18
+ plugins/ # App plugins
19
+ layouts/ # Layouts
20
+ \`\`\`
21
+
22
+ ## Data Fetching
23
+ \`\`\`vue
24
+ <script setup>
25
+ // useFetch - SSR + client hydration, auto-deduplication
26
+ const { data: users, pending, error, refresh } = await useFetch('/api/users');
27
+
28
+ // useAsyncData - for custom async functions
29
+ const { data: user } = await useAsyncData('user', () => {
30
+ return $fetch(\`/api/users/\${route.params.id}\`);
31
+ });
32
+
33
+ // useLazyFetch - doesn't block navigation
34
+ const { data: stats, pending } = useLazyFetch('/api/stats');
35
+
36
+ // With options
37
+ const { data } = await useFetch('/api/users', {
38
+ query: { page: 1, limit: 10 },
39
+ headers: { Authorization: \`Bearer \${token}\` },
40
+ transform: (data) => data.users, // Transform response
41
+ pick: ['id', 'name'], // Only pick these fields
42
+ watch: [page], // Refetch when page changes
43
+ });
44
+ \`\`\`
45
+
46
+ ## Server API Routes
47
+ \`\`\`typescript
48
+ // server/api/users.get.ts
49
+ export default defineEventHandler(async (event) => {
50
+ return await prisma.user.findMany();
51
+ });
52
+
53
+ // server/api/users.post.ts
54
+ export default defineEventHandler(async (event) => {
55
+ const body = await readBody(event);
56
+
57
+ // Validation
58
+ if (!body.email) {
59
+ throw createError({ statusCode: 400, message: 'Email required' });
60
+ }
61
+
62
+ return await prisma.user.create({ data: body });
63
+ });
64
+
65
+ // server/api/users/[id].get.ts
66
+ export default defineEventHandler(async (event) => {
67
+ const id = getRouterParam(event, 'id');
68
+ const user = await prisma.user.findUnique({ where: { id } });
69
+
70
+ if (!user) {
71
+ throw createError({ statusCode: 404, message: 'User not found' });
72
+ }
73
+
74
+ return user;
75
+ });
76
+ \`\`\`
77
+
78
+ ## Middleware
79
+ \`\`\`typescript
80
+ // middleware/auth.ts
81
+ export default defineNuxtRouteMiddleware((to, from) => {
82
+ const { user } = useAuth();
83
+
84
+ if (!user.value && to.path !== '/login') {
85
+ return navigateTo('/login');
86
+ }
87
+ });
88
+
89
+ // Apply to page
90
+ <script setup>
91
+ definePageMeta({
92
+ middleware: 'auth',
93
+ // Or inline:
94
+ // middleware: [(to, from) => { ... }]
95
+ });
96
+ </script>
97
+
98
+ // Global middleware
99
+ // middleware/auth.global.ts
100
+ export default defineNuxtRouteMiddleware((to) => {
101
+ // Runs on every navigation
102
+ });
103
+ \`\`\`
104
+
105
+ ## Plugins
106
+ \`\`\`typescript
107
+ // plugins/api.ts
108
+ export default defineNuxtPlugin((nuxtApp) => {
109
+ const api = $fetch.create({
110
+ baseURL: '/api',
111
+ onRequest({ options }) {
112
+ const token = useCookie('token');
113
+ if (token.value) {
114
+ options.headers = { Authorization: \`Bearer \${token.value}\` };
115
+ }
116
+ },
117
+ });
118
+
119
+ return {
120
+ provide: { api }
121
+ };
122
+ });
123
+
124
+ // Usage in components
125
+ const { $api } = useNuxtApp();
126
+ const users = await $api('/users');
127
+ \`\`\`
128
+
129
+ ## State Management
130
+ \`\`\`typescript
131
+ // composables/useAuth.ts
132
+ export const useAuth = () => {
133
+ const user = useState<User | null>('user', () => null);
134
+
135
+ const login = async (email: string, password: string) => {
136
+ user.value = await $fetch('/api/auth/login', {
137
+ method: 'POST',
138
+ body: { email, password }
139
+ });
140
+ };
141
+
142
+ const logout = () => {
143
+ user.value = null;
144
+ navigateTo('/login');
145
+ };
146
+
147
+ return { user, login, logout };
148
+ };
149
+ \`\`\`
150
+
151
+ ## Runtime Config
152
+ \`\`\`typescript
153
+ // nuxt.config.ts
154
+ export default defineNuxtConfig({
155
+ runtimeConfig: {
156
+ // Server-only (not exposed to client)
157
+ apiSecret: process.env.API_SECRET,
158
+
159
+ // Public (exposed to client)
160
+ public: {
161
+ apiBase: process.env.API_BASE || '/api'
162
+ }
163
+ }
164
+ });
165
+
166
+ // Usage
167
+ const config = useRuntimeConfig();
168
+ // Server: config.apiSecret
169
+ // Client: config.public.apiBase
170
+ \`\`\`
171
+
172
+ ## Error Handling
173
+ \`\`\`vue
174
+ <!-- error.vue (root level) -->
175
+ <script setup>
176
+ const props = defineProps<{ error: { statusCode: number; message: string } }>();
177
+
178
+ const handleError = () => clearError({ redirect: '/' });
179
+ </script>
180
+
181
+ <template>
182
+ <div>
183
+ <h1>{{ error.statusCode }}</h1>
184
+ <p>{{ error.message }}</p>
185
+ <button @click="handleError">Go Home</button>
186
+ </div>
187
+ </template>
188
+ \`\`\`
189
+
190
+ ## ❌ DON'T
191
+ - Use useFetch in non-setup contexts
192
+ - Forget to handle pending/error states
193
+ - Put secrets in runtimeConfig.public
194
+ - Use $fetch without useFetch for SSR data
195
+
196
+ ## ✅ DO
197
+ - Use useFetch for SSR data fetching
198
+ - Use useState for shared state
199
+ - Use auto-imports (no manual imports needed)
200
+ - Use middleware for auth/guards
201
+ - Use runtimeConfig for environment variables
202
+ - Handle errors with createError