@tekyzinc/gsd-t 2.45.11 → 2.50.10

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 (45) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +26 -5
  3. package/bin/debug-ledger.js +193 -0
  4. package/bin/gsd-t.js +259 -1
  5. package/commands/gsd-t-complete-milestone.md +2 -1
  6. package/commands/gsd-t-debug.md +48 -2
  7. package/commands/gsd-t-doc-ripple.md +148 -0
  8. package/commands/gsd-t-execute.md +102 -5
  9. package/commands/gsd-t-help.md +25 -2
  10. package/commands/gsd-t-integrate.md +41 -1
  11. package/commands/gsd-t-qa.md +26 -5
  12. package/commands/gsd-t-quick.md +39 -1
  13. package/commands/gsd-t-test-sync.md +26 -1
  14. package/commands/gsd-t-verify.md +8 -2
  15. package/commands/gsd-t-wave.md +57 -0
  16. package/docs/GSD-T-README.md +84 -1
  17. package/docs/architecture.md +9 -1
  18. package/docs/framework-comparison-scorecard.md +160 -0
  19. package/docs/requirements.md +33 -0
  20. package/examples/rules/desktop.ini +2 -0
  21. package/package.json +2 -2
  22. package/templates/CLAUDE-global.md +82 -4
  23. package/templates/stacks/_security.md +243 -0
  24. package/templates/stacks/desktop.ini +2 -0
  25. package/templates/stacks/docker.md +202 -0
  26. package/templates/stacks/firebase.md +166 -0
  27. package/templates/stacks/flutter.md +205 -0
  28. package/templates/stacks/github-actions.md +201 -0
  29. package/templates/stacks/graphql.md +216 -0
  30. package/templates/stacks/neo4j.md +218 -0
  31. package/templates/stacks/nextjs.md +184 -0
  32. package/templates/stacks/node-api.md +196 -0
  33. package/templates/stacks/playwright.md +528 -0
  34. package/templates/stacks/postgresql.md +225 -0
  35. package/templates/stacks/python.md +243 -0
  36. package/templates/stacks/react-native.md +216 -0
  37. package/templates/stacks/react.md +293 -0
  38. package/templates/stacks/redux.md +193 -0
  39. package/templates/stacks/rest-api.md +202 -0
  40. package/templates/stacks/supabase.md +188 -0
  41. package/templates/stacks/tailwind.md +169 -0
  42. package/templates/stacks/typescript.md +176 -0
  43. package/templates/stacks/vite.md +176 -0
  44. package/templates/stacks/vue.md +189 -0
  45. package/templates/stacks/zustand.md +203 -0
@@ -0,0 +1,216 @@
1
+ # GraphQL Standards
2
+
3
+ These rules are MANDATORY. Violations fail the task. No exceptions.
4
+
5
+ ---
6
+
7
+ ## 1. Schema Design
8
+
9
+ ```
10
+ MANDATORY:
11
+ ├── Schema-first design — define the schema, then implement resolvers
12
+ ├── Use PascalCase for types: User, OrderItem
13
+ ├── Use camelCase for fields: firstName, createdAt
14
+ ├── Use UPPER_SNAKE_CASE for enum values: ACTIVE, PENDING_REVIEW
15
+ ├── Input types for mutations: CreateUserInput, UpdateOrderInput
16
+ ├── Every mutation returns the affected type (not just Boolean)
17
+ └── Add descriptions to all types and fields — they power documentation
18
+ ```
19
+
20
+ **GOOD**
21
+ ```graphql
22
+ """A registered user in the system."""
23
+ type User {
24
+ id: ID!
25
+ """User's display name."""
26
+ displayName: String!
27
+ email: String!
28
+ role: UserRole!
29
+ orders(first: Int = 20, after: String): OrderConnection!
30
+ createdAt: DateTime!
31
+ }
32
+
33
+ enum UserRole {
34
+ ADMIN
35
+ MEMBER
36
+ VIEWER
37
+ }
38
+
39
+ input CreateUserInput {
40
+ displayName: String!
41
+ email: String!
42
+ role: UserRole = MEMBER
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ## 2. Query Design
49
+
50
+ ```
51
+ MANDATORY:
52
+ ├── Singular for single resource: user(id: ID!): User
53
+ ├── Plural for collections: users(first: Int, after: String, filter: UserFilter): UserConnection!
54
+ ├── Use connection pattern (Relay) for paginated lists
55
+ ├── Accept filter input types — not individual filter args
56
+ ├── NEVER return unbounded lists — always require pagination args
57
+ └── Nullable return for single lookups (user may not exist)
58
+ ```
59
+
60
+ **GOOD**
61
+ ```graphql
62
+ type Query {
63
+ user(id: ID!): User
64
+ users(first: Int = 20, after: String, filter: UserFilter): UserConnection!
65
+ }
66
+
67
+ input UserFilter {
68
+ role: UserRole
69
+ isActive: Boolean
70
+ search: String
71
+ }
72
+
73
+ type UserConnection {
74
+ edges: [UserEdge!]!
75
+ pageInfo: PageInfo!
76
+ totalCount: Int!
77
+ }
78
+
79
+ type UserEdge {
80
+ node: User!
81
+ cursor: String!
82
+ }
83
+
84
+ type PageInfo {
85
+ hasNextPage: Boolean!
86
+ endCursor: String
87
+ }
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 3. Mutation Design
93
+
94
+ ```
95
+ MANDATORY:
96
+ ├── Verb-first naming: createUser, updateOrder, deleteComment
97
+ ├── Accept a single input argument: createUser(input: CreateUserInput!): User!
98
+ ├── Return the mutated object — not Boolean or generic status
99
+ ├── Use union types for error handling (preferred) or throw errors
100
+ ├── Validate inputs in resolvers — schema types are not sufficient
101
+ └── Mutations must be idempotent where possible
102
+ ```
103
+
104
+ **GOOD**
105
+ ```graphql
106
+ type Mutation {
107
+ createUser(input: CreateUserInput!): CreateUserPayload!
108
+ updateUser(id: ID!, input: UpdateUserInput!): User!
109
+ deleteUser(id: ID!): DeleteUserPayload!
110
+ }
111
+
112
+ type CreateUserPayload {
113
+ user: User
114
+ errors: [ValidationError!]
115
+ }
116
+
117
+ type ValidationError {
118
+ field: String!
119
+ message: String!
120
+ }
121
+ ```
122
+
123
+ ---
124
+
125
+ ## 4. Resolver Patterns
126
+
127
+ ```
128
+ MANDATORY:
129
+ ├── Thin resolvers — delegate to service/data layer, don't put business logic in resolvers
130
+ ├── Use DataLoader for N+1 prevention — batch and cache database lookups
131
+ ├── Auth checks in resolvers or middleware — not in the service layer
132
+ ├── Return null for not-found, throw for unauthorized/forbidden
133
+ └── Log errors with context (query name, variables, user ID)
134
+ ```
135
+
136
+ **GOOD**
137
+ ```typescript
138
+ const resolvers = {
139
+ Query: {
140
+ user: async (_, { id }, { dataSources, user }) => {
141
+ if (!user) throw new AuthenticationError('Must be logged in');
142
+ return dataSources.userService.getById(id);
143
+ },
144
+ },
145
+ User: {
146
+ orders: (parent, args, { dataSources }) =>
147
+ dataSources.orderLoader.load({ userId: parent.id, ...args }),
148
+ },
149
+ };
150
+ ```
151
+
152
+ ---
153
+
154
+ ## 5. N+1 Prevention — DataLoader
155
+
156
+ ```
157
+ MANDATORY:
158
+ ├── Use DataLoader for any field that resolves per-item in a list
159
+ ├── Create loaders per request (new instance in context factory)
160
+ ├── Batch function receives array of keys, returns array of results in same order
161
+ └── Cache is request-scoped — not shared between requests
162
+ ```
163
+
164
+ **GOOD**
165
+ ```typescript
166
+ const userLoader = new DataLoader<string, User>(async (ids) => {
167
+ const users = await db.query('SELECT * FROM users WHERE id = ANY($1)', [ids]);
168
+ const userMap = new Map(users.map(u => [u.id, u]));
169
+ return ids.map(id => userMap.get(id) ?? new Error(`User ${id} not found`));
170
+ });
171
+ ```
172
+
173
+ ---
174
+
175
+ ## 6. Client-Side Patterns
176
+
177
+ ```
178
+ MANDATORY:
179
+ ├── Co-locate queries with components — query file next to the component
180
+ ├── Use fragments for shared field selections
181
+ ├── Name all operations: query GetUser, mutation CreateOrder
182
+ ├── Use generated types (graphql-codegen) — NEVER manually type query results
183
+ ├── Handle loading, error, and empty states for every query
184
+ └── Cache update after mutations: refetch or update cache directly
185
+ ```
186
+
187
+ ---
188
+
189
+ ## 7. Anti-Patterns
190
+
191
+ ```
192
+ NEVER:
193
+ ├── Unbounded list queries without pagination
194
+ ├── Business logic in resolvers — delegate to services
195
+ ├── N+1 queries — use DataLoader
196
+ ├── Anonymous operations (unnamed queries/mutations)
197
+ ├── Deeply nested queries without depth limiting
198
+ ├── Returning Boolean from mutations — return the affected type
199
+ ├── Over-fetching: requesting all fields when you need two
200
+ └── Manually typing query results — use codegen
201
+ ```
202
+
203
+ ---
204
+
205
+ ## GraphQL Verification Checklist
206
+
207
+ - [ ] Schema-first with descriptions on all types/fields
208
+ - [ ] Connection pattern for paginated lists
209
+ - [ ] All mutations return affected type (not Boolean)
210
+ - [ ] DataLoader used for N+1 prevention
211
+ - [ ] Input validation in resolvers
212
+ - [ ] Auth checks in resolvers or middleware
213
+ - [ ] Operations named (query GetUser, not anonymous)
214
+ - [ ] Generated types via codegen
215
+ - [ ] Loading, error, empty states handled client-side
216
+ - [ ] Query depth limiting configured
@@ -0,0 +1,218 @@
1
+ # Neo4j Standards
2
+
3
+ These rules are MANDATORY. Violations fail the task. No exceptions.
4
+
5
+ ---
6
+
7
+ ## 1. Data Modeling
8
+
9
+ ```
10
+ MANDATORY:
11
+ ├── Nodes represent entities (nouns): (:User), (:Product), (:Order)
12
+ ├── Relationships represent verbs: -[:PURCHASED]->. -[:FOLLOWS]->
13
+ ├── Node labels: PascalCase singular (User, BusinessType — not users)
14
+ ├── Relationship types: UPPER_SNAKE_CASE (PURCHASED, WORKS_AT, BELONGS_TO)
15
+ ├── Properties: camelCase (firstName, createdAt)
16
+ ├── Store properties on the node or relationship that "owns" them
17
+ ├── Relationships ALWAYS have a direction — even if queried bidirectionally
18
+ └── NEVER use relationships as nodes (reify only when the relationship needs its own relationships)
19
+ ```
20
+
21
+ **GOOD**
22
+ ```cypher
23
+ (:User {id: "u-123", name: "Jane", email: "jane@example.com"})
24
+ -[:PURCHASED {purchasedAt: datetime(), amount: 29.99}]->
25
+ (:Product {id: "p-456", name: "Widget", category: "Tools"})
26
+ ```
27
+
28
+ ---
29
+
30
+ ## 2. Cypher Query Patterns
31
+
32
+ ```
33
+ MANDATORY:
34
+ ├── Use parameterized queries — NEVER string concatenation
35
+ ├── Use MERGE for upserts — CREATE for guaranteed-new, MATCH for existing
36
+ ├── Always LIMIT results on user-facing queries
37
+ ├── Use OPTIONAL MATCH when the relationship may not exist
38
+ ├── Use WITH to chain query stages — improves readability and performance
39
+ ├── Prefer pattern comprehension over COLLECT + UNWIND for subqueries
40
+ └── Always specify relationship direction in MATCH patterns
41
+ ```
42
+
43
+ **BAD**
44
+ ```cypher
45
+ MATCH (u:User) WHERE u.name = '${name}' RETURN u // injection risk!
46
+ MATCH (u:User)--(p:Product) RETURN u, p // no direction, no limit
47
+ ```
48
+
49
+ **GOOD**
50
+ ```cypher
51
+ MATCH (u:User {id: $userId})-[:PURCHASED]->(p:Product)
52
+ RETURN u.name, p.name, p.category
53
+ ORDER BY p.name
54
+ LIMIT 50
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 3. Indexing and Constraints
60
+
61
+ ```
62
+ MANDATORY:
63
+ ├── Unique constraint on all ID properties: CREATE CONSTRAINT FOR (u:User) REQUIRE u.id IS UNIQUE
64
+ ├── Index properties used in WHERE and MATCH lookups
65
+ ├── Composite indexes for multi-property lookups
66
+ ├── Full-text indexes for search fields (name, description)
67
+ ├── Use EXPLAIN and PROFILE to verify query plans use indexes
68
+ └── NEVER rely on full node scans for lookups
69
+ ```
70
+
71
+ **GOOD**
72
+ ```cypher
73
+ // Unique constraint (also creates an index)
74
+ CREATE CONSTRAINT user_id_unique FOR (u:User) REQUIRE u.id IS UNIQUE;
75
+
76
+ // Property index for common lookups
77
+ CREATE INDEX user_email_idx FOR (u:User) ON (u.email);
78
+
79
+ // Composite index
80
+ CREATE INDEX order_status_date FOR (o:Order) ON (o.status, o.createdAt);
81
+
82
+ // Full-text index for search
83
+ CREATE FULLTEXT INDEX user_search FOR (u:User) ON EACH [u.name, u.email];
84
+ ```
85
+
86
+ ---
87
+
88
+ ## 4. Transaction Management
89
+
90
+ ```
91
+ MANDATORY:
92
+ ├── Use explicit transactions for multi-statement operations
93
+ ├── Keep transactions short — don't hold locks while doing external I/O
94
+ ├── Read transactions for queries: session.executeRead()
95
+ ├── Write transactions for mutations: session.executeWrite()
96
+ ├── Handle transient errors with retry (the driver retries automatically in managed transactions)
97
+ └── Always close sessions after use
98
+ ```
99
+
100
+ **GOOD**
101
+ ```typescript
102
+ const session = driver.session();
103
+ try {
104
+ const result = await session.executeRead(async (tx) => {
105
+ const res = await tx.run(
106
+ 'MATCH (u:User {id: $userId})-[:PURCHASED]->(p:Product) RETURN p',
107
+ { userId }
108
+ );
109
+ return res.records.map(r => r.get('p').properties);
110
+ });
111
+ return result;
112
+ } finally {
113
+ await session.close();
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 5. Driver Configuration
120
+
121
+ ```
122
+ MANDATORY:
123
+ ├── Create driver once at startup — reuse across requests
124
+ ├── Set maxConnectionPoolSize based on workload (default 100 is usually fine)
125
+ ├── Set connectionAcquisitionTimeout (default 60s — lower for web apps)
126
+ ├── Use bolt:// for direct, neo4j:// for routing (cluster)
127
+ ├── Verify connectivity at startup: driver.verifyConnectivity()
128
+ └── Close driver on application shutdown: driver.close()
129
+ ```
130
+
131
+ **GOOD**
132
+ ```typescript
133
+ import neo4j from 'neo4j-driver';
134
+
135
+ const driver = neo4j.driver(
136
+ process.env.NEO4J_URI!,
137
+ neo4j.auth.basic(process.env.NEO4J_USER!, process.env.NEO4J_PASSWORD!),
138
+ {
139
+ maxConnectionPoolSize: 50,
140
+ connectionAcquisitionTimeout: 10000,
141
+ }
142
+ );
143
+
144
+ await driver.verifyConnectivity();
145
+
146
+ // On shutdown:
147
+ process.on('SIGTERM', () => driver.close());
148
+ ```
149
+
150
+ ---
151
+
152
+ ## 6. Performance
153
+
154
+ ```
155
+ MANDATORY:
156
+ ├── PROFILE queries during development to check plan and db hits
157
+ ├── Use indexes — full node scans are O(n) and kill performance
158
+ ├── Avoid variable-length paths without upper bound: -[:FOLLOWS*1..5]-> not -[:FOLLOWS*]->
159
+ ├── Use LIMIT early in the query — not just at the end
160
+ ├── Batch large writes (1000-5000 nodes per transaction)
161
+ └── Use APOC for batch operations when available
162
+ ```
163
+
164
+ **BAD** — unbounded variable-length path:
165
+ ```cypher
166
+ MATCH (u:User)-[:FOLLOWS*]->(f:User) RETURN f // scans entire graph!
167
+ ```
168
+
169
+ **GOOD**
170
+ ```cypher
171
+ MATCH (u:User {id: $userId})-[:FOLLOWS*1..3]->(f:User)
172
+ RETURN DISTINCT f.id, f.name
173
+ LIMIT 100
174
+ ```
175
+
176
+ ---
177
+
178
+ ## 7. APOC Patterns
179
+
180
+ ```
181
+ WHEN APOC IS AVAILABLE:
182
+ ├── apoc.periodic.iterate for large batch operations
183
+ ├── apoc.merge.node for conditional upserts with labels
184
+ ├── apoc.path.expandConfig for complex traversals with filters
185
+ ├── apoc.export.json for data dumps
186
+ └── Check APOC version compatibility with your Neo4j version
187
+ ```
188
+
189
+ ---
190
+
191
+ ## 8. Anti-Patterns
192
+
193
+ ```
194
+ NEVER:
195
+ ├── String concatenation in Cypher — use parameters ($param)
196
+ ├── Unbounded queries without LIMIT
197
+ ├── Variable-length paths without upper bound (-[:REL*]->)
198
+ ├── Missing indexes on lookup properties
199
+ ├── Creating a new driver per request — reuse the driver
200
+ ├── Storing large blobs in node properties — use external storage
201
+ ├── Dense connected nodes (supernode > 100K relationships) without optimization
202
+ └── Using nodes where relationships should be used (and vice versa)
203
+ ```
204
+
205
+ ---
206
+
207
+ ## Neo4j Verification Checklist
208
+
209
+ - [ ] Parameterized queries only — no string concatenation
210
+ - [ ] Unique constraints on all ID properties
211
+ - [ ] Indexes on all WHERE/MATCH lookup properties
212
+ - [ ] All queries have LIMIT
213
+ - [ ] Variable-length paths have upper bounds
214
+ - [ ] Explicit read/write transactions
215
+ - [ ] Driver created once and reused
216
+ - [ ] Sessions closed after use
217
+ - [ ] PROFILE run on complex queries
218
+ - [ ] Node labels PascalCase, relationship types UPPER_SNAKE_CASE
@@ -0,0 +1,184 @@
1
+ # Next.js Standards
2
+
3
+ These rules are MANDATORY. Violations fail the task. No exceptions.
4
+
5
+ ---
6
+
7
+ ## 1. App Router (Next.js 13+)
8
+
9
+ ```
10
+ MANDATORY:
11
+ ├── Use the App Router (app/) — not Pages Router (pages/) for new projects
12
+ ├── Default to Server Components — add 'use client' ONLY when needed
13
+ ├── 'use client' needed for: useState, useEffect, event handlers, browser APIs
14
+ ├── Keep 'use client' boundary as low as possible — don't mark entire pages
15
+ └── NEVER import server-only code in client components
16
+ ```
17
+
18
+ **BAD** — marking the whole page as client:
19
+ ```tsx
20
+ 'use client'; // ← Unnecessary — only the button needs interactivity
21
+ export default function Page() {
22
+ return <div><h1>Static content</h1><LikeButton /></div>;
23
+ }
24
+ ```
25
+
26
+ **GOOD** — push client boundary down:
27
+ ```tsx
28
+ // app/page.tsx — Server Component (default)
29
+ export default function Page() {
30
+ return <div><h1>Static content</h1><LikeButton /></div>;
31
+ }
32
+
33
+ // components/LikeButton.tsx
34
+ 'use client';
35
+ export function LikeButton() {
36
+ const [liked, setLiked] = useState(false);
37
+ return <button onClick={() => setLiked(!liked)}>Like</button>;
38
+ }
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 2. Data Fetching
44
+
45
+ ```
46
+ MANDATORY:
47
+ ├── Server Components: fetch directly (async component) — no useEffect
48
+ ├── Client Components: React Query for server data — same as react.md Section 1
49
+ ├── Use Route Handlers (app/api/) for API endpoints — not pages/api/
50
+ ├── Set revalidate or cache options on server-side fetches
51
+ └── NEVER fetch from your own API routes in Server Components — call the function directly
52
+ ```
53
+
54
+ **BAD** — Server Component calling its own API:
55
+ ```tsx
56
+ // app/page.tsx
57
+ const res = await fetch('http://localhost:3000/api/users'); // calling yourself!
58
+ ```
59
+
60
+ **GOOD** — call the data function directly:
61
+ ```tsx
62
+ // app/page.tsx
63
+ import { getUsers } from '@/lib/data/users';
64
+ export default async function Page() {
65
+ const users = await getUsers();
66
+ return <UserList users={users} />;
67
+ }
68
+ ```
69
+
70
+ ---
71
+
72
+ ## 3. Route Structure
73
+
74
+ ```
75
+ MANDATORY:
76
+ ├── app/{route}/page.tsx — page component
77
+ ├── app/{route}/layout.tsx — shared layout (wraps child pages)
78
+ ├── app/{route}/loading.tsx — Suspense fallback for the route
79
+ ├── app/{route}/error.tsx — error boundary for the route ('use client')
80
+ ├── app/{route}/not-found.tsx — 404 for the route
81
+ ├── Group routes with (parentheses) for layout grouping: app/(auth)/login
82
+ └── Dynamic routes: app/users/[id]/page.tsx
83
+ ```
84
+
85
+ ---
86
+
87
+ ## 4. Server Actions
88
+
89
+ ```
90
+ MANDATORY (Next.js 14+):
91
+ ├── Use Server Actions for form mutations — not client-side API calls
92
+ ├── Mark with 'use server' at the top of the function or file
93
+ ├── Validate ALL inputs with Zod — Server Actions are public endpoints
94
+ ├── Return typed results — not raw responses
95
+ └── Revalidate cache after mutations: revalidatePath() or revalidateTag()
96
+ ```
97
+
98
+ **GOOD**
99
+ ```tsx
100
+ 'use server';
101
+ import { z } from 'zod';
102
+ import { revalidatePath } from 'next/cache';
103
+
104
+ const schema = z.object({ name: z.string().min(2), email: z.string().email() });
105
+
106
+ export async function createUser(formData: FormData) {
107
+ const parsed = schema.safeParse(Object.fromEntries(formData));
108
+ if (!parsed.success) return { error: parsed.error.flatten() };
109
+
110
+ await db.user.create({ data: parsed.data });
111
+ revalidatePath('/users');
112
+ return { success: true };
113
+ }
114
+ ```
115
+
116
+ ---
117
+
118
+ ## 5. Environment Variables
119
+
120
+ ```
121
+ MANDATORY:
122
+ ├── NEXT_PUBLIC_ prefix for client-exposed vars — all others are server-only
123
+ ├── NEVER put secrets in NEXT_PUBLIC_ vars — they're in the client bundle
124
+ ├── Access server-only vars in Server Components, Route Handlers, Server Actions
125
+ ├── Validate env vars at startup with t3-env or manual checks
126
+ └── .env.local in .gitignore — commit .env.example with placeholder values
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 6. Metadata and SEO
132
+
133
+ ```
134
+ MANDATORY:
135
+ ├── Export metadata object or generateMetadata function from page.tsx
136
+ ├── Every page needs title and description at minimum
137
+ ├── Use template for consistent titles: { template: '%s | AppName' }
138
+ ├── Set Open Graph and Twitter card metadata for shared pages
139
+ └── Add robots.txt and sitemap.xml via app/robots.ts and app/sitemap.ts
140
+ ```
141
+
142
+ ---
143
+
144
+ ## 7. Middleware
145
+
146
+ ```
147
+ WHEN NEEDED:
148
+ ├── Use middleware.ts at project root for auth redirects, geolocation, headers
149
+ ├── Keep middleware fast — it runs on EVERY request matching the matcher
150
+ ├── Use matcher config to limit which routes trigger middleware
151
+ ├── NEVER do heavy computation or database calls in middleware
152
+ └── Use NextResponse.next() to continue, NextResponse.redirect() to redirect
153
+ ```
154
+
155
+ ---
156
+
157
+ ## 8. Anti-Patterns
158
+
159
+ ```
160
+ NEVER:
161
+ ├── 'use client' on entire pages when only a small part needs interactivity
162
+ ├── useEffect + fetch in components when a Server Component would work
163
+ ├── Fetching your own API routes from Server Components
164
+ ├── Server Actions without input validation — they're public endpoints
165
+ ├── Secrets in NEXT_PUBLIC_ env vars
166
+ ├── getServerSideProps / getStaticProps (Pages Router) in App Router projects
167
+ ├── Importing server-only modules (fs, db) in 'use client' components
168
+ └── Massive layouts that re-render on every navigation
169
+ ```
170
+
171
+ ---
172
+
173
+ ## Next.js Verification Checklist
174
+
175
+ - [ ] App Router used (app/ directory)
176
+ - [ ] Server Components by default — 'use client' only where needed
177
+ - [ ] Client boundary pushed as low as possible
178
+ - [ ] Server fetches call functions directly — not own API routes
179
+ - [ ] Server Actions validate input with Zod
180
+ - [ ] Cache revalidated after mutations
181
+ - [ ] Every page has metadata (title + description)
182
+ - [ ] loading.tsx and error.tsx for each major route
183
+ - [ ] No secrets in NEXT_PUBLIC_ vars
184
+ - [ ] Middleware is fast and uses matcher config