sonamu 0.8.24 → 0.8.26

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 (88) hide show
  1. package/dist/api/__tests__/config.test.js +189 -0
  2. package/dist/api/config.d.ts.map +1 -1
  3. package/dist/api/config.js +7 -2
  4. package/dist/api/sonamu.d.ts.map +1 -1
  5. package/dist/api/sonamu.js +14 -10
  6. package/dist/auth/index.d.ts +1 -0
  7. package/dist/auth/index.d.ts.map +1 -1
  8. package/dist/auth/index.js +2 -1
  9. package/dist/auth/knex-adapter.d.ts +23 -0
  10. package/dist/auth/knex-adapter.d.ts.map +1 -0
  11. package/dist/auth/knex-adapter.js +163 -0
  12. package/dist/auth/plugins/wrappers/admin.d.ts +2 -2
  13. package/dist/bin/__tests__/ts-loader-register.test.js +45 -0
  14. package/dist/bin/cli.js +47 -9
  15. package/dist/bin/ts-loader-register.js +3 -29
  16. package/dist/bin/ts-loader-registration.d.ts +2 -0
  17. package/dist/bin/ts-loader-registration.d.ts.map +1 -0
  18. package/dist/bin/ts-loader-registration.js +42 -0
  19. package/dist/cone/cone-generator.js +3 -3
  20. package/dist/database/puri-subset.test-d.js +9 -1
  21. package/dist/database/puri-subset.types.d.ts +1 -1
  22. package/dist/database/puri-subset.types.d.ts.map +1 -1
  23. package/dist/database/puri-subset.types.js +1 -1
  24. package/dist/testing/fixture-generator.js +5 -5
  25. package/dist/ui/ai-client.js +2 -2
  26. package/dist/ui/api.d.ts.map +1 -1
  27. package/dist/ui/api.js +14 -14
  28. package/dist/ui/cdd-service.d.ts +15 -18
  29. package/dist/ui/cdd-service.d.ts.map +1 -1
  30. package/dist/ui/cdd-service.js +246 -222
  31. package/dist/ui/cdd-types.d.ts +41 -68
  32. package/dist/ui/cdd-types.d.ts.map +1 -1
  33. package/dist/ui/cdd-types.js +2 -2
  34. package/dist/ui-web/assets/index-CKo0Z2Iu.css +1 -0
  35. package/dist/ui-web/assets/{index-CxiydzeC.js → index-DK-2aacv.js} +83 -83
  36. package/dist/ui-web/index.html +2 -2
  37. package/package.json +6 -2
  38. package/src/api/__tests__/config.test.ts +225 -0
  39. package/src/api/config.ts +10 -4
  40. package/src/api/sonamu.ts +16 -13
  41. package/src/auth/index.ts +1 -0
  42. package/src/auth/knex-adapter.ts +208 -0
  43. package/src/bin/__tests__/ts-loader-register.test.ts +62 -0
  44. package/src/bin/cli.ts +52 -9
  45. package/src/bin/ts-loader-register.ts +2 -32
  46. package/src/bin/ts-loader-registration.ts +55 -0
  47. package/src/cone/cone-generator.ts +2 -2
  48. package/src/database/puri-subset.test-d.ts +102 -0
  49. package/src/database/puri-subset.types.ts +1 -1
  50. package/src/skills/commands/sonamu-skills.md +20 -0
  51. package/src/skills/sonamu/SKILL.md +179 -137
  52. package/src/skills/sonamu/ai-agents.md +69 -69
  53. package/src/skills/sonamu/api.md +147 -147
  54. package/src/skills/sonamu/auth-migration.md +220 -220
  55. package/src/skills/sonamu/auth-plugins.md +83 -83
  56. package/src/skills/sonamu/auth.md +106 -106
  57. package/src/skills/sonamu/cdd.md +65 -200
  58. package/src/skills/sonamu/cone.md +138 -138
  59. package/src/skills/sonamu/config.md +191 -191
  60. package/src/skills/sonamu/create-sonamu.md +66 -66
  61. package/src/skills/sonamu/database.md +158 -158
  62. package/src/skills/sonamu/entity-basic.md +292 -293
  63. package/src/skills/sonamu/entity-relations.md +246 -246
  64. package/src/skills/sonamu/entity-validation-checklist.md +124 -124
  65. package/src/skills/sonamu/fixture-cli.md +231 -231
  66. package/src/skills/sonamu/framework-change.md +37 -37
  67. package/src/skills/sonamu/frontend.md +223 -223
  68. package/src/skills/sonamu/i18n.md +82 -82
  69. package/src/skills/sonamu/migration.md +77 -77
  70. package/src/skills/sonamu/model.md +222 -222
  71. package/src/skills/sonamu/naite.md +86 -86
  72. package/src/skills/sonamu/project-init.md +228 -228
  73. package/src/skills/sonamu/puri.md +122 -122
  74. package/src/skills/sonamu/scaffolding.md +154 -154
  75. package/src/skills/sonamu/skill-contribution.md +124 -124
  76. package/src/skills/sonamu/subset.md +46 -46
  77. package/src/skills/sonamu/tasks.md +82 -82
  78. package/src/skills/sonamu/testing-devrunner.md +147 -147
  79. package/src/skills/sonamu/testing.md +673 -673
  80. package/src/skills/sonamu/upsert.md +79 -79
  81. package/src/skills/sonamu/vector.md +67 -67
  82. package/src/testing/fixture-generator.ts +4 -4
  83. package/src/ui/ai-client.ts +1 -1
  84. package/src/ui/api.ts +18 -17
  85. package/src/ui/cdd-service.ts +264 -254
  86. package/src/ui/cdd-types.ts +40 -75
  87. package/dist/ui-web/assets/index-BrQKU3j9.css +0 -1
  88. package/src/skills/sonamu/workflow.md +0 -317
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: sonamu-api
3
- description: Sonamu @api 데코레이터로 Model 메서드를 HTTP 엔드포인트로 노출. httpMethod, guards, clients 옵션 설정. Use when exposing Model methods as API endpoints.
3
+ description: Expose Model methods as HTTP endpoints with the Sonamu @api decorator. Configure httpMethod, guards, and clients options. Use when exposing Model methods as API endpoints.
4
4
  ---
5
5
 
6
- # @api 데코레이터
6
+ # @api Decorator
7
7
 
8
- ## 기본 사용
8
+ ## Basic Usage
9
9
 
10
10
  ```typescript
11
11
  @api({ httpMethod: "GET" })
@@ -13,35 +13,35 @@ async findById(id: number): Promise<User> { }
13
13
  // → GET /user/findById?id=1
14
14
  ```
15
15
 
16
- ## 옵션
16
+ ## Options
17
17
 
18
- | 옵션 | 설명 | 기본값 |
19
- |------|------|--------|
18
+ | Option | Description | Default |
19
+ |--------|-------------|---------|
20
20
  | `httpMethod` | GET, POST, PUT, DELETE, PATCH | GET |
21
- | `clients` | 생성할 클라이언트 타입 | `["axios"]` |
22
- | `resourceName` | TanStack Query queryKey | - |
23
- | `guards` | 인증/권한 가드 | - |
24
- | `path` | 커스텀 경로 | `/{model}/{method}` |
25
- | `description` | API 설명 (문서화용) | - |
26
- | `timeout` | 요청 타임아웃 (ms) | - |
27
- | `contentType` | 응답 Content-Type | `application/json` |
28
- | `cacheControl` | Cache-Control 헤더 설정 | - |
29
- | `compress` | 응답 압축 설정 (`false`로 비활성화 가능) | - |
30
-
31
- ## clients 옵션
32
-
33
- | Client | 용도 |
34
- |--------|------|
35
- | `axios` | 일반 API 호출 |
36
- | `axios-multipart` | 파일 업로드 (axios) |
37
- | `tanstack-query` | 조회용 Query hook |
38
- | `tanstack-mutation` | 변경용 Mutation hook |
39
- | `tanstack-mutation-multipart` | 파일 업로드 Mutation |
40
- | `window-fetch` | 브라우저 fetch API |
41
-
42
- ## 패턴별 예시
43
-
44
- ### 조회 API
21
+ | `clients` | Client types to generate | `["axios"]` |
22
+ | `resourceName` | queryKey for TanStack Query | - |
23
+ | `guards` | Authentication/authorization guards | - |
24
+ | `path` | Custom path | `/{model}/{method}` |
25
+ | `description` | API description (for documentation) | - |
26
+ | `timeout` | Request timeout (ms) | - |
27
+ | `contentType` | Response Content-Type | `application/json` |
28
+ | `cacheControl` | Cache-Control header setting | - |
29
+ | `compress` | Response compression setting (can disable with `false`) | - |
30
+
31
+ ## clients Options
32
+
33
+ | Client | Purpose |
34
+ |--------|---------|
35
+ | `axios` | General API calls |
36
+ | `axios-multipart` | File upload (axios) |
37
+ | `tanstack-query` | Query hook for reads |
38
+ | `tanstack-mutation` | Mutation hook for writes |
39
+ | `tanstack-mutation-multipart` | File upload Mutation |
40
+ | `window-fetch` | Browser fetch API |
41
+
42
+ ## Pattern Examples
43
+
44
+ ### Read API
45
45
 
46
46
  ```typescript
47
47
  @api({
@@ -52,7 +52,7 @@ async findById(id: number): Promise<User> { }
52
52
  async findMany(params: UserListParams): Promise<ListResult<User>> { }
53
53
  ```
54
54
 
55
- ### 변경 API
55
+ ### Write API
56
56
 
57
57
  ```typescript
58
58
  @api({
@@ -62,14 +62,14 @@ async findMany(params: UserListParams): Promise<ListResult<User>> { }
62
62
  async save(params: UserSaveParams[]): Promise<number[]> { }
63
63
  ```
64
64
 
65
- ### 권한 필요 API
65
+ ### API Requiring Authorization
66
66
 
67
67
  ```typescript
68
68
  @api({ httpMethod: "POST", guards: ["admin"] })
69
69
  async del(ids: number[]): Promise<number> { }
70
70
  ```
71
71
 
72
- ## Context 접근
72
+ ## Context Access
73
73
 
74
74
  ```typescript
75
75
  import { Sonamu } from "sonamu";
@@ -81,60 +81,60 @@ async me(): Promise<User | null> {
81
81
  }
82
82
  ```
83
83
 
84
- | Context 속성 | 설명 |
85
- |-------------|------|
86
- | `user` | 인증된 사용자 (better-auth User, null이면 미인증) |
87
- | `session` | 현재 세션 정보 (better-auth Session, null이면 미인증) |
84
+ | Context Property | Description |
85
+ |-----------------|-------------|
86
+ | `user` | Authenticated user (better-auth User, null if unauthenticated) |
87
+ | `session` | Current session info (better-auth Session, null if unauthenticated) |
88
88
  | `request` | FastifyRequest |
89
89
  | `reply` | FastifyReply |
90
- | `headers` | HTTP 요청 헤더 |
91
- | `bufferedFiles` | 버퍼 모드 업로드 파일 |
92
- | `uploadedFiles` | 스트림 모드 업로드 파일 |
93
- | `locale` | 요청 언어 |
90
+ | `headers` | HTTP request headers |
91
+ | `bufferedFiles` | Buffer mode uploaded files |
92
+ | `uploadedFiles` | Stream mode uploaded files |
93
+ | `locale` | Request locale |
94
94
 
95
- ## 파일 업로드 (@upload)
95
+ ## File Upload (@upload)
96
96
 
97
- > **CRITICAL: `@upload`는 `@api` 없이 단독으로 사용한다.**
98
- > `@upload`를 붙이면 POST 엔드포인트와 `axios-multipart`/`tanstack-mutation-multipart` 클라이언트가 **자동 생성**된다.
99
- > `@api`를 함께 붙이면 `checkSingleDecorator` 충돌로 **빌드 에러**가 발생한다.
97
+ > **CRITICAL: `@upload` is used standalone without `@api`.**
98
+ > Adding `@upload` **automatically generates** a POST endpoint and `axios-multipart`/`tanstack-mutation-multipart` clients.
99
+ > Adding `@api` alongside it causes a **build error** due to `checkSingleDecorator` conflict.
100
100
 
101
101
  ```typescript
102
102
  // CORRECT
103
103
  @upload({ limits: { files: 10 }, guards: ["user"] })
104
104
  async upload(...): Promise<number[]> { }
105
105
 
106
- // WRONG — 빌드 에러 발생
106
+ // WRONG — causes build error
107
107
  @api({ httpMethod: "POST", clients: ["axios-multipart"] })
108
108
  @upload({ limits: { files: 10 } })
109
109
  async upload(...): Promise<number[]> { }
110
110
  ```
111
111
 
112
- **`@upload` 지원 옵션** (`httpMethod`, `clients`는 지원하지 않음자동 설정됨)
112
+ **`@upload` supported options** (`httpMethod`, `clients` are not supported set automatically)
113
113
 
114
- | 옵션 | 설명 |
115
- |------|------|
116
- | `guards` | 인증/권한 가드 |
117
- | `limits` | 파일 개수/크기 제한 (`{ files: N }`) |
118
- | `consume` | `"buffer"` (기본) 또는 `"stream"` |
119
- | `description` | API 문서 설명 |
120
- | `destination` | 스트림 모드 전용: 스토리지 드라이버 |
121
- | `keyGenerator` | 스트림 모드 전용: 저장 경로 생성 함수 |
114
+ | Option | Description |
115
+ |--------|-------------|
116
+ | `guards` | Authentication/authorization guards |
117
+ | `limits` | File count/size limits (`{ files: N }`) |
118
+ | `consume` | `"buffer"` (default) or `"stream"` |
119
+ | `description` | API documentation description |
120
+ | `destination` | Stream mode only: storage driver key |
121
+ | `keyGenerator` | Stream mode only: function to generate storage path |
122
122
 
123
- ### 파라미터 규칙: 반드시 단일 객체로 묶기
123
+ ### Parameter Rule: Must Wrap in a Single Object
124
124
 
125
- > **CRITICAL: `@upload` 메서드에 파라미터가 2 이상이면 반드시 단일 객체로 묶는다.**
125
+ > **CRITICAL: If an `@upload` method has 2 or more parameters, they must be wrapped into a single object.**
126
126
  >
127
- > primitive 파라미터를 여러 쓰면 `services.template.ts`의 codegen 버그로 `useUploadMutation`이 잘못 생성된다.
127
+ > Using multiple primitive parameters causes a codegen bug in `services.template.ts` that generates `useUploadMutation` incorrectly.
128
128
 
129
129
  ```typescript
130
- // WRONG — codegen 깨짐 (mutationFn 인수 누락)
130
+ // WRONG — codegen breaks (missing mutationFn argument)
131
131
  async upload(entity_type: string, entity_id: number, file_type: string)
132
132
 
133
- // CORRECT — 단일 객체로 묶기
133
+ // CORRECT — wrap in a single object
134
134
  async upload(params: { entity_type: string; entity_id: number; file_type: string })
135
135
  ```
136
136
 
137
- 호출부 패턴:
137
+ Call site pattern:
138
138
  ```typescript
139
139
  uploadMutation.mutate({
140
140
  params: { entity_type, entity_id, file_type },
@@ -142,40 +142,40 @@ uploadMutation.mutate({
142
142
  })
143
143
  ```
144
144
 
145
- > 자세한 원인 분석은 `framework-change.md`의 `@upload 다중 파라미터` 섹션 참고.
145
+ > For detailed root cause analysis, see the `@upload multiple parameters` section in `framework-change.md`.
146
146
 
147
- ### 버퍼 모드 (기본)
147
+ ### Buffer Mode (Default)
148
148
 
149
149
  ```typescript
150
150
  @upload({ limits: { files: 10 } })
151
151
  async uploadFiles(): Promise<{ files: SonamuFile[] }> {
152
152
  const { bufferedFiles } = Sonamu.getContext();
153
- // bufferedFiles[].buffer로 파일 데이터 접근
153
+ // Access file data via bufferedFiles[].buffer
154
154
  }
155
155
  ```
156
156
 
157
- ### 스트림 모드 (대용량)
157
+ ### Stream Mode (Large Files)
158
158
 
159
159
  ```typescript
160
160
  @upload({
161
161
  consume: "stream",
162
- destination: "s3", // 또는 "fs"
162
+ destination: "s3", // or "fs"
163
163
  keyGenerator: (file) => `uploads/${Date.now()}-${file.filename}`,
164
164
  limits: { files: 5 },
165
165
  })
166
166
  async uploadLargeFiles(): Promise<{ urls: string[] }> {
167
167
  const { uploadedFiles } = Sonamu.getContext();
168
- // uploadedFiles[].key로 저장된 경로 접근
168
+ // Access stored path via uploadedFiles[].key
169
169
  }
170
170
  ```
171
171
 
172
172
  ---
173
173
 
174
- ## 실전 비즈니스 로직 패턴
174
+ ## Real-world Business Logic Patterns
175
175
 
176
- ### 트랜잭션과 이력 기록
176
+ ### Transaction with History Logging
177
177
 
178
- 상태 변경 트랜잭션으로 메인 데이터와 이력을 함께 처리하는 패턴:
178
+ Pattern for atomically handling main data and history together when changing state:
179
179
 
180
180
  ```typescript
181
181
  // consultation.model.ts
@@ -189,7 +189,7 @@ async changeStatus(
189
189
  const wdb = this.getPuri("w");
190
190
 
191
191
  return wdb.transaction(async (trx) => {
192
- // 1. 상담 업데이트
192
+ // 1. Update consultation
193
193
  await trx.ubRegister("consultations", {
194
194
  id,
195
195
  status,
@@ -197,7 +197,7 @@ async changeStatus(
197
197
  });
198
198
  await trx.ubUpsert("consultations");
199
199
 
200
- // 2. 상태 변경 이력 기록
200
+ // 2. Record status change history
201
201
  await trx.ubRegister("consultation_histories", {
202
202
  consultation_id: id,
203
203
  status,
@@ -206,20 +206,20 @@ async changeStatus(
206
206
  });
207
207
  await trx.ubUpsert("consultation_histories");
208
208
 
209
- // 3. 결과 반환
209
+ // 3. Return result
210
210
  return this.findById("A", id);
211
211
  });
212
212
  }
213
213
  ```
214
214
 
215
- **핵심 포인트:**
216
- - 트랜잭션으로 원자성 보장
217
- - ubRegister + ubUpsert 패턴
218
- - 변경 최신 데이터 반환
215
+ **Key points:**
216
+ - Atomicity guaranteed by transaction
217
+ - ubRegister + ubUpsert pattern
218
+ - Return latest data after change
219
219
 
220
- ### 검증 로직과 비즈니스 규칙
220
+ ### Validation Logic and Business Rules
221
221
 
222
- 등록 중복 체크, 정원 확인 복잡한 검증을 수행하는 패턴:
222
+ Pattern for complex validation such as duplicate checks and capacity checks before registration:
223
223
 
224
224
  ```typescript
225
225
  @api({ httpMethod: "POST", guards: ["user"] })
@@ -227,49 +227,49 @@ async enroll(
227
227
  courseId: number,
228
228
  userId: number
229
229
  ): Promise<Enrollment> {
230
- // 1. 중복 등록 방지
230
+ // 1. Prevent duplicate registration
231
231
  const existing = await this.findOne("A", {
232
232
  course_id: courseId,
233
233
  user_id: userId,
234
234
  });
235
235
 
236
236
  if (existing) {
237
- throw new Error("이미 등록된 강좌입니다");
237
+ throw new Error("Already enrolled in this course");
238
238
  }
239
239
 
240
- // 2. 정원 확인
240
+ // 2. Check capacity
241
241
  const course = await CourseModel.findById("A", courseId);
242
242
  const { total } = await this.findMany({ course_id: courseId });
243
243
 
244
244
  if (total >= course.max_students) {
245
- throw new Error("정원이 가득 찼습니다");
245
+ throw new Error("Course is at capacity");
246
246
  }
247
247
 
248
- // 3. 등록 실행
248
+ // 3. Enroll
249
249
  const [id] = await this.save([{ course_id: courseId, user_id: userId }]);
250
250
  return this.findById("A", id);
251
251
  }
252
252
  ```
253
253
 
254
- **핵심 포인트:**
255
- - 단계별 검증 (중복정원)
256
- - 명확한 에러 메시지
257
- - 검증 통과 저장
254
+ **Key points:**
255
+ - Step-by-step validation (duplicatecapacity)
256
+ - Clear error messages
257
+ - Save after validation passes
258
258
 
259
- ### 권한 가드 활용
259
+ ### Using Authorization Guards
260
260
 
261
- 사용자 역할에 따른 접근 제어:
261
+ Access control based on user role:
262
262
 
263
263
  ```typescript
264
- // 일반 사용자 전용
264
+ // Regular user only
265
265
  @api({ httpMethod: "POST", guards: ["user"] })
266
266
  async save(spa: PostSaveParams[]): Promise<number[]> { }
267
267
 
268
- // 관리자 전용
268
+ // Admin only
269
269
  @api({ httpMethod: "POST", guards: ["admin"] })
270
270
  async del(ids: number[]): Promise<number> { }
271
271
 
272
- // 현재 로그인 사용자 정보 활용
272
+ // Using currently logged-in user info
273
273
  @api({ httpMethod: "GET", guards: ["user"] })
274
274
  async myConsultations(): Promise<ListResult<Consultation>> {
275
275
  const { user } = Sonamu.getContext();
@@ -277,56 +277,56 @@ async myConsultations(): Promise<ListResult<Consultation>> {
277
277
  }
278
278
  ```
279
279
 
280
- ### API 테스트 작성
280
+ ### Writing API Tests
281
281
 
282
- Business Logic 테스트에서 커스텀 API를 검증:
282
+ Validating custom APIs in Business Logic tests:
283
283
 
284
284
  ```typescript
285
285
  // consultation.test.ts
286
286
  describe("E. Business Logic", () => {
287
- test("상태 변경 API", async () => {
287
+ test("Status change API", async () => {
288
288
  const { consultationId } = await createTestConsultationWithDeps();
289
289
 
290
- // 커스텀 API 호출
290
+ // Call custom API
291
291
  const updated = await ConsultationModel.changeStatus(
292
292
  consultationId,
293
293
  "completed",
294
- "상담 완료"
294
+ "Consultation complete"
295
295
  );
296
296
 
297
297
  expect(updated.status).toBe("completed");
298
298
 
299
- // 이력 기록 확인
299
+ // Verify history was recorded
300
300
  const histories = await ConsultationHistoryModel.findMany({
301
301
  consultation_id: consultationId,
302
302
  });
303
303
  expect(histories.rows).toHaveLength(1);
304
304
  });
305
305
 
306
- test("등록 검증", async () => {
306
+ test("Enrollment validation", async () => {
307
307
  const courseId = 1;
308
308
  const userId = 1;
309
309
 
310
- // 등록 성공
310
+ // First enrollment succeeds
311
311
  await EnrollmentModel.enroll(courseId, userId);
312
312
 
313
- // 중복 등록 실패
313
+ // Duplicate enrollment fails
314
314
  await expect(
315
315
  EnrollmentModel.enroll(courseId, userId)
316
- ).rejects.toThrow("이미 등록된 강좌입니다");
316
+ ).rejects.toThrow("Already enrolled in this course");
317
317
  });
318
318
  });
319
319
  ```
320
320
 
321
321
  ---
322
322
 
323
- ## 컨벤션과 베스트 프랙티스
323
+ ## Conventions and Best Practices
324
324
 
325
- ### 에러 메시지 패턴
325
+ ### Error Message Pattern
326
326
 
327
- 일관된 에러 메시지를 위해 `this.modelName`과 `SD()` 함수를 사용합니다.
327
+ Use `this.modelName` and the `SD()` function for consistent error messages.
328
328
 
329
- **BAD: 하드코딩된 모델명**
329
+ **BAD: Hardcoded model name**
330
330
  ```typescript
331
331
  // findById
332
332
  if (!rows[0]) {
@@ -337,27 +337,27 @@ if (!rows[0]) {
337
337
  throw new BadRequestException(SD("error.unknownSearchField")(params.search));
338
338
  ```
339
339
 
340
- **GOOD: this.modelName 사용**
340
+ **GOOD: Using this.modelName**
341
341
  ```typescript
342
- // findById - 모델명 자동 인식
342
+ // findById - auto-detects model name
343
343
  if (!rows[0]) {
344
344
  throw new NotFoundException(SD("notFound")(this.modelName, id));
345
345
  }
346
346
 
347
- // findMany - 짧고 명확한
347
+ // findMany - short and clear key
348
348
  throw new BadRequestException(SD("search.invalidField")(params.search));
349
349
  ```
350
350
 
351
- **장점:**
352
- - DRY 원칙 준수: 모델명 곳에서 관리
353
- - 리팩토링 안전: 모델명 변경 에러 메시지 자동 반영
354
- - 짧은 i18n 키: `notFound`, `search.invalidField`가 간결
351
+ **Benefits:**
352
+ - DRY principle: model name managed in one place
353
+ - Refactoring safe: error messages auto-reflect model name changes
354
+ - Short i18n keys: `notFound`, `search.invalidField` are more concise
355
355
 
356
- ### satisfies 키워드
356
+ ### satisfies Keyword
357
357
 
358
- TypeScript satisfies 키워드로 타입 추론을 유지하면서 타입 체크합니다.
358
+ Use TypeScript's satisfies keyword to preserve type inference while checking types.
359
359
 
360
- **BAD: 타입 추론 상실**
360
+ **BAD: Loss of type inference**
361
361
  ```typescript
362
362
  const params: RoleListParams = {
363
363
  num: 24,
@@ -368,7 +368,7 @@ const params: RoleListParams = {
368
368
  };
369
369
  ```
370
370
 
371
- **GOOD: satisfies로 타입 체크 + 추론 유지**
371
+ **GOOD: Type check + preserved inference with satisfies**
372
372
  ```typescript
373
373
  const params = {
374
374
  num: 24,
@@ -379,27 +379,27 @@ const params = {
379
379
  } satisfies RoleListParams;
380
380
  ```
381
381
 
382
- **장점:**
383
- - 컴파일 타임 검증: params RoleListParams 타입을 만족하는지 확인
384
- - 타입 추론 유지: params 실제 타입이 좁혀진 상태로 유지됨
385
- - IDE 지원 향상: 자동완성과 타입 체크가 정확
382
+ **Benefits:**
383
+ - Compile-time verification: checks that params satisfies the RoleListParams type
384
+ - Preserved type inference: params keeps its narrowed type
385
+ - Better IDE support: more accurate autocomplete and type checking
386
386
 
387
- ### debug 옵션
387
+ ### debug Option
388
388
 
389
- executeSubsetQuery의 debug 옵션은 기본값이 false이므로 명시할 필요 없습니다.
389
+ The debug option in executeSubsetQuery defaults to false, so it does not need to be specified explicitly.
390
390
 
391
- **BAD: 불필요한 debug: false**
391
+ **BAD: Unnecessary debug: false**
392
392
  ```typescript
393
393
  return this.executeSubsetQuery({
394
394
  subset,
395
395
  qb,
396
396
  params,
397
397
  enhancers,
398
- debug: false, // 기본값이므로 불필요
398
+ debug: false, // unnecessary — it's the default
399
399
  });
400
400
  ```
401
401
 
402
- **GOOD: 기본값 활용**
402
+ **GOOD: Use the default**
403
403
  ```typescript
404
404
  return this.executeSubsetQuery({
405
405
  subset,
@@ -409,20 +409,20 @@ return this.executeSubsetQuery({
409
409
  });
410
410
  ```
411
411
 
412
- **debug: true를 사용하는 경우:**
412
+ **When to use debug: true:**
413
413
  ```typescript
414
- // 디버깅 시에만 명시
414
+ // Only specify when debugging
415
415
  return this.executeSubsetQuery({
416
416
  subset,
417
417
  qb,
418
418
  params,
419
- debug: true, // SQL 쿼리 로그 출력
419
+ debug: true, // Print SQL query log
420
420
  });
421
421
  ```
422
422
 
423
- ## @stream 데코레이터 (SSE)
423
+ ## @stream Decorator (SSE)
424
424
 
425
- Server-Sent Events 엔드포인트를 생성합니다.
425
+ Creates a Server-Sent Events endpoint.
426
426
 
427
427
  ```typescript
428
428
  import { stream } from "sonamu";
@@ -439,29 +439,29 @@ import { z } from "zod";
439
439
  async processStream() { ... }
440
440
  ```
441
441
 
442
- | 옵션 | 설명 | 필수 |
443
- |------|------|------|
444
- | `type` | `"sse"` (현재 SSE 지원) | |
445
- | `events` | Zod 스키마로 이벤트 키별 페이로드 정의 | |
446
- | `path` | 커스텀 경로 | - |
447
- | `resourceName` | 리소스 이름 | - |
448
- | `guards` | 인증/권한 가드 | - |
442
+ | Option | Description | Required |
443
+ |--------|-------------|----------|
444
+ | `type` | `"sse"` (only SSE currently supported) | Yes |
445
+ | `events` | Define event keys and payloads with Zod schema | Yes |
446
+ | `path` | Custom path | - |
447
+ | `resourceName` | Resource name | - |
448
+ | `guards` | Authentication/authorization guards | - |
449
449
 
450
- ## @transactional 데코레이터
450
+ ## @transactional Decorator
451
451
 
452
- 메서드 전체를 자동 트랜잭션으로 감쌉니다. 이미 트랜잭션 컨텍스트 안이면 재사용합니다.
452
+ Wraps the entire method in an automatic transaction. Reuses an existing transaction context if one is already active.
453
453
 
454
454
  ```typescript
455
455
  import { transactional } from "sonamu";
456
456
 
457
457
  @transactional({ isolation: "serializable" })
458
458
  async transferFunds(fromId: number, toId: number, amount: number) {
459
- // this.getPuri("w") 자동으로 트랜잭션 안에서 실행됨
459
+ // this.getPuri("w") automatically runs inside the transaction
460
460
  }
461
461
  ```
462
462
 
463
- | 옵션 | 설명 | 기본값 |
464
- |------|------|--------|
465
- | `isolation` | 트랜잭션 격리 수준 (read uncommitted/read committed/repeatable read/serializable) | - |
466
- | `readOnly` | 읽기 전용 트랜잭션 | `false` |
467
- | `dbPreset` | DB 프리셋 | `"w"` |
463
+ | Option | Description | Default |
464
+ |--------|-------------|---------|
465
+ | `isolation` | Transaction isolation level (read uncommitted/read committed/repeatable read/serializable) | - |
466
+ | `readOnly` | Read-only transaction | `false` |
467
+ | `dbPreset` | DB preset | `"w"` |