@sylphx/flow 1.0.5 → 1.1.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.
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: Tech Stack (Recommended)
3
+ description: Opinionated stack (Next.js, PandaCSS, GraphQL, Pothos, Drizzle) - optimized for LLM accuracy
4
+ ---
5
+
6
+ # Technical Stack
7
+
8
+ Scalable, secure SaaS stack. Type safety, performance, serverless. Validate with E2E (Playwright), monitor with Sentry.
9
+
10
+ ## Domain-Driven Architecture
11
+
12
+ Feature-based layout: `src/features/<domain>/` (frontend), `src/graphql/<domain>/` (backend). Colocate related code. Cross-domain via explicit exports.
13
+
14
+ **Frontend domains**: `src/features/<domain>/` → `components/`, `hooks/`, `store/`, `services/`, `utils/`, `types.ts`
15
+ **Backend domains**: `src/graphql/<domain>/` → `types/`, `queries.ts`, `mutations.ts`, `subscriptions.ts`, `loaders.ts` (DataLoader for N+1)
16
+ **Shared infra**: `src/lib/` (clients), `src/app/` (routes, providers)
17
+
18
+ ## Frontend Stack
19
+
20
+ **Framework**: Next.js App Router (routing, SSR/SSG, Turbopack). `src/app/(app)/dashboard/page.tsx`
21
+
22
+ **UI**: React + Radix UI primitives (a11y). Prefer Radix for structural/interactive, custom only when Radix lacks. `src/features/<domain>/components/`
23
+
24
+ **State**: Zustand (global sessions). Avoid Redux. `src/features/<domain>/store/`
25
+
26
+ **Styling**: PandaCSS (type-safe atomic CSS-in-JS, zero-runtime, <10KB)
27
+ - **Tokens/Themes**: `panda.config.ts` semantics (`colors.primary.500`), `_dark`/CSS vars
28
+ - **Atomic**: Inline JSX (`bg-primary-500 p-4`), `css({ color: 'red' })` merges
29
+ - **Recipes**: `cva` (Button variants), `sva` slots (Card), `jsx: ['Button']` track
30
+ - **Merging**: `cx(recipe(), css({ bg: 'red' }))` overrides
31
+ - **Optimize**: `staticCss: { recipes: '*' }`, purge globs, `panda analyze`, Next.js plugins
32
+
33
+ **Hooks**: react-use (localStorage, useMeasure, useDebounce, sensors), urql (GraphQL cache, SSR, subscriptions, offline, batching)
34
+
35
+ **Auth**: Better Auth (passkey-first, 2FA), reCAPTCHA (bot mitigation)
36
+
37
+ ## Backend Stack
38
+
39
+ GraphQL-first, serverless. `src/graphql/<domain>/`
40
+
41
+ **Schema/Server**: Pothos (code-first), Yoga. `gql.tada` for all GraphQL docs (never raw templates), `graphql-scalars` for custom scalars
42
+ - Modular `queryField`, typed client hooks via `gql.tada`, colocate operations with components/pages, DataLoader in `loaders.ts` (batch, cache, prevent N+1)
43
+
44
+ **Auth**: Better Auth (JWT/Redis denylist), rotate tokens
45
+
46
+ **Request Context**: AsyncLocalStorage with `headers()`/`cookies()` → tiny accessors (`getAuthSession()`, `getLocale()`) instead of passing context objects
47
+
48
+ **ORM**: Drizzle (queries/migrations). **Never** raw SQL except unavoidable complex cases (use parameterized placeholders). Query builder methods (`eq`, `and`, `or`). Schemas/queries per domain: `src/domains/<domain>/data/`
49
+ - `db.select().from(users).where(eq(users.id, userId))`
50
+
51
+ **Security**: @simplewebauthn/server, Redis limits
52
+
53
+ ## Data Layer
54
+
55
+ **DB**: PostgreSQL (Neon/pgBouncer), RLS. Scale: Partition (logs/date)
56
+
57
+ **Cache/RT**: Upstash Redis (cache/pubsub/streams). TTL (24h), event streams
58
+
59
+ ## Payments
60
+
61
+ **Billing**: Stripe (Checkout/Portal/Invoices/webhooks idempotent). Wallet credit on session complete, 3x retry
62
+
63
+ ## DevOps
64
+
65
+ **Local**: Docker Compose (stack), bun, Biome (linting/formatting), Lefthook (Git hooks)
66
+ - **Biome Ignore**: Tests (`__tests__/**`, `*.test.*`), generated (`*.generated.*`, `dist/**`), project-specific (`styled-system`, `*.gql.tada.ts`, `drizzle`, `.next`)
67
+ - **Biome Config**: Recommended + custom flow, ignoreUnknown false
68
+ - **Lefthook**: Pre-commit (Biome, type-check), pre-push (tests). `bun add -D lefthook`, `lefthook install`
69
+ - **Entry**: `bun install && migrate && dev`, Lefthook auto-runs, `biome check .` in CI
70
+
71
+ **Deploy**: Serverless (Vercel), GraphQL BFF. CI: Actions, 99.9% SLO alerts
72
+
73
+ ## Framework Rules
74
+
75
+ ### GraphQL Standards
76
+ - **IDs**: Use `ID` scalar (not `String`). Base64 for keys.
77
+ - **Enums/Unions**: Enums for fixed (Role), unions for polymorphic (Result = Post | User). Limit depth 3-5.
78
+
79
+ ### GraphQL Document Placement
80
+ Colocate operations in domain services (`src/features/<domain>/services/`), `src/graphql/<domain>/`
81
+ - **Routes/pages**: Import from domain services for tree-shaking
82
+ - **Components**: Queries/mutations in domain services, export typed helpers
83
+ - **Stores**: GraphQL docs in domain services
84
+ - **Fragments**: `src/features/<domain>/services/fragments/` with barrel exports
85
+ - **Tests**: Colocate under `src/features/<domain>/`
86
+ - **Typed modules**: `.gql.ts` stay domain-local, enriched by `graphql-env.d.ts`
87
+
88
+ ### Pothos Best Practices
89
+ - **ID Handling**: `exposeId: false` by default (security), `exposeId: true` only when needed
90
+ - **Subscription Ordering**: `subscribe` before `resolve` for TS inference
91
+ - **Extensions**: `extendType` for modularity, integrate gql.tada for E2E types
92
+ - **Errors**: Custom scalars (graphql-scalars), try-catch with GraphQLError
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: UI/UX Guidelines
3
+ description: Modern SPA design patterns, PandaCSS, Radix UI, conversational interfaces
4
+ ---
5
+
6
+ # Modern SPA UI/UX
7
+
8
+ Apply when designing/updating single-page application interfaces. Keep concise, modular, consistent with conversational products (chat-first SaaS).
9
+
10
+ ## Core Principles
11
+ - Minimalist functionalism: Remove clutter, highlight primary content/controls
12
+ - Balanced readability: Letter-spacing 1.2–1.5, line-height ≥1.5, clear hierarchy
13
+ - Global consistency: Reuse tokens for spacing, color, typography, radii
14
+ - Perceived fluidity: Transitions <200ms, never block input
15
+
16
+ ## Visual System
17
+ - **Color**: Dark backgrounds (#111–#1A1A), optional light mode. One accent for CTAs. WCAG AA contrast ≥4.5:1
18
+ - **Shapes**: 8–12px border radius. Cards with subtle shadows (0 1px 3px rgba(0,0,0,0.1))
19
+ - **Typography**: Sans-serif (Inter, SF Pro). Bold headings, progressively larger. Body 14–16px, 1.5 line-height
20
+
21
+ ## Micro-Interactions
22
+ - Duration: 200–400ms ease-in-out
23
+ - Hover: 1.03× scale or deeper shadow
24
+ - Active: 0.95–0.98× for tactile feedback
25
+ - Page transitions: Fade/slide, prefetch routes (<100ms latency)
26
+
27
+ ## Interaction Patterns
28
+ - **Conversational**: Messages top-to-bottom, timestamp/metadata aligned (chat UIs)
29
+ - **Input**: Pin primary composer at viewport bottom, minimal controls
30
+ - **Feedback**: Visual response on every submit/load/state change (spinners, progress, confirmations)
31
+ - **Expandable**: Collapse long sections, keep critical details visible
32
+
33
+ ## UX Guidelines
34
+ - Persistent nav (top/sidebar), limit to 5–7 items
35
+ - Eliminate distractions: No autoplay, reduce focal points
36
+ - Natural scrolling, lazy loading for long lists/media
37
+ - Reusable primitives (buttons, cards, inputs) with consistent props
38
+ - Mobile-first: Validate 320px, 768px, 1024px breakpoints, identical functionality
39
+
40
+ ## Validation
41
+ - Contrast/readability in dark/light (axe-core, Lighthouse ≥90)
42
+ - Hover/tap feedback within 50ms
43
+ - Responsive layouts on phone/tablet/desktop, adjust spacing/typography for overflow
44
+ - Document UI decisions (tokens, components) for reuse
@@ -0,0 +1,165 @@
1
+ ---
2
+ name: Next.js Application
3
+ description: Next.js App Router, Server Components, data fetching, routing, deployment
4
+ ---
5
+
6
+ # Next.js Development
7
+
8
+ ## When Next.js vs React SPA
9
+
10
+ **Next.js**: SEO matters, fast initial load, full-stack in one codebase
11
+ **React SPA**: Dashboard/admin, behind auth, separate backend exists
12
+
13
+ ## App Router vs Pages Router
14
+
15
+ **App Router (app/)** - Recommended:
16
+ - Server Components by default (less JS)
17
+ - Built-in layouts, loading, error states
18
+ - Better data fetching patterns
19
+
20
+ **Pages Router (pages/)**: Legacy maintenance only
21
+
22
+ ## Server vs Client Components
23
+
24
+ ### Decision Tree
25
+ ```
26
+ Needs interactivity? (onClick, useState, hooks)
27
+ ├─ YES → 'use client'
28
+ └─ NO → Server Component (default)
29
+ ```
30
+
31
+ **Server Components (default):**
32
+ - Render on server, direct DB access, zero client JS
33
+ - Can't use hooks/handlers
34
+
35
+ **Client Components ('use client'):**
36
+ - Hooks, handlers, interactivity, ships JS to browser
37
+
38
+ ### Composition Pattern
39
+ Server components wrap client components. Fetch data in server, pass as props to client.
40
+
41
+ ## Data Fetching
42
+
43
+ ### Cache Options
44
+ ```typescript
45
+ // Cached forever (default)
46
+ await fetch('https://api.example.com/data')
47
+
48
+ // Cache with revalidation
49
+ await fetch('...', { next: { revalidate: 3600 } })
50
+
51
+ // Never cache
52
+ await fetch('...', { cache: 'no-store' })
53
+ ```
54
+
55
+ ### Parallel Fetching
56
+ ```typescript
57
+ // BAD - Sequential
58
+ const posts = await getPosts()
59
+ const users = await getUsers()
60
+
61
+ // GOOD - Parallel
62
+ const [posts, users] = await Promise.all([getPosts(), getUsers()])
63
+ ```
64
+
65
+ ## Caching & Revalidation
66
+
67
+ **Time-based (ISR):**
68
+ ```typescript
69
+ export const revalidate = 3600 // Every hour
70
+ ```
71
+
72
+ **On-demand:**
73
+ ```typescript
74
+ import { revalidatePath, revalidateTag } from 'next/cache'
75
+ revalidatePath('/posts')
76
+ revalidateTag('posts')
77
+ ```
78
+
79
+ **Dynamic (no cache):**
80
+ ```typescript
81
+ export const dynamic = 'force-dynamic'
82
+ ```
83
+
84
+ ## Routing
85
+
86
+ ### File Structure
87
+ ```
88
+ app/page.tsx → /
89
+ app/about/page.tsx → /about
90
+ app/blog/[slug]/page.tsx → /blog/:slug
91
+ app/(marketing)/features/page.tsx → /features (route group)
92
+ ```
93
+
94
+ ### Special Files
95
+ - `layout.tsx`: Shared UI for segment + children
96
+ - `loading.tsx`: Loading UI (Suspense boundary)
97
+ - `error.tsx`: Error UI (must be 'use client')
98
+
99
+ ## Authentication
100
+
101
+ ### Middleware (Route Protection)
102
+ ```typescript
103
+ // middleware.ts
104
+ export function middleware(request: Request) {
105
+ const token = request.cookies.get('token')
106
+ if (!token) return NextResponse.redirect(new URL('/login', request.url))
107
+ }
108
+
109
+ export const config = { matcher: '/dashboard/:path*' }
110
+ ```
111
+
112
+ ### Server Actions (Form Handling)
113
+ ```typescript
114
+ // app/actions.ts
115
+ 'use server'
116
+
117
+ export async function createPost(formData: FormData) {
118
+ const title = formData.get('title')
119
+ if (!title) return { error: 'Missing title' }
120
+
121
+ const post = await db.posts.create({ data: { title } })
122
+ revalidatePath('/posts')
123
+ return { success: true, post }
124
+ }
125
+
126
+ // In component
127
+ <form action={createPost}>
128
+ <input name="title" />
129
+ <button type="submit">Create</button>
130
+ </form>
131
+ ```
132
+
133
+ ## Performance
134
+
135
+ **Image**: Use `<Image>` with `priority` for above-fold, `placeholder="blur"` for UX
136
+ **Font**: `next/font/google` for automatic optimization
137
+ **Code Splitting**: `dynamic(() => import('./Heavy'), { ssr: false })` for client-only heavy components
138
+
139
+ ## Common Pitfalls
140
+
141
+ ❌ **'use client' everywhere** → Only for interactivity
142
+ ❌ **Over-fetching** → Fetch once in layout/page, pass as props
143
+ ❌ **Not streaming** → Use Suspense boundaries
144
+ ❌ **Forgetting revalidation** → Always revalidate after mutations
145
+
146
+ ## Environment Variables
147
+
148
+ ```bash
149
+ DATABASE_URL="..." # Server only
150
+ NEXT_PUBLIC_API_URL="..." # Exposed to browser (must start with NEXT_PUBLIC_)
151
+ ```
152
+
153
+ ## Decision Guide
154
+
155
+ **Server Component:**
156
+ - Fetching data, backend access, large dependencies, non-interactive
157
+
158
+ **Client Component:**
159
+ - Interactive (clicks, state, hooks), browser APIs, event handlers
160
+
161
+ **API Route:**
162
+ - Hide API keys, webhooks, complex server logic, third-party integrations
163
+
164
+ **Server Action:**
165
+ - Form submissions, mutations (CRUD), simple server ops
@@ -0,0 +1,220 @@
1
+ ---
2
+ name: Node.js API
3
+ description: Express/Fastify, REST/GraphQL, authentication, middleware, error handling
4
+ ---
5
+
6
+ # Backend API Development
7
+
8
+ ## REST Structure
9
+
10
+ ```
11
+ GET /users List
12
+ POST /users Create
13
+ GET /users/:id Get
14
+ PATCH /users/:id Update
15
+ DELETE /users/:id Delete
16
+ GET /users/:id/posts Nested
17
+ ```
18
+
19
+ **Status**: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 500 Internal Server Error
20
+
21
+ **Response format (project standard):**
22
+ ```json
23
+ { "data": {...}, "meta": {...} }
24
+ { "items": [...], "total": 100, "page": 1, "limit": 20 }
25
+ { "error": { "code": "VALIDATION_ERROR", "message": "...", "field": "..." } }
26
+ ```
27
+
28
+ ## N+1 Problem
29
+
30
+ ```javascript
31
+ // BAD - N+1 queries
32
+ for (user of users) { user.posts = await getPosts(user.id) }
33
+
34
+ // GOOD - Join
35
+ await db.users.findMany({ include: { posts: true } })
36
+
37
+ // GOOD - Batch fetch
38
+ const userIds = users.map(u => u.id)
39
+ const posts = await db.posts.findMany({ where: { userId: { in: userIds } } })
40
+
41
+ // GraphQL: DataLoader
42
+ const loader = new DataLoader(async (ids) => {
43
+ const items = await db.find({ where: { id: { in: ids } } })
44
+ return ids.map(id => items.filter(i => i.parentId === id))
45
+ })
46
+ ```
47
+
48
+ ## Database
49
+
50
+ **Connection pooling:**
51
+ ```javascript
52
+ const pool = new Pool({ max: 20, idleTimeoutMillis: 30000 })
53
+ ```
54
+
55
+ **Caching pattern:**
56
+ ```javascript
57
+ async function getUser(id) {
58
+ const cached = await redis.get(`user:${id}`)
59
+ if (cached) return JSON.parse(cached)
60
+
61
+ const user = await db.users.findUnique({ where: { id } })
62
+ await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 3600)
63
+ return user
64
+ }
65
+
66
+ // Invalidate on update
67
+ async function updateUser(id, data) {
68
+ const user = await db.users.update({ where: { id }, data })
69
+ await redis.del(`user:${id}`)
70
+ return user
71
+ }
72
+ ```
73
+
74
+ ## Authentication
75
+
76
+ **Session vs Token:**
77
+ - Session: Traditional web apps, need fine-grained control
78
+ - Token (JWT): SPAs, mobile apps, microservices
79
+
80
+ **JWT middleware (project standard):**
81
+ ```javascript
82
+ function requireAuth(req, res, next) {
83
+ const token = req.headers.authorization?.split(' ')[1]
84
+ if (!token) return res.status(401).json({ error: { code: 'NO_TOKEN' } })
85
+
86
+ try {
87
+ req.userId = jwt.verify(token, process.env.JWT_SECRET).userId
88
+ next()
89
+ } catch {
90
+ res.status(401).json({ error: { code: 'INVALID_TOKEN' } })
91
+ }
92
+ }
93
+ ```
94
+
95
+ **Authorization patterns:**
96
+ ```javascript
97
+ // Role check
98
+ function requireRole(...roles) {
99
+ return async (req, res, next) => {
100
+ const user = await db.users.findUnique({ where: { id: req.userId } })
101
+ if (!roles.includes(user.role)) {
102
+ return res.status(403).json({ error: { code: 'FORBIDDEN' } })
103
+ }
104
+ next()
105
+ }
106
+ }
107
+
108
+ // Ownership check
109
+ function requireOwnership(getter) {
110
+ return async (req, res, next) => {
111
+ const resource = await getter(req)
112
+ if (resource.userId !== req.userId) {
113
+ return res.status(403).json({ error: { code: 'NOT_OWNER' } })
114
+ }
115
+ next()
116
+ }
117
+ }
118
+ ```
119
+
120
+ ## Error Handling
121
+
122
+ **Project standard:**
123
+ ```javascript
124
+ class ApiError extends Error {
125
+ constructor(statusCode, code, message) {
126
+ super(message)
127
+ this.statusCode = statusCode
128
+ this.code = code
129
+ this.isOperational = true
130
+ }
131
+ }
132
+
133
+ // Error middleware (last!)
134
+ app.use((err, req, res, next) => {
135
+ if (err.isOperational) {
136
+ return res.status(err.statusCode).json({
137
+ error: { code: err.code, message: err.message }
138
+ })
139
+ }
140
+
141
+ console.error('PROGRAMMER ERROR:', err)
142
+ res.status(500).json({ error: { code: 'INTERNAL_ERROR' } })
143
+ })
144
+
145
+ // Usage
146
+ if (!user) throw new ApiError(404, 'NOT_FOUND', 'User not found')
147
+ ```
148
+
149
+ ## Performance
150
+
151
+ **Targets**: DB < 100ms, API < 200ms
152
+
153
+ **Optimize:**
154
+ - N+1 → Joins / DataLoader
155
+ - Connection pooling
156
+ - Redis caching
157
+ - Job queues (background tasks)
158
+ - Pagination (always LIMIT)
159
+
160
+ ## GraphQL Basics
161
+
162
+ **When**: Complex relationships, flexible queries
163
+ **vs REST**: Simple CRUD → REST
164
+
165
+ **DataLoader (required for N+1):**
166
+ ```javascript
167
+ const postLoader = new DataLoader(async (userIds) => {
168
+ const posts = await db.posts.findMany({ where: { userId: { in: userIds } } })
169
+ return userIds.map(id => posts.filter(p => p.userId === id))
170
+ })
171
+
172
+ // In resolver
173
+ User: {
174
+ posts: (user) => postLoader.load(user.id)
175
+ }
176
+ ```
177
+
178
+ ## Common Patterns
179
+
180
+ **Repository:**
181
+ ```javascript
182
+ class UserRepo {
183
+ findById(id) { return db.users.findUnique({ where: { id } }) }
184
+ save(data) { return db.users.create({ data }) }
185
+ }
186
+ ```
187
+
188
+ **Service:**
189
+ ```javascript
190
+ class UserService {
191
+ async createUser(data) {
192
+ if (!data.email) throw new Error('Email required')
193
+ return await this.repo.save(data)
194
+ }
195
+ }
196
+ ```
197
+
198
+ **Middleware chain:**
199
+ ```javascript
200
+ app.use(cors())
201
+ app.use(express.json())
202
+ app.use(rateLimit())
203
+ app.use(auth())
204
+ app.use(errorHandler()) // Last!
205
+ ```
206
+
207
+ ## Best Practices
208
+
209
+ ✅ Prepared statements (prevent injection)
210
+ ✅ Connection pooling
211
+ ✅ Index foreign keys
212
+ ✅ Rate limit auth endpoints
213
+ ✅ Hash passwords (bcrypt/argon2)
214
+ ✅ HTTPS only
215
+ ✅ Validate server-side
216
+
217
+ ❌ SQL injection (string concat)
218
+ ❌ Plain text passwords
219
+ ❌ N+1 queries
220
+ ❌ No error handling