oh-my-customcode 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/LICENSE +21 -0
- package/README.md +287 -0
- package/dist/cli/index.js +13299 -0
- package/dist/index.js +927 -0
- package/package.json +74 -0
- package/templates/.claude/contexts/dev.md +20 -0
- package/templates/.claude/contexts/ecomode.md +63 -0
- package/templates/.claude/contexts/index.yaml +41 -0
- package/templates/.claude/contexts/research.md +28 -0
- package/templates/.claude/contexts/review.md +23 -0
- package/templates/.claude/hooks/hooks.json +185 -0
- package/templates/.claude/hooks/hud/index.yaml +27 -0
- package/templates/.claude/hooks/hud/update-status.sh +32 -0
- package/templates/.claude/hooks/index.yaml +46 -0
- package/templates/.claude/hooks/memory-persistence/pre-compact.sh +37 -0
- package/templates/.claude/hooks/memory-persistence/session-end.sh +64 -0
- package/templates/.claude/hooks/memory-persistence/session-start.sh +41 -0
- package/templates/.claude/hooks/strategic-compact/suggest-compact.sh +50 -0
- package/templates/.claude/install-hooks.sh +100 -0
- package/templates/.claude/rules/MAY-optimization.md +93 -0
- package/templates/.claude/rules/MUST-agent-design.md +107 -0
- package/templates/.claude/rules/MUST-agent-identification.md +108 -0
- package/templates/.claude/rules/MUST-continuous-improvement.md +132 -0
- package/templates/.claude/rules/MUST-intent-transparency.md +199 -0
- package/templates/.claude/rules/MUST-language-policy.md +62 -0
- package/templates/.claude/rules/MUST-orchestrator-coordination.md +266 -0
- package/templates/.claude/rules/MUST-parallel-execution.md +341 -0
- package/templates/.claude/rules/MUST-permissions.md +84 -0
- package/templates/.claude/rules/MUST-safety.md +69 -0
- package/templates/.claude/rules/MUST-sync-verification.md +219 -0
- package/templates/.claude/rules/MUST-tool-identification.md +112 -0
- package/templates/.claude/rules/SHOULD-ecomode.md +145 -0
- package/templates/.claude/rules/SHOULD-error-handling.md +102 -0
- package/templates/.claude/rules/SHOULD-hud-statusline.md +89 -0
- package/templates/.claude/rules/SHOULD-interaction.md +103 -0
- package/templates/.claude/rules/SHOULD-memory-integration.md +114 -0
- package/templates/.claude/rules/SHOULD-pipeline-mode.md +165 -0
- package/templates/.claude/rules/index.yaml +125 -0
- package/templates/.claude/uninstall-hooks.sh +52 -0
- package/templates/CLAUDE.md.en +259 -0
- package/templates/CLAUDE.md.ko +259 -0
- package/templates/agents/index.yaml +237 -0
- package/templates/agents/infra-engineer/aws-expert/AGENT.md +47 -0
- package/templates/agents/infra-engineer/aws-expert/index.yaml +27 -0
- package/templates/agents/infra-engineer/docker-expert/AGENT.md +47 -0
- package/templates/agents/infra-engineer/docker-expert/index.yaml +27 -0
- package/templates/agents/manager/creator/AGENT.md +274 -0
- package/templates/agents/manager/creator/index.yaml +66 -0
- package/templates/agents/manager/gitnerd/AGENT.md +91 -0
- package/templates/agents/manager/gitnerd/index.yaml +55 -0
- package/templates/agents/manager/sauron/AGENT.md +153 -0
- package/templates/agents/manager/sauron/index.yaml +52 -0
- package/templates/agents/manager/supplier/AGENT.md +142 -0
- package/templates/agents/manager/supplier/index.yaml +31 -0
- package/templates/agents/manager/sync-checker/AGENT.md +34 -0
- package/templates/agents/manager/sync-checker/index.yaml +32 -0
- package/templates/agents/manager/updater/AGENT.md +125 -0
- package/templates/agents/manager/updater/index.yaml +31 -0
- package/templates/agents/orchestrator/dev-lead/AGENT.md +116 -0
- package/templates/agents/orchestrator/dev-lead/index.yaml +73 -0
- package/templates/agents/orchestrator/planner/AGENT.md +102 -0
- package/templates/agents/orchestrator/planner/index.yaml +38 -0
- package/templates/agents/orchestrator/qa-lead/AGENT.md +92 -0
- package/templates/agents/orchestrator/qa-lead/index.yaml +40 -0
- package/templates/agents/orchestrator/secretary/AGENT.md +132 -0
- package/templates/agents/orchestrator/secretary/index.yaml +55 -0
- package/templates/agents/qa-team/qa-engineer/AGENT.md +98 -0
- package/templates/agents/qa-team/qa-engineer/index.yaml +59 -0
- package/templates/agents/qa-team/qa-planner/AGENT.md +75 -0
- package/templates/agents/qa-team/qa-planner/index.yaml +47 -0
- package/templates/agents/qa-team/qa-writer/AGENT.md +98 -0
- package/templates/agents/qa-team/qa-writer/index.yaml +44 -0
- package/templates/agents/sw-architect/documenter/AGENT.md +120 -0
- package/templates/agents/sw-architect/documenter/index.yaml +39 -0
- package/templates/agents/sw-architect/speckit-agent/AGENT.md +127 -0
- package/templates/agents/sw-architect/speckit-agent/index.yaml +78 -0
- package/templates/agents/sw-engineer/backend/express-expert/AGENT.md +132 -0
- package/templates/agents/sw-engineer/backend/express-expert/index.yaml +36 -0
- package/templates/agents/sw-engineer/backend/fastapi-expert/AGENT.md +47 -0
- package/templates/agents/sw-engineer/backend/fastapi-expert/index.yaml +27 -0
- package/templates/agents/sw-engineer/backend/go-backend-expert/AGENT.md +47 -0
- package/templates/agents/sw-engineer/backend/go-backend-expert/index.yaml +27 -0
- package/templates/agents/sw-engineer/backend/nestjs-expert/AGENT.md +107 -0
- package/templates/agents/sw-engineer/backend/nestjs-expert/index.yaml +43 -0
- package/templates/agents/sw-engineer/backend/springboot-expert/AGENT.md +103 -0
- package/templates/agents/sw-engineer/backend/springboot-expert/index.yaml +69 -0
- package/templates/agents/sw-engineer/frontend/svelte-agent/AGENT.md +71 -0
- package/templates/agents/sw-engineer/frontend/svelte-agent/index.yaml +41 -0
- package/templates/agents/sw-engineer/frontend/vercel-agent/AGENT.md +67 -0
- package/templates/agents/sw-engineer/frontend/vercel-agent/index.yaml +43 -0
- package/templates/agents/sw-engineer/frontend/vuejs-agent/AGENT.md +71 -0
- package/templates/agents/sw-engineer/frontend/vuejs-agent/index.yaml +48 -0
- package/templates/agents/sw-engineer/language/golang-expert/AGENT.md +47 -0
- package/templates/agents/sw-engineer/language/golang-expert/index.yaml +27 -0
- package/templates/agents/sw-engineer/language/java21-expert/AGENT.md +122 -0
- package/templates/agents/sw-engineer/language/java21-expert/index.yaml +51 -0
- package/templates/agents/sw-engineer/language/kotlin-expert/AGENT.md +47 -0
- package/templates/agents/sw-engineer/language/kotlin-expert/index.yaml +27 -0
- package/templates/agents/sw-engineer/language/python-expert/AGENT.md +47 -0
- package/templates/agents/sw-engineer/language/python-expert/index.yaml +27 -0
- package/templates/agents/sw-engineer/language/rust-expert/AGENT.md +47 -0
- package/templates/agents/sw-engineer/language/rust-expert/index.yaml +27 -0
- package/templates/agents/sw-engineer/language/typescript-expert/AGENT.md +47 -0
- package/templates/agents/sw-engineer/language/typescript-expert/index.yaml +27 -0
- package/templates/agents/sw-engineer/tooling/bun-expert/AGENT.md +73 -0
- package/templates/agents/sw-engineer/tooling/bun-expert/index.yaml +46 -0
- package/templates/agents/sw-engineer/tooling/npm-expert/AGENT.md +160 -0
- package/templates/agents/sw-engineer/tooling/npm-expert/index.yaml +45 -0
- package/templates/agents/sw-engineer/tooling/optimizer/AGENT.md +170 -0
- package/templates/agents/sw-engineer/tooling/optimizer/index.yaml +45 -0
- package/templates/agents/system/memory-keeper/AGENT.md +126 -0
- package/templates/agents/system/memory-keeper/index.yaml +45 -0
- package/templates/agents/system/naggy/AGENT.md +72 -0
- package/templates/agents/system/naggy/index.yaml +35 -0
- package/templates/commands/COMMANDS.md +136 -0
- package/templates/commands/creator/agent.md +121 -0
- package/templates/commands/dev/refactor.md +126 -0
- package/templates/commands/dev/review.md +82 -0
- package/templates/commands/git/branch.yaml +8 -0
- package/templates/commands/git/commit.yaml +4 -0
- package/templates/commands/git/pr.yaml +4 -0
- package/templates/commands/git/status.yaml +4 -0
- package/templates/commands/git/sync.yaml +4 -0
- package/templates/commands/index.yaml +225 -0
- package/templates/commands/intent/explain.md +144 -0
- package/templates/commands/memory/recall.md +164 -0
- package/templates/commands/memory/save.md +128 -0
- package/templates/commands/naggy/add.yaml +8 -0
- package/templates/commands/naggy/done.yaml +8 -0
- package/templates/commands/naggy/list.yaml +4 -0
- package/templates/commands/naggy/priority.yaml +11 -0
- package/templates/commands/naggy/remind.yaml +4 -0
- package/templates/commands/npm/audit.yaml +62 -0
- package/templates/commands/npm/publish.yaml +52 -0
- package/templates/commands/npm/version.yaml +62 -0
- package/templates/commands/optimize/analyze.yaml +34 -0
- package/templates/commands/optimize/bundle.yaml +50 -0
- package/templates/commands/optimize/report.yaml +56 -0
- package/templates/commands/pipeline/list.md +81 -0
- package/templates/commands/pipeline/run.md +127 -0
- package/templates/commands/sauron/quick.yaml +4 -0
- package/templates/commands/sauron/report.yaml +4 -0
- package/templates/commands/sauron/watch.yaml +4 -0
- package/templates/commands/supplier/audit.md +133 -0
- package/templates/commands/supplier/fix.md +121 -0
- package/templates/commands/sync/agents.yaml +4 -0
- package/templates/commands/sync/check.yaml +4 -0
- package/templates/commands/sync/commands.yaml +4 -0
- package/templates/commands/sync/docs.yaml +4 -0
- package/templates/commands/sync/fix.yaml +4 -0
- package/templates/commands/system/help.md +137 -0
- package/templates/commands/system/lists.md +86 -0
- package/templates/commands/system/status.md +163 -0
- package/templates/commands/updater/docs.md +165 -0
- package/templates/commands/updater/external.md +214 -0
- package/templates/guides/aws/common-patterns.md +169 -0
- package/templates/guides/aws/index.yaml +26 -0
- package/templates/guides/aws/well-architected.md +143 -0
- package/templates/guides/claude-code/01-overview.md +42 -0
- package/templates/guides/claude-code/03-tools.md +107 -0
- package/templates/guides/claude-code/04-agent-skills.md +90 -0
- package/templates/guides/claude-code/05-agent-sdk.md +129 -0
- package/templates/guides/claude-code/06-mcp.md +165 -0
- package/templates/guides/claude-code/07-prompt-engineering.md +100 -0
- package/templates/guides/claude-code/08-testing.md +58 -0
- package/templates/guides/claude-code/09-guardrails.md +80 -0
- package/templates/guides/claude-code/10-monitoring.md +89 -0
- package/templates/guides/claude-code/index.yaml +51 -0
- package/templates/guides/docker/compose-best-practices.md +284 -0
- package/templates/guides/docker/dockerfile-best-practices.md +262 -0
- package/templates/guides/docker/index.yaml +26 -0
- package/templates/guides/fastapi/best-practices.md +232 -0
- package/templates/guides/fastapi/index.yaml +21 -0
- package/templates/guides/go-backend/index.yaml +26 -0
- package/templates/guides/go-backend/project-layout.md +243 -0
- package/templates/guides/go-backend/uber-style.md +212 -0
- package/templates/guides/golang/concurrency.md +282 -0
- package/templates/guides/golang/effective-go.md +309 -0
- package/templates/guides/golang/error-handling.md +250 -0
- package/templates/guides/golang/index.yaml +27 -0
- package/templates/guides/index.yaml +101 -0
- package/templates/guides/kotlin/coding-conventions.md +247 -0
- package/templates/guides/kotlin/idioms.md +234 -0
- package/templates/guides/kotlin/index.yaml +26 -0
- package/templates/guides/python/index.yaml +26 -0
- package/templates/guides/python/pep8-style-guide.md +202 -0
- package/templates/guides/python/zen-of-python.md +79 -0
- package/templates/guides/rust/error-handling.md +262 -0
- package/templates/guides/rust/index.yaml +26 -0
- package/templates/guides/rust/ownership.md +180 -0
- package/templates/guides/springboot/best-practices.md +361 -0
- package/templates/guides/springboot/index.yaml +22 -0
- package/templates/guides/typescript/advanced-types.md +225 -0
- package/templates/guides/typescript/index.yaml +26 -0
- package/templates/guides/typescript/type-system.md +219 -0
- package/templates/guides/web-design/accessibility.md +66 -0
- package/templates/guides/web-design/index.yaml +20 -0
- package/templates/guides/web-design/performance.md +102 -0
- package/templates/pipelines/examples/code-review.yaml +66 -0
- package/templates/pipelines/index.yaml +18 -0
- package/templates/pipelines/templates/pipeline-template.yaml +50 -0
- package/templates/skills/backend/fastapi-best-practices/SKILL.md +269 -0
- package/templates/skills/backend/fastapi-best-practices/index.yaml +25 -0
- package/templates/skills/backend/go-backend-best-practices/SKILL.md +337 -0
- package/templates/skills/backend/go-backend-best-practices/index.yaml +26 -0
- package/templates/skills/backend/springboot-best-practices/SKILL.md +356 -0
- package/templates/skills/backend/springboot-best-practices/index.yaml +27 -0
- package/templates/skills/development/go-best-practices/SKILL.md +202 -0
- package/templates/skills/development/go-best-practices/index.yaml +25 -0
- package/templates/skills/development/kotlin-best-practices/SKILL.md +255 -0
- package/templates/skills/development/kotlin-best-practices/index.yaml +27 -0
- package/templates/skills/development/python-best-practices/SKILL.md +221 -0
- package/templates/skills/development/python-best-practices/index.yaml +25 -0
- package/templates/skills/development/react-best-practices/SKILL.md +100 -0
- package/templates/skills/development/react-best-practices/index.yaml +39 -0
- package/templates/skills/development/rust-best-practices/SKILL.md +266 -0
- package/templates/skills/development/rust-best-practices/index.yaml +26 -0
- package/templates/skills/development/typescript-best-practices/SKILL.md +320 -0
- package/templates/skills/development/typescript-best-practices/index.yaml +28 -0
- package/templates/skills/development/vercel-deploy/SKILL.md +73 -0
- package/templates/skills/development/vercel-deploy/index.yaml +30 -0
- package/templates/skills/development/web-design-guidelines/SKILL.md +117 -0
- package/templates/skills/development/web-design-guidelines/index.yaml +34 -0
- package/templates/skills/index.yaml +129 -0
- package/templates/skills/infrastructure/aws-best-practices/SKILL.md +279 -0
- package/templates/skills/infrastructure/aws-best-practices/index.yaml +27 -0
- package/templates/skills/infrastructure/docker-best-practices/SKILL.md +274 -0
- package/templates/skills/infrastructure/docker-best-practices/index.yaml +26 -0
- package/templates/skills/orchestration/intent-detection/SKILL.md +214 -0
- package/templates/skills/orchestration/intent-detection/index.yaml +30 -0
- package/templates/skills/orchestration/intent-detection/patterns/agent-triggers.yaml +333 -0
- package/templates/skills/orchestration/pipeline-execution/SKILL.md +188 -0
- package/templates/skills/orchestration/pipeline-execution/index.yaml +27 -0
- package/templates/skills/system/memory-management/SKILL.md +194 -0
- package/templates/skills/system/memory-management/index.yaml +30 -0
- package/templates/skills/system/result-aggregation/SKILL.md +163 -0
- package/templates/skills/system/result-aggregation/index.yaml +36 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
# Spring Boot Best Practices
|
|
2
|
+
|
|
3
|
+
> Source: Spring Documentation and Baeldung
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
com.example.app/
|
|
9
|
+
├── controller/ # REST endpoints
|
|
10
|
+
├── service/ # Business logic
|
|
11
|
+
│ └── impl/
|
|
12
|
+
├── repository/ # Data access
|
|
13
|
+
├── model/ # Domain entities
|
|
14
|
+
├── dto/ # Data transfer objects
|
|
15
|
+
├── config/ # Configuration
|
|
16
|
+
├── exception/ # Custom exceptions
|
|
17
|
+
└── util/ # Utilities
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Dependency Injection
|
|
21
|
+
|
|
22
|
+
### Constructor Injection (Recommended)
|
|
23
|
+
|
|
24
|
+
```java
|
|
25
|
+
@Service
|
|
26
|
+
@RequiredArgsConstructor
|
|
27
|
+
public class UserService {
|
|
28
|
+
private final UserRepository userRepository;
|
|
29
|
+
private final EmailService emailService;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Avoid Field Injection
|
|
34
|
+
|
|
35
|
+
```java
|
|
36
|
+
// NOT RECOMMENDED
|
|
37
|
+
@Service
|
|
38
|
+
public class UserService {
|
|
39
|
+
@Autowired
|
|
40
|
+
private UserRepository userRepository;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## REST Controllers
|
|
45
|
+
|
|
46
|
+
```java
|
|
47
|
+
@RestController
|
|
48
|
+
@RequestMapping("/api/v1/users")
|
|
49
|
+
@RequiredArgsConstructor
|
|
50
|
+
public class UserController {
|
|
51
|
+
|
|
52
|
+
private final UserService userService;
|
|
53
|
+
|
|
54
|
+
@GetMapping
|
|
55
|
+
public List<UserResponse> getAllUsers() {
|
|
56
|
+
return userService.findAll();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@GetMapping("/{id}")
|
|
60
|
+
public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
|
|
61
|
+
return ResponseEntity.ok(userService.findById(id));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@PostMapping
|
|
65
|
+
@ResponseStatus(HttpStatus.CREATED)
|
|
66
|
+
public UserResponse createUser(@Valid @RequestBody UserRequest request) {
|
|
67
|
+
return userService.create(request);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@PutMapping("/{id}")
|
|
71
|
+
public UserResponse updateUser(
|
|
72
|
+
@PathVariable Long id,
|
|
73
|
+
@Valid @RequestBody UserRequest request
|
|
74
|
+
) {
|
|
75
|
+
return userService.update(id, request);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
@DeleteMapping("/{id}")
|
|
79
|
+
@ResponseStatus(HttpStatus.NO_CONTENT)
|
|
80
|
+
public void deleteUser(@PathVariable Long id) {
|
|
81
|
+
userService.delete(id);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Service Layer
|
|
87
|
+
|
|
88
|
+
```java
|
|
89
|
+
public interface UserService {
|
|
90
|
+
UserResponse findById(Long id);
|
|
91
|
+
UserResponse create(UserRequest request);
|
|
92
|
+
UserResponse update(Long id, UserRequest request);
|
|
93
|
+
void delete(Long id);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@Service
|
|
97
|
+
@Transactional(readOnly = true)
|
|
98
|
+
@RequiredArgsConstructor
|
|
99
|
+
public class UserServiceImpl implements UserService {
|
|
100
|
+
|
|
101
|
+
private final UserRepository userRepository;
|
|
102
|
+
private final UserMapper userMapper;
|
|
103
|
+
|
|
104
|
+
@Override
|
|
105
|
+
public UserResponse findById(Long id) {
|
|
106
|
+
User user = userRepository.findById(id)
|
|
107
|
+
.orElseThrow(() -> new UserNotFoundException(id));
|
|
108
|
+
return userMapper.toResponse(user);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@Override
|
|
112
|
+
@Transactional
|
|
113
|
+
public UserResponse create(UserRequest request) {
|
|
114
|
+
User user = userMapper.toEntity(request);
|
|
115
|
+
User saved = userRepository.save(user);
|
|
116
|
+
return userMapper.toResponse(saved);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Repository Layer
|
|
122
|
+
|
|
123
|
+
```java
|
|
124
|
+
public interface UserRepository extends JpaRepository<User, Long> {
|
|
125
|
+
|
|
126
|
+
Optional<User> findByEmail(String email);
|
|
127
|
+
|
|
128
|
+
List<User> findByStatusOrderByCreatedAtDesc(UserStatus status);
|
|
129
|
+
|
|
130
|
+
@Query("SELECT u FROM User u WHERE u.department = :dept AND u.status = 'ACTIVE'")
|
|
131
|
+
List<User> findActiveDepartmentMembers(@Param("dept") String department);
|
|
132
|
+
|
|
133
|
+
@Modifying
|
|
134
|
+
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
|
|
135
|
+
int updateStatus(@Param("id") Long id, @Param("status") UserStatus status);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Entity Design
|
|
140
|
+
|
|
141
|
+
```java
|
|
142
|
+
@Entity
|
|
143
|
+
@Table(name = "users")
|
|
144
|
+
@Getter
|
|
145
|
+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
|
146
|
+
@EntityListeners(AuditingEntityListener.class)
|
|
147
|
+
public class User {
|
|
148
|
+
|
|
149
|
+
@Id
|
|
150
|
+
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
151
|
+
private Long id;
|
|
152
|
+
|
|
153
|
+
@Column(nullable = false, unique = true, length = 100)
|
|
154
|
+
private String email;
|
|
155
|
+
|
|
156
|
+
@Column(nullable = false)
|
|
157
|
+
private String password;
|
|
158
|
+
|
|
159
|
+
@Enumerated(EnumType.STRING)
|
|
160
|
+
@Column(nullable = false)
|
|
161
|
+
private UserStatus status = UserStatus.ACTIVE;
|
|
162
|
+
|
|
163
|
+
@CreatedDate
|
|
164
|
+
@Column(updatable = false)
|
|
165
|
+
private LocalDateTime createdAt;
|
|
166
|
+
|
|
167
|
+
@LastModifiedDate
|
|
168
|
+
private LocalDateTime updatedAt;
|
|
169
|
+
|
|
170
|
+
public User(String email, String password) {
|
|
171
|
+
this.email = email;
|
|
172
|
+
this.password = password;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
public void updatePassword(String newPassword) {
|
|
176
|
+
this.password = newPassword;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Exception Handling
|
|
182
|
+
|
|
183
|
+
```java
|
|
184
|
+
// Custom Exception
|
|
185
|
+
public class UserNotFoundException extends RuntimeException {
|
|
186
|
+
public UserNotFoundException(Long id) {
|
|
187
|
+
super("User not found: " + id);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Global Handler
|
|
192
|
+
@RestControllerAdvice
|
|
193
|
+
@Slf4j
|
|
194
|
+
public class GlobalExceptionHandler {
|
|
195
|
+
|
|
196
|
+
@ExceptionHandler(UserNotFoundException.class)
|
|
197
|
+
@ResponseStatus(HttpStatus.NOT_FOUND)
|
|
198
|
+
public ErrorResponse handleUserNotFound(UserNotFoundException ex) {
|
|
199
|
+
log.warn("User not found: {}", ex.getMessage());
|
|
200
|
+
return new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
@ExceptionHandler(MethodArgumentNotValidException.class)
|
|
204
|
+
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
|
205
|
+
public ErrorResponse handleValidation(MethodArgumentNotValidException ex) {
|
|
206
|
+
List<String> errors = ex.getBindingResult()
|
|
207
|
+
.getFieldErrors()
|
|
208
|
+
.stream()
|
|
209
|
+
.map(e -> e.getField() + ": " + e.getDefaultMessage())
|
|
210
|
+
.toList();
|
|
211
|
+
return new ErrorResponse("VALIDATION_ERROR", String.join(", ", errors));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@ExceptionHandler(Exception.class)
|
|
215
|
+
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
|
216
|
+
public ErrorResponse handleGeneral(Exception ex) {
|
|
217
|
+
log.error("Unexpected error", ex);
|
|
218
|
+
return new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Configuration
|
|
224
|
+
|
|
225
|
+
```yaml
|
|
226
|
+
# application.yml
|
|
227
|
+
spring:
|
|
228
|
+
profiles:
|
|
229
|
+
active: ${SPRING_PROFILES_ACTIVE:local}
|
|
230
|
+
datasource:
|
|
231
|
+
url: ${DATABASE_URL}
|
|
232
|
+
username: ${DATABASE_USERNAME}
|
|
233
|
+
password: ${DATABASE_PASSWORD}
|
|
234
|
+
jpa:
|
|
235
|
+
hibernate:
|
|
236
|
+
ddl-auto: validate
|
|
237
|
+
properties:
|
|
238
|
+
hibernate:
|
|
239
|
+
format_sql: true
|
|
240
|
+
default_batch_fetch_size: 100
|
|
241
|
+
|
|
242
|
+
logging:
|
|
243
|
+
level:
|
|
244
|
+
org.hibernate.SQL: DEBUG
|
|
245
|
+
org.hibernate.type.descriptor.sql: TRACE
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
```java
|
|
249
|
+
@Configuration
|
|
250
|
+
@ConfigurationProperties(prefix = "app")
|
|
251
|
+
@Validated
|
|
252
|
+
@Getter
|
|
253
|
+
@Setter
|
|
254
|
+
public class AppProperties {
|
|
255
|
+
|
|
256
|
+
@NotBlank
|
|
257
|
+
private String name;
|
|
258
|
+
|
|
259
|
+
@NotNull
|
|
260
|
+
private Security security = new Security();
|
|
261
|
+
|
|
262
|
+
@Getter
|
|
263
|
+
@Setter
|
|
264
|
+
public static class Security {
|
|
265
|
+
private String jwtSecret;
|
|
266
|
+
private Duration jwtExpiration = Duration.ofHours(24);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Security Configuration
|
|
272
|
+
|
|
273
|
+
```java
|
|
274
|
+
@Configuration
|
|
275
|
+
@EnableWebSecurity
|
|
276
|
+
@RequiredArgsConstructor
|
|
277
|
+
public class SecurityConfig {
|
|
278
|
+
|
|
279
|
+
private final JwtAuthenticationFilter jwtFilter;
|
|
280
|
+
|
|
281
|
+
@Bean
|
|
282
|
+
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
|
283
|
+
return http
|
|
284
|
+
.csrf(AbstractHttpConfigurer::disable)
|
|
285
|
+
.sessionManagement(session ->
|
|
286
|
+
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
|
287
|
+
.authorizeHttpRequests(auth -> auth
|
|
288
|
+
.requestMatchers("/api/v1/auth/**").permitAll()
|
|
289
|
+
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
|
|
290
|
+
.anyRequest().authenticated())
|
|
291
|
+
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
|
|
292
|
+
.build();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@Bean
|
|
296
|
+
public PasswordEncoder passwordEncoder() {
|
|
297
|
+
return new BCryptPasswordEncoder();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Testing
|
|
303
|
+
|
|
304
|
+
### Controller Test
|
|
305
|
+
|
|
306
|
+
```java
|
|
307
|
+
@WebMvcTest(UserController.class)
|
|
308
|
+
class UserControllerTest {
|
|
309
|
+
|
|
310
|
+
@Autowired
|
|
311
|
+
private MockMvc mockMvc;
|
|
312
|
+
|
|
313
|
+
@MockBean
|
|
314
|
+
private UserService userService;
|
|
315
|
+
|
|
316
|
+
@Test
|
|
317
|
+
void getUser_shouldReturnUser() throws Exception {
|
|
318
|
+
given(userService.findById(1L))
|
|
319
|
+
.willReturn(new UserResponse(1L, "test@example.com"));
|
|
320
|
+
|
|
321
|
+
mockMvc.perform(get("/api/v1/users/1")
|
|
322
|
+
.contentType(MediaType.APPLICATION_JSON))
|
|
323
|
+
.andExpect(status().isOk())
|
|
324
|
+
.andExpect(jsonPath("$.id").value(1))
|
|
325
|
+
.andExpect(jsonPath("$.email").value("test@example.com"));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Integration Test
|
|
331
|
+
|
|
332
|
+
```java
|
|
333
|
+
@SpringBootTest
|
|
334
|
+
@AutoConfigureMockMvc
|
|
335
|
+
@Transactional
|
|
336
|
+
class UserIntegrationTest {
|
|
337
|
+
|
|
338
|
+
@Autowired
|
|
339
|
+
private MockMvc mockMvc;
|
|
340
|
+
|
|
341
|
+
@Autowired
|
|
342
|
+
private UserRepository userRepository;
|
|
343
|
+
|
|
344
|
+
@Test
|
|
345
|
+
void createUser_shouldPersistUser() throws Exception {
|
|
346
|
+
String request = """
|
|
347
|
+
{
|
|
348
|
+
"email": "test@example.com",
|
|
349
|
+
"password": "password123"
|
|
350
|
+
}
|
|
351
|
+
""";
|
|
352
|
+
|
|
353
|
+
mockMvc.perform(post("/api/v1/users")
|
|
354
|
+
.contentType(MediaType.APPLICATION_JSON)
|
|
355
|
+
.content(request))
|
|
356
|
+
.andExpect(status().isCreated());
|
|
357
|
+
|
|
358
|
+
assertThat(userRepository.findByEmail("test@example.com")).isPresent();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Spring Boot Guide
|
|
2
|
+
|
|
3
|
+
metadata:
|
|
4
|
+
name: springboot
|
|
5
|
+
description: Spring Boot framework reference documentation
|
|
6
|
+
|
|
7
|
+
source:
|
|
8
|
+
type: external
|
|
9
|
+
origin: spring.io
|
|
10
|
+
urls:
|
|
11
|
+
- https://docs.spring.io/spring-boot/docs/current/reference/html/
|
|
12
|
+
- https://spring.io/guides
|
|
13
|
+
- https://www.baeldung.com/spring-boot
|
|
14
|
+
last_fetched: "2026-01-22"
|
|
15
|
+
|
|
16
|
+
documents:
|
|
17
|
+
- name: best-practices
|
|
18
|
+
path: ./best-practices.md
|
|
19
|
+
description: Spring Boot best practices and patterns
|
|
20
|
+
|
|
21
|
+
used_by:
|
|
22
|
+
- springboot-expert
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# TypeScript Advanced Types
|
|
2
|
+
|
|
3
|
+
> Reference for advanced TypeScript type patterns
|
|
4
|
+
|
|
5
|
+
## Utility Types
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
// Partial - make all properties optional
|
|
9
|
+
type PartialUser = Partial<User>;
|
|
10
|
+
|
|
11
|
+
// Required - make all properties required
|
|
12
|
+
type RequiredUser = Required<User>;
|
|
13
|
+
|
|
14
|
+
// Readonly - make all properties readonly
|
|
15
|
+
type ReadonlyUser = Readonly<User>;
|
|
16
|
+
|
|
17
|
+
// Pick - select specific properties
|
|
18
|
+
type UserName = Pick<User, 'name' | 'email'>;
|
|
19
|
+
|
|
20
|
+
// Omit - exclude specific properties
|
|
21
|
+
type UserWithoutId = Omit<User, 'id'>;
|
|
22
|
+
|
|
23
|
+
// Record - create object type with key-value pairs
|
|
24
|
+
type UserMap = Record<string, User>;
|
|
25
|
+
|
|
26
|
+
// Exclude - exclude types from union
|
|
27
|
+
type NonNullStatus = Exclude<Status | null, null>;
|
|
28
|
+
|
|
29
|
+
// Extract - extract types from union
|
|
30
|
+
type StringStatus = Extract<Status, string>;
|
|
31
|
+
|
|
32
|
+
// NonNullable - remove null and undefined
|
|
33
|
+
type DefinedValue = NonNullable<string | null | undefined>;
|
|
34
|
+
|
|
35
|
+
// ReturnType - get function return type
|
|
36
|
+
type Result = ReturnType<typeof fetchUser>;
|
|
37
|
+
|
|
38
|
+
// Parameters - get function parameter types
|
|
39
|
+
type Params = Parameters<typeof fetchUser>;
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Mapped Types
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// Make all properties optional
|
|
46
|
+
type Optional<T> = {
|
|
47
|
+
[P in keyof T]?: T[P];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Make all properties readonly
|
|
51
|
+
type Immutable<T> = {
|
|
52
|
+
readonly [P in keyof T]: T[P];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Make all properties nullable
|
|
56
|
+
type Nullable<T> = {
|
|
57
|
+
[P in keyof T]: T[P] | null;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Remap keys
|
|
61
|
+
type Getters<T> = {
|
|
62
|
+
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Filter properties by type
|
|
66
|
+
type OnlyStrings<T> = {
|
|
67
|
+
[P in keyof T as T[P] extends string ? P : never]: T[P];
|
|
68
|
+
};
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Conditional Types
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
// Basic conditional
|
|
75
|
+
type IsString<T> = T extends string ? true : false;
|
|
76
|
+
|
|
77
|
+
// With infer
|
|
78
|
+
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
|
|
79
|
+
|
|
80
|
+
// Array element type
|
|
81
|
+
type ElementType<T> = T extends (infer E)[] ? E : never;
|
|
82
|
+
|
|
83
|
+
// Function return type
|
|
84
|
+
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
|
|
85
|
+
|
|
86
|
+
// Distributive conditional
|
|
87
|
+
type ToArray<T> = T extends any ? T[] : never;
|
|
88
|
+
// ToArray<string | number> = string[] | number[]
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Template Literal Types
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// Simple template
|
|
95
|
+
type Greeting = `Hello, ${string}!`;
|
|
96
|
+
|
|
97
|
+
// With union
|
|
98
|
+
type EventHandler = `on${Capitalize<'click' | 'hover' | 'focus'>}`;
|
|
99
|
+
// 'onClick' | 'onHover' | 'onFocus'
|
|
100
|
+
|
|
101
|
+
// Extract from string
|
|
102
|
+
type ExtractRouteParams<T extends string> =
|
|
103
|
+
T extends `${string}:${infer Param}/${infer Rest}`
|
|
104
|
+
? Param | ExtractRouteParams<Rest>
|
|
105
|
+
: T extends `${string}:${infer Param}`
|
|
106
|
+
? Param
|
|
107
|
+
: never;
|
|
108
|
+
|
|
109
|
+
// ExtractRouteParams<'/users/:id/posts/:postId'>
|
|
110
|
+
// = 'id' | 'postId'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Discriminated Unions
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
interface Circle {
|
|
117
|
+
kind: 'circle';
|
|
118
|
+
radius: number;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
interface Square {
|
|
122
|
+
kind: 'square';
|
|
123
|
+
side: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
interface Rectangle {
|
|
127
|
+
kind: 'rectangle';
|
|
128
|
+
width: number;
|
|
129
|
+
height: number;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
type Shape = Circle | Square | Rectangle;
|
|
133
|
+
|
|
134
|
+
function getArea(shape: Shape): number {
|
|
135
|
+
switch (shape.kind) {
|
|
136
|
+
case 'circle':
|
|
137
|
+
return Math.PI * shape.radius ** 2;
|
|
138
|
+
case 'square':
|
|
139
|
+
return shape.side ** 2;
|
|
140
|
+
case 'rectangle':
|
|
141
|
+
return shape.width * shape.height;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Index Signatures
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// String index
|
|
150
|
+
interface Dictionary {
|
|
151
|
+
[key: string]: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Number index
|
|
155
|
+
interface NumberMap {
|
|
156
|
+
[index: number]: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// With specific properties
|
|
160
|
+
interface User {
|
|
161
|
+
id: string;
|
|
162
|
+
name: string;
|
|
163
|
+
[key: string]: string; // additional properties
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Template literal index
|
|
167
|
+
type Handlers = {
|
|
168
|
+
[K in `on${Capitalize<string>}`]: (event: Event) => void;
|
|
169
|
+
};
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Type Narrowing
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// Control flow analysis
|
|
176
|
+
function process(value: string | number | null) {
|
|
177
|
+
if (value === null) {
|
|
178
|
+
return; // value is null
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (typeof value === 'string') {
|
|
182
|
+
console.log(value.toUpperCase()); // value is string
|
|
183
|
+
} else {
|
|
184
|
+
console.log(value.toFixed(2)); // value is number
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Assertion functions
|
|
189
|
+
function assertIsString(value: unknown): asserts value is string {
|
|
190
|
+
if (typeof value !== 'string') {
|
|
191
|
+
throw new Error('Not a string');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Type predicates
|
|
196
|
+
function isNonNull<T>(value: T): value is NonNullable<T> {
|
|
197
|
+
return value !== null && value !== undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const filtered = items.filter(isNonNull);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Brand Types
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// Create distinct types for same underlying type
|
|
207
|
+
type UserId = string & { readonly brand: unique symbol };
|
|
208
|
+
type PostId = string & { readonly brand: unique symbol };
|
|
209
|
+
|
|
210
|
+
function createUserId(id: string): UserId {
|
|
211
|
+
return id as UserId;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function createPostId(id: string): PostId {
|
|
215
|
+
return id as PostId;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function getUser(id: UserId): User { ... }
|
|
219
|
+
|
|
220
|
+
const userId = createUserId('123');
|
|
221
|
+
const postId = createPostId('456');
|
|
222
|
+
|
|
223
|
+
getUser(userId); // OK
|
|
224
|
+
getUser(postId); // Error: PostId not assignable to UserId
|
|
225
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# TypeScript Guide
|
|
2
|
+
|
|
3
|
+
metadata:
|
|
4
|
+
name: typescript
|
|
5
|
+
description: TypeScript language reference documentation
|
|
6
|
+
|
|
7
|
+
source:
|
|
8
|
+
type: external
|
|
9
|
+
origin: typescriptlang.org
|
|
10
|
+
urls:
|
|
11
|
+
- https://www.typescriptlang.org/docs/handbook/
|
|
12
|
+
- https://google.github.io/styleguide/tsguide.html
|
|
13
|
+
- https://basarat.gitbook.io/typescript/
|
|
14
|
+
last_fetched: "2026-01-22"
|
|
15
|
+
|
|
16
|
+
documents:
|
|
17
|
+
- name: type-system
|
|
18
|
+
path: ./type-system.md
|
|
19
|
+
description: TypeScript type system fundamentals
|
|
20
|
+
|
|
21
|
+
- name: advanced-types
|
|
22
|
+
path: ./advanced-types.md
|
|
23
|
+
description: Advanced TypeScript type patterns
|
|
24
|
+
|
|
25
|
+
used_by:
|
|
26
|
+
- typescript-expert
|