@levironexe/architect 0.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.
- package/CHANGELOG.md +32 -0
- package/CONTRIBUTING.md +55 -0
- package/README.md +341 -0
- package/dist/analyzers/ast-parser.d.ts +3 -0
- package/dist/analyzers/ast-parser.js +305 -0
- package/dist/analyzers/ast-parser.js.map +1 -0
- package/dist/analyzers/dependency-graph.d.ts +2 -0
- package/dist/analyzers/dependency-graph.js +67 -0
- package/dist/analyzers/dependency-graph.js.map +1 -0
- package/dist/analyzers/duplication.d.ts +2 -0
- package/dist/analyzers/duplication.js +56 -0
- package/dist/analyzers/duplication.js.map +1 -0
- package/dist/analyzers/file-walker.d.ts +3 -0
- package/dist/analyzers/file-walker.js +80 -0
- package/dist/analyzers/file-walker.js.map +1 -0
- package/dist/cli/context-runner.d.ts +1 -0
- package/dist/cli/context-runner.js +16 -0
- package/dist/cli/context-runner.js.map +1 -0
- package/dist/cli/index.d.ts +24 -0
- package/dist/cli/index.js +217 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-runner.d.ts +25 -0
- package/dist/cli/init-runner.js +152 -0
- package/dist/cli/init-runner.js.map +1 -0
- package/dist/cli/scan-runner.d.ts +8 -0
- package/dist/cli/scan-runner.js +133 -0
- package/dist/cli/scan-runner.js.map +1 -0
- package/dist/formatters/plan-json.d.ts +2 -0
- package/dist/formatters/plan-json.js +4 -0
- package/dist/formatters/plan-json.js.map +1 -0
- package/dist/formatters/plan-markdown.d.ts +2 -0
- package/dist/formatters/plan-markdown.js +42 -0
- package/dist/formatters/plan-markdown.js.map +1 -0
- package/dist/formatters/plan-prompt.d.ts +4 -0
- package/dist/formatters/plan-prompt.js +5 -0
- package/dist/formatters/plan-prompt.js.map +1 -0
- package/dist/formatters/plan-terminal.d.ts +5 -0
- package/dist/formatters/plan-terminal.js +62 -0
- package/dist/formatters/plan-terminal.js.map +1 -0
- package/dist/generators/blueprint-renderer.d.ts +3 -0
- package/dist/generators/blueprint-renderer.js +27 -0
- package/dist/generators/blueprint-renderer.js.map +1 -0
- package/dist/generators/claudeWriter.d.ts +3 -0
- package/dist/generators/claudeWriter.js +9 -0
- package/dist/generators/claudeWriter.js.map +1 -0
- package/dist/generators/copilotWriter.d.ts +3 -0
- package/dist/generators/copilotWriter.js +11 -0
- package/dist/generators/copilotWriter.js.map +1 -0
- package/dist/generators/cursorWriter.d.ts +3 -0
- package/dist/generators/cursorWriter.js +14 -0
- package/dist/generators/cursorWriter.js.map +1 -0
- package/dist/generators/genericWriter.d.ts +3 -0
- package/dist/generators/genericWriter.js +9 -0
- package/dist/generators/genericWriter.js.map +1 -0
- package/dist/generators/template-context.d.ts +18 -0
- package/dist/generators/template-context.js +126 -0
- package/dist/generators/template-context.js.map +1 -0
- package/dist/generators/templateRenderer.d.ts +2 -0
- package/dist/generators/templateRenderer.js +19 -0
- package/dist/generators/templateRenderer.js.map +1 -0
- package/dist/generators/windsurfWriter.d.ts +3 -0
- package/dist/generators/windsurfWriter.js +14 -0
- package/dist/generators/windsurfWriter.js.map +1 -0
- package/dist/generators/writer-types.d.ts +11 -0
- package/dist/generators/writer-types.js +40 -0
- package/dist/generators/writer-types.js.map +1 -0
- package/dist/llm/claude-provider.d.ts +8 -0
- package/dist/llm/claude-provider.js +22 -0
- package/dist/llm/claude-provider.js.map +1 -0
- package/dist/llm/concern-classifier.d.ts +15 -0
- package/dist/llm/concern-classifier.js +61 -0
- package/dist/llm/concern-classifier.js.map +1 -0
- package/dist/llm/config.d.ts +11 -0
- package/dist/llm/config.js +120 -0
- package/dist/llm/config.js.map +1 -0
- package/dist/llm/ollama-provider.d.ts +8 -0
- package/dist/llm/ollama-provider.js +27 -0
- package/dist/llm/ollama-provider.js.map +1 -0
- package/dist/llm/openai-provider.d.ts +8 -0
- package/dist/llm/openai-provider.js +19 -0
- package/dist/llm/openai-provider.js.map +1 -0
- package/dist/llm/prompt-builder.d.ts +12 -0
- package/dist/llm/prompt-builder.js +132 -0
- package/dist/llm/prompt-builder.js.map +1 -0
- package/dist/llm/provider.d.ts +17 -0
- package/dist/llm/provider.js +2 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/llm/response-parser.d.ts +6 -0
- package/dist/llm/response-parser.js +128 -0
- package/dist/llm/response-parser.js.map +1 -0
- package/dist/planner/plan-generator.d.ts +7 -0
- package/dist/planner/plan-generator.js +275 -0
- package/dist/planner/plan-generator.js.map +1 -0
- package/dist/planner/plan-prompt-builder.d.ts +9 -0
- package/dist/planner/plan-prompt-builder.js +92 -0
- package/dist/planner/plan-prompt-builder.js.map +1 -0
- package/dist/planner/plan-response-parser.d.ts +7 -0
- package/dist/planner/plan-response-parser.js +21 -0
- package/dist/planner/plan-response-parser.js.map +1 -0
- package/dist/planner/plan-validator.d.ts +3 -0
- package/dist/planner/plan-validator.js +49 -0
- package/dist/planner/plan-validator.js.map +1 -0
- package/dist/reporters/scan-json.d.ts +13 -0
- package/dist/reporters/scan-json.js +26 -0
- package/dist/reporters/scan-json.js.map +1 -0
- package/dist/reporters/terminal.d.ts +6 -0
- package/dist/reporters/terminal.js +224 -0
- package/dist/reporters/terminal.js.map +1 -0
- package/dist/scoring/consistency-score.d.ts +3 -0
- package/dist/scoring/consistency-score.js +23 -0
- package/dist/scoring/consistency-score.js.map +1 -0
- package/dist/scoring/duplication-score.d.ts +3 -0
- package/dist/scoring/duplication-score.js +16 -0
- package/dist/scoring/duplication-score.js.map +1 -0
- package/dist/scoring/health-score.d.ts +4 -0
- package/dist/scoring/health-score.js +20 -0
- package/dist/scoring/health-score.js.map +1 -0
- package/dist/scoring/issue-builder.d.ts +4 -0
- package/dist/scoring/issue-builder.js +62 -0
- package/dist/scoring/issue-builder.js.map +1 -0
- package/dist/scoring/modularity-score.d.ts +3 -0
- package/dist/scoring/modularity-score.js +56 -0
- package/dist/scoring/modularity-score.js.map +1 -0
- package/dist/scoring/pattern-analysis.d.ts +3 -0
- package/dist/scoring/pattern-analysis.js +74 -0
- package/dist/scoring/pattern-analysis.js.map +1 -0
- package/dist/scoring/separation-score.d.ts +3 -0
- package/dist/scoring/separation-score.js +35 -0
- package/dist/scoring/separation-score.js.map +1 -0
- package/dist/skills/detector.d.ts +4 -0
- package/dist/skills/detector.js +104 -0
- package/dist/skills/detector.js.map +1 -0
- package/dist/skills/lister.d.ts +9 -0
- package/dist/skills/lister.js +35 -0
- package/dist/skills/lister.js.map +1 -0
- package/dist/skills/loader.d.ts +6 -0
- package/dist/skills/loader.js +76 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/structure-check.d.ts +2 -0
- package/dist/skills/structure-check.js +37 -0
- package/dist/skills/structure-check.js.map +1 -0
- package/dist/skills/validator.d.ts +6 -0
- package/dist/skills/validator.js +229 -0
- package/dist/skills/validator.js.map +1 -0
- package/dist/types/analysis.d.ts +130 -0
- package/dist/types/analysis.js +41 -0
- package/dist/types/analysis.js.map +1 -0
- package/dist/types/concern.d.ts +48 -0
- package/dist/types/concern.js +16 -0
- package/dist/types/concern.js.map +1 -0
- package/dist/types/generation.d.ts +32 -0
- package/dist/types/generation.js +2 -0
- package/dist/types/generation.js.map +1 -0
- package/dist/types/issue.d.ts +12 -0
- package/dist/types/issue.js +2 -0
- package/dist/types/issue.js.map +1 -0
- package/dist/types/pattern.d.ts +15 -0
- package/dist/types/pattern.js +2 -0
- package/dist/types/pattern.js.map +1 -0
- package/dist/types/plan.d.ts +56 -0
- package/dist/types/plan.js +2 -0
- package/dist/types/plan.js.map +1 -0
- package/dist/types/scan-output.d.ts +84 -0
- package/dist/types/scan-output.js +2 -0
- package/dist/types/scan-output.js.map +1 -0
- package/dist/types/scoring.d.ts +15 -0
- package/dist/types/scoring.js +2 -0
- package/dist/types/scoring.js.map +1 -0
- package/dist/types/skill.d.ts +97 -0
- package/dist/types/skill.js +2 -0
- package/dist/types/skill.js.map +1 -0
- package/dist/utils/agent-detector.d.ts +2 -0
- package/dist/utils/agent-detector.js +22 -0
- package/dist/utils/agent-detector.js.map +1 -0
- package/dist/utils/interactive.d.ts +6 -0
- package/dist/utils/interactive.js +15 -0
- package/dist/utils/interactive.js.map +1 -0
- package/dist/utils/path.d.ts +5 -0
- package/dist/utils/path.js +31 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/progress.d.ts +17 -0
- package/dist/utils/progress.js +48 -0
- package/dist/utils/progress.js.map +1 -0
- package/dist/utils/thresholds.d.ts +6 -0
- package/dist/utils/thresholds.js +48 -0
- package/dist/utils/thresholds.js.map +1 -0
- package/package.json +63 -0
- package/skills/meta/general-js.skill.yaml +131 -0
- package/skills/patterns/clerk-auth.skill.yaml +349 -0
- package/skills/patterns/docker-deploy.skill.yaml +214 -0
- package/skills/patterns/drizzle.skill.yaml +277 -0
- package/skills/patterns/mongoose.skill.yaml +290 -0
- package/skills/patterns/nextauth.skill.yaml +308 -0
- package/skills/patterns/playwright-e2e.skill.yaml +265 -0
- package/skills/patterns/prisma.skill.yaml +255 -0
- package/skills/patterns/s3-storage.skill.yaml +235 -0
- package/skills/patterns/selenium-e2e.skill.yaml +276 -0
- package/skills/patterns/supabase-auth.skill.yaml +298 -0
- package/skills/patterns/supabase.skill.yaml +304 -0
- package/skills/patterns/vercel-deploy.skill.yaml +219 -0
- package/skills/patterns/vitest-testing.skill.yaml +262 -0
- package/skills/stacks/express-api.skill.yaml +155 -0
- package/skills/stacks/fastify-api.skill.yaml +119 -0
- package/skills/stacks/hono-api.skill.yaml +130 -0
- package/skills/stacks/nestjs.skill.yaml +135 -0
- package/skills/stacks/nextjs-app-router.skill.yaml +176 -0
- package/skills/stacks/react-spa.skill.yaml +153 -0
- package/skills/stacks/vue-nuxt.skill.yaml +115 -0
- package/templates/architect-plan.md +139 -0
- package/templates/architect-refactor.md +119 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
schema_version: "2.0.0"
|
|
2
|
+
id: nestjs
|
|
3
|
+
name: "NestJS"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
description: "Enterprise-grade NestJS with module-per-feature structure, dependency injection, guards, and pipes."
|
|
6
|
+
category: stack
|
|
7
|
+
language: javascript
|
|
8
|
+
frameworks:
|
|
9
|
+
- nestjs
|
|
10
|
+
detection:
|
|
11
|
+
dependencies:
|
|
12
|
+
any:
|
|
13
|
+
- "@nestjs/core"
|
|
14
|
+
none:
|
|
15
|
+
- express
|
|
16
|
+
- fastify
|
|
17
|
+
- hono
|
|
18
|
+
source_indicators:
|
|
19
|
+
- "@Module("
|
|
20
|
+
- "@Controller("
|
|
21
|
+
- "@Injectable("
|
|
22
|
+
- "@nestjs/common"
|
|
23
|
+
structure:
|
|
24
|
+
required_dirs:
|
|
25
|
+
- path: src/modules
|
|
26
|
+
purpose: "Feature modules — one directory per domain (users/, orders/, etc.) containing controller, service, and module file."
|
|
27
|
+
- path: src/common
|
|
28
|
+
purpose: "Cross-cutting NestJS primitives shared across all feature modules: guards (auth, roles), pipes (validation, transformation), interceptors (logging, caching), decorators, and exception filters. Nothing business-domain specific lives here."
|
|
29
|
+
- path: src/config
|
|
30
|
+
purpose: "Configuration module and environment validation."
|
|
31
|
+
recommended_dirs:
|
|
32
|
+
- path: src/database
|
|
33
|
+
purpose: "Database module, entity definitions, and repository setup. TypeORM entities, Prisma schema, or Drizzle table definitions live here — not in feature modules. Feature modules import the database module via DI."
|
|
34
|
+
- path: src/shared
|
|
35
|
+
purpose: "Shared DTOs, TypeScript interfaces, and pure utility functions used across feature modules. Unlike src/common (NestJS primitives), src/shared contains plain TypeScript — no NestJS decorators."
|
|
36
|
+
separation:
|
|
37
|
+
rules:
|
|
38
|
+
- concern: module_structure
|
|
39
|
+
belongs_in: src/modules
|
|
40
|
+
rule_text: "Each feature module contains its own controller, service, and module file co-located in one directory. Do NOT put all controllers in one folder and all services in another — group by feature, not by type."
|
|
41
|
+
example: |
|
|
42
|
+
// src/modules/users/users.module.ts
|
|
43
|
+
@Module({ controllers: [UsersController], providers: [UsersService] })
|
|
44
|
+
export class UsersModule {}
|
|
45
|
+
|
|
46
|
+
// src/modules/users/users.controller.ts
|
|
47
|
+
@Controller('users')
|
|
48
|
+
export class UsersController {
|
|
49
|
+
constructor(private readonly usersService: UsersService) {}
|
|
50
|
+
}
|
|
51
|
+
indicators:
|
|
52
|
+
- "@Module("
|
|
53
|
+
- "src/modules"
|
|
54
|
+
- concern: dependency_injection
|
|
55
|
+
belongs_in: src/modules
|
|
56
|
+
rule_text: "Inject all dependencies via constructor — never instantiate services or repositories with `new`. This enables testing and module substitution."
|
|
57
|
+
example: |
|
|
58
|
+
// ✓ Injected via constructor
|
|
59
|
+
@Controller('users')
|
|
60
|
+
export class UsersController {
|
|
61
|
+
constructor(private readonly usersService: UsersService) {}
|
|
62
|
+
@Get() findAll() { return this.usersService.findAll(); }
|
|
63
|
+
}
|
|
64
|
+
anti_indicators:
|
|
65
|
+
- "new UsersService"
|
|
66
|
+
- "new Repository"
|
|
67
|
+
- concern: guards_and_pipes
|
|
68
|
+
belongs_in: src/common
|
|
69
|
+
rule_text: "Auth guards, validation pipes, and response interceptors live in src/common. Apply them globally via APP_GUARD/APP_PIPE in AppModule, or per-controller/route with decorators."
|
|
70
|
+
example: |
|
|
71
|
+
// src/common/guards/jwt-auth.guard.ts
|
|
72
|
+
@Injectable()
|
|
73
|
+
export class JwtAuthGuard extends AuthGuard('jwt') {}
|
|
74
|
+
|
|
75
|
+
// Apply globally in app.module.ts
|
|
76
|
+
providers: [{ provide: APP_GUARD, useClass: JwtAuthGuard }]
|
|
77
|
+
indicators:
|
|
78
|
+
- "@UseGuards"
|
|
79
|
+
- "@UsePipes"
|
|
80
|
+
- "APP_GUARD"
|
|
81
|
+
- "APP_PIPE"
|
|
82
|
+
patterns:
|
|
83
|
+
data_flow:
|
|
84
|
+
direction: "Controller → Service → Repository/ORM"
|
|
85
|
+
rules:
|
|
86
|
+
- "Controllers receive HTTP input and delegate to services."
|
|
87
|
+
- "Services contain business logic and call repositories."
|
|
88
|
+
- "Repositories handle all database interactions."
|
|
89
|
+
error_handling:
|
|
90
|
+
recommended: "Use NestJS exception filters and built-in HttpException classes."
|
|
91
|
+
naming:
|
|
92
|
+
modules: "[resource].module.ts"
|
|
93
|
+
controllers: "[resource].controller.ts"
|
|
94
|
+
services: "[resource].service.ts"
|
|
95
|
+
anti_patterns:
|
|
96
|
+
- id: direct_instantiation
|
|
97
|
+
severity: critical
|
|
98
|
+
description: "Creating service or repository instances manually with `new` instead of using NestJS dependency injection — untestable and bypasses the DI container."
|
|
99
|
+
bad_example: |
|
|
100
|
+
// ❌ Bypasses DI — untestable and breaks module scoping
|
|
101
|
+
@Controller('users')
|
|
102
|
+
export class UsersController {
|
|
103
|
+
private service = new UsersService();
|
|
104
|
+
}
|
|
105
|
+
good_example: |
|
|
106
|
+
// ✓ Injected via constructor — testable and scoped correctly
|
|
107
|
+
constructor(private readonly usersService: UsersService) {}
|
|
108
|
+
- id: flat_file_structure
|
|
109
|
+
severity: warning
|
|
110
|
+
description: "Organizing all controllers in one folder and all services in another instead of grouping by feature module."
|
|
111
|
+
bad_example: |
|
|
112
|
+
// ❌ Flat structure — breaks NestJS module boundaries
|
|
113
|
+
src/controllers/users.controller.ts
|
|
114
|
+
src/services/users.service.ts
|
|
115
|
+
good_example: |
|
|
116
|
+
// ✓ Feature module structure
|
|
117
|
+
src/modules/users/users.controller.ts
|
|
118
|
+
src/modules/users/users.service.ts
|
|
119
|
+
src/modules/users/users.module.ts
|
|
120
|
+
- id: business_logic_in_controller
|
|
121
|
+
severity: critical
|
|
122
|
+
description: "Placing business rules, validation logic, or DB queries directly in controllers instead of services."
|
|
123
|
+
bad_example: |
|
|
124
|
+
@Post()
|
|
125
|
+
async create(@Body() dto: CreateUserDto) {
|
|
126
|
+
const existing = await this.db.findOne({ email: dto.email });
|
|
127
|
+
if (existing) throw new ConflictException();
|
|
128
|
+
dto.password = await bcrypt.hash(dto.password, 10);
|
|
129
|
+
return this.db.save(dto);
|
|
130
|
+
}
|
|
131
|
+
good_example: |
|
|
132
|
+
@Post()
|
|
133
|
+
create(@Body() dto: CreateUserDto) {
|
|
134
|
+
return this.usersService.create(dto);
|
|
135
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
schema_version: "2.0.0"
|
|
2
|
+
id: nextjs-app-router
|
|
3
|
+
name: "Next.js App Router"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
description: "Next.js App Router structure with app routes, layouts, server components, server actions, shared UI, and data-access helpers properly separated."
|
|
6
|
+
category: stack
|
|
7
|
+
language: javascript
|
|
8
|
+
frameworks:
|
|
9
|
+
- next
|
|
10
|
+
- react
|
|
11
|
+
detection:
|
|
12
|
+
dependencies:
|
|
13
|
+
any:
|
|
14
|
+
- next
|
|
15
|
+
files:
|
|
16
|
+
- next.config.js
|
|
17
|
+
- next.config.mjs
|
|
18
|
+
- next.config.ts
|
|
19
|
+
- app
|
|
20
|
+
source_indicators:
|
|
21
|
+
- "next/"
|
|
22
|
+
- "\"use client\""
|
|
23
|
+
- "'use client'"
|
|
24
|
+
structure:
|
|
25
|
+
required_dirs:
|
|
26
|
+
- path: app
|
|
27
|
+
purpose: "App Router entry point. Contains only Next.js file-convention files: layout.tsx, page.tsx, loading.tsx, error.tsx, route.ts. No business logic or reusable components live here."
|
|
28
|
+
- path: components
|
|
29
|
+
purpose: "Reusable React components shared across multiple routes. Components here must not import from app/ — they receive data as props or use shared hooks."
|
|
30
|
+
- path: lib
|
|
31
|
+
purpose: "Server-side data access, third-party SDK wrappers, and shared utilities. All database queries and external API calls live here so they can be reused by server components, actions, and route handlers."
|
|
32
|
+
recommended_dirs:
|
|
33
|
+
- path: actions
|
|
34
|
+
purpose: "Server Actions for form mutations and data mutations. Each file begins with 'use server'. Actions call lib/ functions — they do not access the database directly."
|
|
35
|
+
- path: hooks
|
|
36
|
+
purpose: "Client-only React hooks that encapsulate stateful browser behaviour (useLocalStorage, useDebounce). Must not import server-only modules."
|
|
37
|
+
separation:
|
|
38
|
+
rules:
|
|
39
|
+
- concern: routing
|
|
40
|
+
belongs_in: app
|
|
41
|
+
rule_text: "The app directory owns route structure using App Router file conventions (layout.tsx, page.tsx, loading.tsx, error.tsx, route.ts). Pages are thin: they call lib/ functions and pass data to components. No inline SQL, fetch wrappers, or reusable UI definitions inside page.tsx files."
|
|
42
|
+
example: |
|
|
43
|
+
// app/users/page.tsx — correct: thin page, delegates to lib and components
|
|
44
|
+
import { listUsers } from '@/lib/users';
|
|
45
|
+
import { UsersTable } from '@/components/users-table';
|
|
46
|
+
|
|
47
|
+
export default async function UsersPage() {
|
|
48
|
+
const users = await listUsers();
|
|
49
|
+
return <UsersTable users={users} />;
|
|
50
|
+
}
|
|
51
|
+
indicators:
|
|
52
|
+
- "app/"
|
|
53
|
+
- "layout.tsx"
|
|
54
|
+
- "page.tsx"
|
|
55
|
+
- "route.ts"
|
|
56
|
+
- concern: shared_ui
|
|
57
|
+
belongs_in: components
|
|
58
|
+
rule_text: "Reusable React components belong in components/ so route files stay focused on composition and data loading. A component should receive data as props and not perform its own data fetching unless it is an explicitly-marked client component using SWR/React Query."
|
|
59
|
+
example: |
|
|
60
|
+
// components/users-table.tsx — server-safe, pure presentational
|
|
61
|
+
import type { User } from '@/lib/users';
|
|
62
|
+
|
|
63
|
+
export function UsersTable({ users }: { users: User[] }) {
|
|
64
|
+
return (
|
|
65
|
+
<table>
|
|
66
|
+
<tbody>
|
|
67
|
+
{users.map((user) => (
|
|
68
|
+
<tr key={user.id}><td>{user.name}</td></tr>
|
|
69
|
+
))}
|
|
70
|
+
</tbody>
|
|
71
|
+
</table>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
anti_indicators:
|
|
75
|
+
- "prisma"
|
|
76
|
+
- "sql`"
|
|
77
|
+
- "drizzle"
|
|
78
|
+
- concern: server_action
|
|
79
|
+
belongs_in: actions
|
|
80
|
+
rule_text: "Server Actions for mutations and form handling live in actions/ with an explicit 'use server' directive at the top of the file. Actions validate input, call lib/ functions, and revalidate cache — they do not contain SQL or SDK calls directly."
|
|
81
|
+
example: |
|
|
82
|
+
// actions/user-actions.ts
|
|
83
|
+
'use server';
|
|
84
|
+
|
|
85
|
+
import { revalidatePath } from 'next/cache';
|
|
86
|
+
import { createUser } from '@/lib/users';
|
|
87
|
+
import { z } from 'zod';
|
|
88
|
+
|
|
89
|
+
const schema = z.object({ name: z.string().min(1) });
|
|
90
|
+
|
|
91
|
+
export async function createUserAction(formData: FormData) {
|
|
92
|
+
const { name } = schema.parse({ name: formData.get('name') });
|
|
93
|
+
await createUser({ name });
|
|
94
|
+
revalidatePath('/users');
|
|
95
|
+
}
|
|
96
|
+
indicators:
|
|
97
|
+
- "\"use server\""
|
|
98
|
+
- "'use server'"
|
|
99
|
+
patterns:
|
|
100
|
+
data_flow:
|
|
101
|
+
direction: "app/page.tsx -> lib/ -> database/external API; actions/ -> lib/ -> database"
|
|
102
|
+
rules:
|
|
103
|
+
- "Use React Server Components by default. Only add 'use client' when the component needs browser APIs, event handlers, or client-side state."
|
|
104
|
+
- "Push 'use client' as far down the component tree as possible to maximize server-rendered surface."
|
|
105
|
+
- "Use loading.tsx and error.tsx file conventions for every route segment that performs async data fetching."
|
|
106
|
+
- "Never access process.env directly in components or lib/ — import from a centralized config module."
|
|
107
|
+
naming:
|
|
108
|
+
routes: "Use App Router file conventions: page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx, route.ts. Use (group) folders for layout sharing without URL impact. Use _folder prefix for private non-routable helpers colocated with routes."
|
|
109
|
+
components: "Use PascalCase for component files (UsersTable.tsx) and function names. Use kebab-case for utility and hook files (use-debounce.ts)."
|
|
110
|
+
anti_patterns:
|
|
111
|
+
- id: use_client_everywhere
|
|
112
|
+
severity: warning
|
|
113
|
+
description: "The 'use client' directive is placed at the top of layout, page, or large wrapper components rather than pushed down to only the leaf components that actually need browser interactivity. This unnecessarily turns large component subtrees into client bundles, increasing JavaScript shipped to the browser."
|
|
114
|
+
bad_example: |
|
|
115
|
+
// app/dashboard/layout.tsx — wrong: entire layout becomes a client bundle
|
|
116
|
+
'use client';
|
|
117
|
+
|
|
118
|
+
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
119
|
+
return <div className="dashboard">{children}</div>;
|
|
120
|
+
}
|
|
121
|
+
good_example: |
|
|
122
|
+
// app/dashboard/layout.tsx — server component, no directive needed
|
|
123
|
+
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
|
|
124
|
+
return <div className="dashboard">{children}</div>;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// components/dashboard-nav.tsx — only the interactive nav is a client component
|
|
128
|
+
'use client';
|
|
129
|
+
import { useState } from 'react';
|
|
130
|
+
export function DashboardNav() { /* toggle state, event handlers */ }
|
|
131
|
+
- id: client_data_fetching_by_default
|
|
132
|
+
severity: warning
|
|
133
|
+
description: "Data fetching is moved to client components using useEffect/useState without a user interaction requirement. This delays the first meaningful paint, exposes API endpoints unnecessarily, and forfeits React Server Component streaming and caching benefits."
|
|
134
|
+
bad_example: |
|
|
135
|
+
'use client';
|
|
136
|
+
|
|
137
|
+
export default function UsersPage() {
|
|
138
|
+
const [users, setUsers] = useState([]);
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
fetch('/api/users').then((r) => r.json()).then(setUsers);
|
|
141
|
+
}, []);
|
|
142
|
+
return <UsersTable users={users} />;
|
|
143
|
+
}
|
|
144
|
+
good_example: |
|
|
145
|
+
// Server component — data fetches on the server before any HTML is sent
|
|
146
|
+
import { listUsers } from '@/lib/users';
|
|
147
|
+
import { UsersTable } from '@/components/users-table';
|
|
148
|
+
|
|
149
|
+
export default async function UsersPage() {
|
|
150
|
+
const users = await listUsers();
|
|
151
|
+
return <UsersTable users={users} />;
|
|
152
|
+
}
|
|
153
|
+
- id: direct_db_in_page
|
|
154
|
+
severity: critical
|
|
155
|
+
description: "Database queries or external API calls are made directly inside page.tsx or layout.tsx files instead of being encapsulated in lib/. This scatters data-access logic across the route tree, makes it impossible to reuse queries in Server Actions or Route Handlers, and prevents centralized error handling and logging."
|
|
156
|
+
bad_example: |
|
|
157
|
+
// app/users/page.tsx — wrong: database logic inside the page
|
|
158
|
+
import { prisma } from '@/lib/prisma';
|
|
159
|
+
|
|
160
|
+
export default async function UsersPage() {
|
|
161
|
+
const users = await prisma.user.findMany({ where: { active: true } });
|
|
162
|
+
return <UserList users={users} />;
|
|
163
|
+
}
|
|
164
|
+
good_example: |
|
|
165
|
+
// lib/users.ts — all user queries live here
|
|
166
|
+
import { prisma } from './prisma';
|
|
167
|
+
export async function listActiveUsers() {
|
|
168
|
+
return prisma.user.findMany({ where: { active: true } });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// app/users/page.tsx — page calls lib, never touches prisma directly
|
|
172
|
+
import { listActiveUsers } from '@/lib/users';
|
|
173
|
+
export default async function UsersPage() {
|
|
174
|
+
const users = await listActiveUsers();
|
|
175
|
+
return <UserList users={users} />;
|
|
176
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
schema_version: "2.0.0"
|
|
2
|
+
id: react-spa
|
|
3
|
+
name: "React Single Page Application"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
description: "React SPA structure with UI components, hooks, pages, services, and utilities separated. Enforces one-way data flow and keeps network logic out of render cycles."
|
|
6
|
+
category: stack
|
|
7
|
+
language: javascript
|
|
8
|
+
frameworks:
|
|
9
|
+
- react
|
|
10
|
+
detection:
|
|
11
|
+
dependencies:
|
|
12
|
+
any:
|
|
13
|
+
- react
|
|
14
|
+
none:
|
|
15
|
+
- next
|
|
16
|
+
source_indicators:
|
|
17
|
+
- "from 'react'"
|
|
18
|
+
- "from \"react\""
|
|
19
|
+
- "useState("
|
|
20
|
+
- "useEffect("
|
|
21
|
+
structure:
|
|
22
|
+
required_dirs:
|
|
23
|
+
- path: src/components
|
|
24
|
+
purpose: "Reusable presentational React components shared across multiple pages. Components here receive all data via props and must not call services or perform fetch requests directly."
|
|
25
|
+
- path: src/hooks
|
|
26
|
+
purpose: "Custom React hooks that encapsulate stateful behaviour, data fetching, and side effects. Hooks here are reused by multiple pages or components — one-off hooks live colocated with their component."
|
|
27
|
+
- path: src/services
|
|
28
|
+
purpose: "Functions that perform HTTP calls and interact with external APIs. Every network request in the app originates here, making it the single place to add auth headers, error normalization, and request retries."
|
|
29
|
+
recommended_dirs:
|
|
30
|
+
- path: src/pages
|
|
31
|
+
purpose: "Top-level route components that correspond to URL paths. Pages orchestrate data loading via hooks and pass results down to components — they are not reused across routes."
|
|
32
|
+
- path: src/utils
|
|
33
|
+
purpose: "Pure helper functions with no React dependency (date formatting, string manipulation, array transforms). Functions here must be synchronous and have no side effects."
|
|
34
|
+
separation:
|
|
35
|
+
rules:
|
|
36
|
+
- concern: ui_component
|
|
37
|
+
belongs_in: src/components
|
|
38
|
+
rule_text: "Components render UI from props and delegated hooks instead of embedding network or business logic. A component that calls fetch() or a service directly cannot be rendered in isolation during testing."
|
|
39
|
+
example: |
|
|
40
|
+
// src/components/user-card.tsx — receives data, never fetches it
|
|
41
|
+
import type { User } from '@/types';
|
|
42
|
+
|
|
43
|
+
export function UserCard({ user }: { user: User }) {
|
|
44
|
+
return (
|
|
45
|
+
<article>
|
|
46
|
+
<h2>{user.name}</h2>
|
|
47
|
+
<p>{user.email}</p>
|
|
48
|
+
</article>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
indicators:
|
|
52
|
+
- "return <"
|
|
53
|
+
- "React.FC"
|
|
54
|
+
- concern: state_logic
|
|
55
|
+
belongs_in: src/hooks
|
|
56
|
+
rule_text: "Reusable stateful behaviour belongs in hooks so pages and components stay declarative. A hook name must start with 'use'. If the same useState/useEffect block appears in two components, extract it into a shared hook."
|
|
57
|
+
example: |
|
|
58
|
+
// src/hooks/use-users.ts — all user-list state in one place
|
|
59
|
+
import { useState, useEffect } from 'react';
|
|
60
|
+
import { listUsers } from '@/services/users';
|
|
61
|
+
import type { User } from '@/types';
|
|
62
|
+
|
|
63
|
+
export function useUsers() {
|
|
64
|
+
const [users, setUsers] = useState<User[]>([]);
|
|
65
|
+
const [loading, setLoading] = useState(true);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
void listUsers().then((data) => {
|
|
69
|
+
setUsers(data);
|
|
70
|
+
setLoading(false);
|
|
71
|
+
});
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
return { users, loading };
|
|
75
|
+
}
|
|
76
|
+
indicators:
|
|
77
|
+
- "useState"
|
|
78
|
+
- "useEffect"
|
|
79
|
+
- concern: api_access
|
|
80
|
+
belongs_in: src/services
|
|
81
|
+
rule_text: "HTTP calls and third-party integrations belong in services that hooks and components consume. Services are plain async functions — they do not call useState or useEffect. This makes them independently testable with mocked fetch."
|
|
82
|
+
example: |
|
|
83
|
+
// src/services/users.ts — network logic isolated from React
|
|
84
|
+
import type { User } from '@/types';
|
|
85
|
+
|
|
86
|
+
export async function listUsers(): Promise<User[]> {
|
|
87
|
+
const response = await fetch('/api/users', {
|
|
88
|
+
headers: { Authorization: `Bearer ${getToken()}` },
|
|
89
|
+
});
|
|
90
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
91
|
+
return response.json() as Promise<User[]>;
|
|
92
|
+
}
|
|
93
|
+
indicators:
|
|
94
|
+
- "fetch("
|
|
95
|
+
- "axios."
|
|
96
|
+
patterns:
|
|
97
|
+
data_flow:
|
|
98
|
+
direction: "src/pages -> src/hooks -> src/services -> external API"
|
|
99
|
+
rules:
|
|
100
|
+
- "Components do not perform inline API calls during render. All data fetching happens in hooks."
|
|
101
|
+
- "Shared behaviour moves into custom hooks. If the same useState/useEffect pair appears in two components, it must be extracted."
|
|
102
|
+
- "Lift state to the lowest common ancestor that needs it. Do not put local UI state (modal open, form field value) into global state management."
|
|
103
|
+
- "Derive values from state — do not create a second useState to hold a value that is computable from existing state."
|
|
104
|
+
naming:
|
|
105
|
+
components: "Use PascalCase for component files and function names (UserCard.tsx, ProductList.tsx). Use kebab-case for non-component utility and hook files (use-users.ts, format-date.ts)."
|
|
106
|
+
hooks: "All hook function names must start with 'use' (useUsers, useAuth). The file name matches the hook name in kebab-case (use-users.ts)."
|
|
107
|
+
anti_patterns:
|
|
108
|
+
- id: business_logic_in_components
|
|
109
|
+
severity: warning
|
|
110
|
+
description: "Business logic (validation, data transformation, API calls) is embedded directly in UI components. This makes the component impossible to render in isolation during testing and ties UI to network behaviour."
|
|
111
|
+
bad_example: |
|
|
112
|
+
export function SignupForm() {
|
|
113
|
+
const handleSubmit = async (event: React.FormEvent) => {
|
|
114
|
+
event.preventDefault();
|
|
115
|
+
const payload = validateSignup(new FormData(event.currentTarget));
|
|
116
|
+
await fetch('/api/signup', { method: 'POST', body: JSON.stringify(payload) });
|
|
117
|
+
};
|
|
118
|
+
return <form onSubmit={handleSubmit}>...</form>;
|
|
119
|
+
}
|
|
120
|
+
good_example: |
|
|
121
|
+
// Component delegates to a hook; hook delegates to a service
|
|
122
|
+
export function SignupForm() {
|
|
123
|
+
const { submit, error } = useSignupForm();
|
|
124
|
+
return <form onSubmit={submit}>{error && <p>{error}</p>}</form>;
|
|
125
|
+
}
|
|
126
|
+
- id: inline_api_calls_in_render
|
|
127
|
+
severity: critical
|
|
128
|
+
description: "API calls occur directly during render (outside of useEffect or an async event handler). Each re-render triggers another network request, leading to request storms and race conditions."
|
|
129
|
+
bad_example: |
|
|
130
|
+
export function UsersPage() {
|
|
131
|
+
// called on every render — network storm
|
|
132
|
+
fetch('/api/users').then(() => undefined);
|
|
133
|
+
return <div>Loading...</div>;
|
|
134
|
+
}
|
|
135
|
+
good_example: |
|
|
136
|
+
export function UsersPage() {
|
|
137
|
+
const { users, loading } = useUsers();
|
|
138
|
+
if (loading) return <div>Loading...</div>;
|
|
139
|
+
return <UsersList users={users} />;
|
|
140
|
+
}
|
|
141
|
+
- id: array_index_as_key
|
|
142
|
+
severity: warning
|
|
143
|
+
description: "Using the array index as the React 'key' prop on list items causes silent reconciliation bugs when the list is reordered, filtered, or items are inserted or deleted. React uses the key to match elements between renders — an index-based key can swap component state to the wrong item."
|
|
144
|
+
bad_example: |
|
|
145
|
+
// key={index} breaks when items are reordered or deleted
|
|
146
|
+
{users.map((user, index) => (
|
|
147
|
+
<UserCard key={index} user={user} />
|
|
148
|
+
))}
|
|
149
|
+
good_example: |
|
|
150
|
+
// Stable unique id from the data model — safe for any list mutation
|
|
151
|
+
{users.map((user) => (
|
|
152
|
+
<UserCard key={user.id} user={user} />
|
|
153
|
+
))}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
schema_version: "2.0.0"
|
|
2
|
+
id: vue-nuxt
|
|
3
|
+
name: "Nuxt 3"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
description: "Full-stack Nuxt 3 with auto-imports, composables, Nitro server routes, and Pinia state management."
|
|
6
|
+
category: stack
|
|
7
|
+
language: javascript
|
|
8
|
+
frameworks:
|
|
9
|
+
- nuxt
|
|
10
|
+
- vue
|
|
11
|
+
detection:
|
|
12
|
+
dependencies:
|
|
13
|
+
any:
|
|
14
|
+
- nuxt
|
|
15
|
+
none:
|
|
16
|
+
- next
|
|
17
|
+
source_indicators:
|
|
18
|
+
- "defineNuxtConfig"
|
|
19
|
+
- "nuxt.config"
|
|
20
|
+
- "useNuxtApp"
|
|
21
|
+
structure:
|
|
22
|
+
required_dirs:
|
|
23
|
+
- path: pages
|
|
24
|
+
purpose: "File-based route components — each .vue file maps directly to a URL path. Pages are thin: they call composables for data and delegate rendering to components. No business logic or direct API calls in page components."
|
|
25
|
+
- path: components
|
|
26
|
+
purpose: "Reusable Vue components auto-imported by Nuxt — no import statement needed. Components receive data via props and emit events; they must not call useFetch or useAsyncData directly."
|
|
27
|
+
- path: composables
|
|
28
|
+
purpose: "Reusable stateful logic using Vue Composition API — auto-imported. Composables own local or feature-scoped reactive state. For state that must survive navigation or be shared between distant components, use a Pinia store instead."
|
|
29
|
+
- path: server/api
|
|
30
|
+
purpose: "Server-side Nitro API route handlers auto-discovered by filename ([resource].[method].ts). Handlers call server/utils/ functions — they must not embed business logic or SQL directly."
|
|
31
|
+
recommended_dirs:
|
|
32
|
+
- path: server/middleware
|
|
33
|
+
purpose: "Nitro server-side middleware that runs on every request: auth token validation, rate limiting, CORS headers. Executes before server/api/ route handlers."
|
|
34
|
+
- path: stores
|
|
35
|
+
purpose: "Pinia stores for global state that must persist across route navigations or be shared between unrelated components (auth session, shopping cart, notifications). Local UI state that belongs to one component or page stays as composable state — not in a store."
|
|
36
|
+
- path: utils
|
|
37
|
+
purpose: "Auto-imported pure utility functions with no Vue or Nuxt dependency (formatDate, slugify, truncate). Must be synchronous and side-effect-free."
|
|
38
|
+
- path: server/utils
|
|
39
|
+
purpose: "Server-side utility functions and service layer shared across server/api/ route handlers. Database queries, third-party API wrappers, and business logic live here — never inline in event handlers."
|
|
40
|
+
separation:
|
|
41
|
+
rules:
|
|
42
|
+
- concern: data_fetching
|
|
43
|
+
belongs_in: composables
|
|
44
|
+
rule_text: "Use useFetch() or useAsyncData() inside composables, not directly in page components. This enables reuse, keeps pages declarative, and supports SSR hydration without duplication."
|
|
45
|
+
example: |
|
|
46
|
+
// composables/useUsers.ts
|
|
47
|
+
export function useUsers() {
|
|
48
|
+
return useFetch('/api/users');
|
|
49
|
+
}
|
|
50
|
+
// pages/users.vue
|
|
51
|
+
const { data: users } = useUsers();
|
|
52
|
+
indicators:
|
|
53
|
+
- "useFetch"
|
|
54
|
+
- "useAsyncData"
|
|
55
|
+
- concern: server_routes
|
|
56
|
+
belongs_in: server/api
|
|
57
|
+
rule_text: "Server routes in server/api/ use defineEventHandler(). They call services or utilities for business logic — no DB queries directly in event handlers."
|
|
58
|
+
example: |
|
|
59
|
+
// server/api/users.get.ts
|
|
60
|
+
export default defineEventHandler(async (event) => {
|
|
61
|
+
return getUsersService();
|
|
62
|
+
});
|
|
63
|
+
- concern: auto_imports
|
|
64
|
+
belongs_in: components
|
|
65
|
+
rule_text: "Nuxt auto-imports components, composables, and utils. Do NOT add explicit import statements for these — let Nuxt's auto-import system handle it."
|
|
66
|
+
example: |
|
|
67
|
+
// ✓ Just use it — Nuxt handles the import
|
|
68
|
+
const { data } = useUsers();
|
|
69
|
+
anti_indicators:
|
|
70
|
+
- "import { use"
|
|
71
|
+
- "from '~/composables"
|
|
72
|
+
- "from '~/components"
|
|
73
|
+
patterns:
|
|
74
|
+
data_flow:
|
|
75
|
+
direction: "Page → Composable → useFetch/useAsyncData → Server Route → Service"
|
|
76
|
+
rules:
|
|
77
|
+
- "Pages use composables for data fetching — not useFetch directly."
|
|
78
|
+
- "Server API routes handle business logic via server utilities."
|
|
79
|
+
- "Pinia stores manage global client-side state."
|
|
80
|
+
error_handling:
|
|
81
|
+
recommended: "Use createError() in server routes and useError() in pages for unified error handling."
|
|
82
|
+
naming:
|
|
83
|
+
composables: "use[Resource].ts"
|
|
84
|
+
stores: "[resource].store.ts"
|
|
85
|
+
server_routes: "[resource].[method].ts"
|
|
86
|
+
anti_patterns:
|
|
87
|
+
- id: explicit_auto_imports
|
|
88
|
+
severity: warning
|
|
89
|
+
description: "Writing explicit import statements for components, composables, or utils that Nuxt auto-imports adds noise and can cause confusion."
|
|
90
|
+
bad_example: |
|
|
91
|
+
// ❌ Nuxt auto-imports this — explicit import is redundant
|
|
92
|
+
import { useUsers } from '~/composables/useUsers';
|
|
93
|
+
good_example: |
|
|
94
|
+
// ✓ Just use it — Nuxt handles the import automatically
|
|
95
|
+
const { data } = useUsers();
|
|
96
|
+
- id: db_in_page_component
|
|
97
|
+
severity: critical
|
|
98
|
+
description: "Querying databases directly in page or layout components instead of going through server routes."
|
|
99
|
+
bad_example: |
|
|
100
|
+
// ❌ DB access in a page component — only works server-side, breaks hydration
|
|
101
|
+
const users = await prisma.user.findMany();
|
|
102
|
+
good_example: |
|
|
103
|
+
// ✓ Page → composable → server route → service
|
|
104
|
+
const { data: users } = useUsers();
|
|
105
|
+
- id: fetch_in_component
|
|
106
|
+
severity: warning
|
|
107
|
+
description: "Using useFetch() or useAsyncData() directly in page components instead of wrapping them in composables — prevents reuse and makes pages harder to test."
|
|
108
|
+
bad_example: |
|
|
109
|
+
// pages/users.vue — useFetch directly in page
|
|
110
|
+
const { data: users } = useFetch('/api/users');
|
|
111
|
+
good_example: |
|
|
112
|
+
// composables/useUsers.ts
|
|
113
|
+
export function useUsers() { return useFetch('/api/users'); }
|
|
114
|
+
// pages/users.vue
|
|
115
|
+
const { data: users } = useUsers();
|