mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-06 20:47:36 +00:00
shell: fish functions -> ~/.local/bin executables + bash/zsh ports; manage ~/.agents
- 16 cross-shell utilities converted to sh/bash chezmoi executables: ai c2p cp-appid extract_agents fab gdc gh-sonr-org-migrate ghg-clone ghn gho git-summary launch_omp_livediff_overlay lc-elir mdcp mdp pj (ghg-clone not named ghg: unrelated cargo binary owns that name) - cwd/history helpers stay per-shell: yy c cd clip-last + ghg/ghn/gho cd-wrappers in bash/functions/ and zsh/functions/, loaded by rc files - fish keeps only shell-state functions; converted ones now hit PATH - cp-appid ignore gating moved to .local/bin path - dot_agents/skills: 82 skills materialized from dotstate storage so dot_claude skill symlinks resolve on fresh bootstrap; dotstate profile/symlinks.json released .agents/skills .bashrc .zshrc
This commit is contained in:
1 parent
6776366ce6
commit
6406208b35
637 files changed
+100297
-497
No files matched your search
@@ -0,0 +1,146 @@
|
||||
# sf-create-server-fn: Use createServerFn for Server-Side Logic
|
||||
|
||||
## Priority: CRITICAL
|
||||
|
||||
## Explanation
|
||||
|
||||
`createServerFn()` creates type-safe server functions that can be called from anywhere - loaders, components, or other server functions. The code inside the handler runs only on the server, with automatic RPC for client calls.
|
||||
|
||||
## Bad Example
|
||||
|
||||
```tsx
|
||||
// Using fetch directly - no type safety, manual serialization
|
||||
async function createPost(data: CreatePostInput) {
|
||||
const response = await fetch('/api/posts', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
if (!response.ok) throw new Error('Failed to create post')
|
||||
return response.json()
|
||||
}
|
||||
|
||||
// Or using API routes - more boilerplate
|
||||
// api/posts.ts
|
||||
export async function POST(request: Request) {
|
||||
const data = await request.json()
|
||||
// No type safety from client
|
||||
const post = await db.posts.create({ data })
|
||||
return new Response(JSON.stringify(post))
|
||||
}
|
||||
```
|
||||
|
||||
## Good Example
|
||||
|
||||
```tsx
|
||||
// lib/posts.functions.ts
|
||||
import { createServerFn } from '@tanstack/react-start'
|
||||
import { z } from 'zod'
|
||||
import { db } from './db.server'
|
||||
|
||||
const createPostSchema = z.object({
|
||||
title: z.string().min(1).max(200),
|
||||
content: z.string().min(1),
|
||||
published: z.boolean().default(false),
|
||||
})
|
||||
|
||||
export const createPost = createServerFn({ method: 'POST' })
|
||||
.inputValidator(createPostSchema)
|
||||
.handler(async ({ data }) => {
|
||||
// This code only runs on the server
|
||||
const post = await db.posts.create({
|
||||
data: {
|
||||
title: data.title,
|
||||
content: data.content,
|
||||
published: data.published,
|
||||
},
|
||||
})
|
||||
return post
|
||||
})
|
||||
|
||||
// Usage in component
|
||||
function CreatePostForm() {
|
||||
const createPostMutation = useServerFn(createPost)
|
||||
|
||||
const handleSubmit = async (formData: FormData) => {
|
||||
try {
|
||||
const post = await createPostMutation({
|
||||
data: {
|
||||
title: formData.get('title') as string,
|
||||
content: formData.get('content') as string,
|
||||
published: false,
|
||||
},
|
||||
})
|
||||
// post is fully typed
|
||||
console.log('Created post:', post.id)
|
||||
} catch (error) {
|
||||
console.error('Failed to create post:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Good Example: GET Function for Data Fetching
|
||||
|
||||
```tsx
|
||||
// lib/posts.functions.ts
|
||||
export const getPosts = createServerFn() // GET is default
|
||||
.handler(async () => {
|
||||
const posts = await db.posts.findMany({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 20,
|
||||
})
|
||||
return posts
|
||||
})
|
||||
|
||||
export const getPost = createServerFn()
|
||||
.validator(z.object({ id: z.string() }))
|
||||
.handler(async ({ data }) => {
|
||||
const post = await db.posts.findUnique({
|
||||
where: { id: data.id },
|
||||
})
|
||||
if (!post) {
|
||||
throw notFound()
|
||||
}
|
||||
return post
|
||||
})
|
||||
|
||||
// Usage in route loader
|
||||
export const Route = createFileRoute('/posts/$postId')({
|
||||
loader: async ({ params }) => {
|
||||
return await getPost({ data: { id: params.postId } })
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Good Example: With Context and Dependencies
|
||||
|
||||
```tsx
|
||||
// Compose server functions
|
||||
export const getPostWithComments = createServerFn()
|
||||
.inputValidator(z.object({ postId: z.string() }))
|
||||
.handler(async ({ data }) => {
|
||||
const [post, comments] = await Promise.all([
|
||||
getPost({ data: { id: data.postId } }),
|
||||
getComments({ data: { postId: data.postId } }),
|
||||
])
|
||||
|
||||
return { post, comments }
|
||||
})
|
||||
```
|
||||
|
||||
## Key Benefits
|
||||
|
||||
- **Type safety**: Input/output types flow through client and server
|
||||
- **Automatic serialization**: No manual JSON parsing
|
||||
- **Code splitting**: Server code never reaches client bundle
|
||||
- **Composable**: Call from loaders, components, or other server functions
|
||||
- **Validation**: Built-in input validation with schema libraries
|
||||
|
||||
## Context
|
||||
|
||||
- Default method is GET (idempotent, cacheable)
|
||||
- Use POST for mutations that change data
|
||||
- Server functions are RPC calls under the hood
|
||||
- Validation errors are properly typed and serialized
|
||||
- Import is safe on client - build process replaces with RPC stub
|
||||
Reference in new issue
Block a user