sonamu 0.7.15 → 0.7.17
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/dist/ai/providers/rtzr/error.d.ts +1 -1
- package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
- package/dist/api/config.d.ts +1 -0
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/decorators.d.ts +1 -1
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +1 -1
- package/dist/api/sonamu.d.ts +3 -1
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +51 -40
- package/dist/database/base-model.d.ts +16 -6
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +44 -3
- package/dist/database/base-model.types.d.ts +29 -48
- package/dist/database/base-model.types.d.ts.map +1 -1
- package/dist/database/base-model.types.js +12 -2
- package/dist/database/puri.d.ts +2 -1
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +2 -1
- package/dist/database/puri.types.d.ts +3 -3
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +1 -1
- package/dist/entity/entity-manager.d.ts +8 -4
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity.d.ts +10 -1
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +84 -39
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/syncer/checksum.d.ts +8 -3
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +17 -9
- package/dist/syncer/code-generator.js +7 -2
- package/dist/syncer/syncer.d.ts +6 -6
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +27 -13
- package/dist/tasks/workflow-manager.d.ts +3 -3
- package/dist/tasks/workflow-manager.d.ts.map +1 -1
- package/dist/tasks/workflow-manager.js +15 -11
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +8 -6
- package/dist/template/implementations/model.template.js +5 -5
- package/dist/template/implementations/services.template.d.ts +17 -0
- package/dist/template/implementations/services.template.d.ts.map +1 -0
- package/dist/template/implementations/services.template.js +159 -0
- package/dist/template/implementations/view_form.template.js +2 -2
- package/dist/template/implementations/view_id_async_select.template.js +2 -2
- package/dist/template/implementations/view_list.template.js +5 -5
- package/dist/types/types.d.ts +43 -25
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +29 -17
- package/dist/ui/ai-api.d.ts +2 -0
- package/dist/ui/ai-api.d.ts.map +1 -1
- package/dist/ui/ai-api.js +43 -49
- package/dist/ui/ai-client.d.ts +10 -0
- package/dist/ui/ai-client.d.ts.map +1 -1
- package/dist/ui/ai-client.js +457 -437
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +14 -3
- package/dist/ui-web/assets/{index-J9MCfjCd.js → index-DzqUrTB-.js} +56 -59
- package/dist/ui-web/index.html +1 -1
- package/package.json +12 -8
- package/src/api/config.ts +3 -0
- package/src/api/decorators.ts +6 -1
- package/src/api/sonamu.ts +71 -52
- package/src/database/base-model.ts +66 -11
- package/src/database/base-model.types.ts +79 -76
- package/src/database/puri.ts +5 -1
- package/src/database/puri.types.ts +3 -6
- package/src/entity/entity.ts +83 -34
- package/src/index.ts +1 -0
- package/src/shared/app.shared.ts.txt +1 -1
- package/src/shared/web.shared.ts.txt +0 -43
- package/src/syncer/checksum.ts +31 -9
- package/src/syncer/code-generator.ts +8 -1
- package/src/syncer/syncer.ts +38 -26
- package/src/tasks/workflow-manager.ts +16 -12
- package/src/template/implementations/generated.template.ts +17 -3
- package/src/template/implementations/model.template.ts +4 -4
- package/src/template/implementations/services.template.ts +226 -0
- package/src/template/implementations/view_form.template.ts +1 -1
- package/src/template/implementations/view_id_async_select.template.ts +1 -1
- package/src/template/implementations/view_list.template.ts +4 -4
- package/src/types/types.ts +33 -16
- package/src/ui/ai-api.ts +61 -60
- package/src/ui/ai-client.ts +535 -499
- package/src/ui/api.ts +14 -2
- package/src/ui/entity.instructions.md +536 -0
- package/dist/template/implementations/service.template.d.ts +0 -29
- package/dist/template/implementations/service.template.d.ts.map +0 -1
- package/dist/template/implementations/service.template.js +0 -202
- package/dist/ui-web/assets/provider-utils_false-BKJD46kk.js +0 -1
- package/dist/ui-web/assets/provider-utils_false-Bu5lmX18.js +0 -1
- package/src/template/implementations/service.template.ts +0 -328
package/src/ui/api.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
TemplateKey,
|
|
27
27
|
} from "../types/types";
|
|
28
28
|
import { nonNullable } from "../utils/utils";
|
|
29
|
+
import { setAiApi } from "./ai-api";
|
|
29
30
|
|
|
30
31
|
export async function sonamuUIApiPlugin(fastify: FastifyInstance) {
|
|
31
32
|
fastify.register(
|
|
@@ -54,6 +55,8 @@ export async function sonamuUIApiPlugin(fastify: FastifyInstance) {
|
|
|
54
55
|
return result;
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
await setAiApi(server);
|
|
59
|
+
|
|
57
60
|
server.get("/api/sonamu/config", async () => {
|
|
58
61
|
return Sonamu.config;
|
|
59
62
|
});
|
|
@@ -345,15 +348,23 @@ export async function sonamuUIApiPlugin(fastify: FastifyInstance) {
|
|
|
345
348
|
entityId: string;
|
|
346
349
|
subsetKey: string;
|
|
347
350
|
fields: string[];
|
|
351
|
+
fieldsInternal?: string[];
|
|
348
352
|
};
|
|
349
353
|
}>("/api/entity/modifySubset", async (request) => {
|
|
350
354
|
return await waitForHMRCompleted(async () => {
|
|
351
|
-
const { entityId, subsetKey, fields } = request.body;
|
|
355
|
+
const { entityId, subsetKey, fields, fieldsInternal } = request.body;
|
|
352
356
|
const entity = EntityManager.get(entityId);
|
|
353
357
|
entity.subsets[subsetKey] = fields;
|
|
358
|
+
if (fieldsInternal !== undefined) {
|
|
359
|
+
if (fieldsInternal.length > 0) {
|
|
360
|
+
entity.subsetsInternal[subsetKey] = fieldsInternal;
|
|
361
|
+
} else {
|
|
362
|
+
delete entity.subsetsInternal[subsetKey];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
354
365
|
await entity.save();
|
|
355
366
|
|
|
356
|
-
return { updated: fields };
|
|
367
|
+
return { updated: fields, updatedInternal: fieldsInternal };
|
|
357
368
|
});
|
|
358
369
|
});
|
|
359
370
|
|
|
@@ -367,6 +378,7 @@ export async function sonamuUIApiPlugin(fastify: FastifyInstance) {
|
|
|
367
378
|
const { entityId, subsetKey } = request.body;
|
|
368
379
|
const entity = EntityManager.get(entityId);
|
|
369
380
|
delete entity.subsets[subsetKey];
|
|
381
|
+
delete entity.subsetsInternal[subsetKey];
|
|
370
382
|
await entity.save();
|
|
371
383
|
|
|
372
384
|
return 1;
|
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
# Sonamu Entity JSON Template and Rules
|
|
2
|
+
|
|
3
|
+
## Basic Template
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"id": "EntityName",
|
|
8
|
+
"table": "table_name",
|
|
9
|
+
"title": "엔티티 제목",
|
|
10
|
+
"props": [
|
|
11
|
+
{ "name": "id", "type": "integer", "desc": "ID" },
|
|
12
|
+
{
|
|
13
|
+
"name": "created_at",
|
|
14
|
+
"type": "date",
|
|
15
|
+
"desc": "등록일시",
|
|
16
|
+
"dbDefault": "CURRENT_TIMESTAMP"
|
|
17
|
+
}
|
|
18
|
+
// Additional property definitions go here
|
|
19
|
+
],
|
|
20
|
+
"indexes": [],
|
|
21
|
+
"subsets": {
|
|
22
|
+
"A": ["id", "created_at"]
|
|
23
|
+
},
|
|
24
|
+
"enums": {
|
|
25
|
+
"EntityNameOrderBy": { "id-desc": "ID최신순" },
|
|
26
|
+
"EntityNameSearchField": { "id": "ID" }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Required Properties
|
|
32
|
+
|
|
33
|
+
모든 엔티티는 다음 프로퍼티를 **필수**로 포함해야 합니다:
|
|
34
|
+
|
|
35
|
+
1. **id** - 기본키
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{ "name": "id", "type": "integer", "desc": "ID" }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
2. **created_at** - 등록일시
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"name": "created_at",
|
|
45
|
+
"type": "date",
|
|
46
|
+
"desc": "등록일시",
|
|
47
|
+
"dbDefault": "CURRENT_TIMESTAMP"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Required Enums
|
|
52
|
+
|
|
53
|
+
모든 엔티티는 다음 Enum을 **필수**로 포함해야 합니다:
|
|
54
|
+
|
|
55
|
+
1. **EntityNameOrderBy** - 정렬 옵션
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
"EntityNameOrderBy": { "id-desc": "ID최신순", "id-asc": "ID오름차순" }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
2. **EntityNameSearchField** - 검색 필드
|
|
62
|
+
```json
|
|
63
|
+
"EntityNameSearchField": { "id": "ID" }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Property Types and Their Attributes
|
|
67
|
+
|
|
68
|
+
### 공통 필드 (Common Fields)
|
|
69
|
+
|
|
70
|
+
모든 프로퍼티 타입에서 사용할 수 있는 공통 필드입니다:
|
|
71
|
+
|
|
72
|
+
| 필드 | 타입 | 필수 | 설명 |
|
|
73
|
+
| --------- | --------------------------- | ---- | ------------------------------ |
|
|
74
|
+
| name | string | ✓ | 프로퍼티 이름 |
|
|
75
|
+
| desc | string | | 설명 |
|
|
76
|
+
| nullable | boolean | | null 허용 여부 (기본값: false) |
|
|
77
|
+
| toFilter | boolean | | 필터 조건으로 사용 여부 |
|
|
78
|
+
| dbDefault | string \| number \| boolean | | DB 기본값 |
|
|
79
|
+
| generated | object | | Generated Column 설정 |
|
|
80
|
+
|
|
81
|
+
**Generated Column 설정:**
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"generated": {
|
|
86
|
+
"type": "STORED", // "STORED" | "VIRTUAL"
|
|
87
|
+
"expression": "lower(name)"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- `STORED`: 값이 디스크에 저장됨
|
|
93
|
+
- `VIRTUAL`: 조회 시 계산됨 (json, vector, 배열 타입 사용 불가)
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
### 1. Integer / Integer[]
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{ "name": "count", "type": "integer", "desc": "수량" }
|
|
101
|
+
{ "name": "scores", "type": "integer[]", "desc": "점수 목록" }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 2. BigInteger / BigInteger[]
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{ "name": "total_amount", "type": "bigInteger", "desc": "총액" }
|
|
108
|
+
{ "name": "big_numbers", "type": "bigInteger[]", "desc": "큰 숫자 목록" }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 3. String / String[]
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{ "name": "title", "type": "string", "desc": "제목", "length": 255 }
|
|
115
|
+
{ "name": "tags", "type": "string[]", "desc": "태그 목록" }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- `length`: 최대 길이 (Optional, 생략 시 text)
|
|
119
|
+
|
|
120
|
+
### 4. Boolean / Boolean[]
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{ "name": "is_active", "type": "boolean", "desc": "활성여부", "dbDefault": false }
|
|
124
|
+
{ "name": "flags", "type": "boolean[]", "desc": "플래그 목록" }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 5. Date / Date[]
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{ "name": "created_at", "type": "date", "desc": "등록일시", "dbDefault": "CURRENT_TIMESTAMP" }
|
|
131
|
+
{ "name": "holidays", "type": "date[]", "desc": "휴일 목록" }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- PostgreSQL의 `timestamptz` 타입으로 매핑됨
|
|
135
|
+
|
|
136
|
+
### 6. Number / Number[]
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"name": "price",
|
|
141
|
+
"type": "number",
|
|
142
|
+
"desc": "가격",
|
|
143
|
+
"precision": 10,
|
|
144
|
+
"scale": 2,
|
|
145
|
+
"numberType": "numeric"
|
|
146
|
+
}
|
|
147
|
+
{ "name": "rates", "type": "number[]", "desc": "비율 목록" }
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
- `precision`: 전체 자릿수 (Optional)
|
|
151
|
+
- `scale`: 소수점 이하 자릿수 (Optional)
|
|
152
|
+
- `numberType`: `"real"` | `"double precision"` | `"numeric"` (Optional, 기본값: numeric)
|
|
153
|
+
|
|
154
|
+
### 7. Numeric / Numeric[]
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{ "name": "exact_value", "type": "numeric", "desc": "정밀값", "precision": 20, "scale": 8 }
|
|
158
|
+
{ "name": "exact_values", "type": "numeric[]", "desc": "정밀값 목록" }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- `precision`: 전체 자릿수 (Optional)
|
|
162
|
+
- `scale`: 소수점 이하 자릿수 (Optional)
|
|
163
|
+
- TypeScript에서 `string`으로 매핑됨 (정밀도 유지)
|
|
164
|
+
|
|
165
|
+
### 8. UUID / UUID[]
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{ "name": "external_id", "type": "uuid", "desc": "외부 ID" }
|
|
169
|
+
{ "name": "reference_ids", "type": "uuid[]", "desc": "참조 ID 목록" }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 9. Enum / Enum[] (Required: id)
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"name": "status",
|
|
177
|
+
"type": "enum",
|
|
178
|
+
"id": "ProductStatus",
|
|
179
|
+
"desc": "상태",
|
|
180
|
+
"length": 16,
|
|
181
|
+
"dbDefault": "\"active\""
|
|
182
|
+
}
|
|
183
|
+
{ "name": "categories", "type": "enum[]", "id": "ProductCategory", "desc": "카테고리 목록" }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
- `id`: enums에 정의된 Enum ID (Required)
|
|
187
|
+
- `length`: 최대 길이 (Optional, enum 타입만)
|
|
188
|
+
|
|
189
|
+
**주의**: `id`로 지정한 Enum은 반드시 `enums` 객체에 정의되어 있어야 합니다.
|
|
190
|
+
|
|
191
|
+
### 10. JSON (Required: id)
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"name": "metadata",
|
|
196
|
+
"type": "json",
|
|
197
|
+
"id": "ProductMetadata",
|
|
198
|
+
"desc": "메타데이터",
|
|
199
|
+
"dbDefault": "{}"
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
- `id`: 타입 ID (Required) - 별도 타입 정의 필요
|
|
204
|
+
|
|
205
|
+
### 11. Virtual (Required: id)
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"name": "full_name",
|
|
210
|
+
"type": "virtual",
|
|
211
|
+
"id": "string",
|
|
212
|
+
"desc": "전체 이름"
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
- `id`: 타입 ID (Required)
|
|
217
|
+
- DB에 컬럼이 생성되지 않음, TypeScript 타입만 생성
|
|
218
|
+
|
|
219
|
+
### 12. Vector / Vector[] (Required: dimensions)
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"name": "embedding",
|
|
224
|
+
"type": "vector",
|
|
225
|
+
"dimensions": 1536,
|
|
226
|
+
"desc": "임베딩 벡터"
|
|
227
|
+
}
|
|
228
|
+
{ "name": "embeddings", "type": "vector[]", "dimensions": 768, "desc": "임베딩 목록" }
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
- `dimensions`: 벡터 차원 수 (Required)
|
|
232
|
+
- pgvector 확장 필요
|
|
233
|
+
|
|
234
|
+
### 13. TsVector
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"name": "search_vector",
|
|
239
|
+
"type": "tsvector",
|
|
240
|
+
"desc": "검색 벡터",
|
|
241
|
+
"generated": {
|
|
242
|
+
"type": "STORED",
|
|
243
|
+
"expression": "to_tsvector('korean', coalesce(title, '') || ' ' || coalesce(content, ''))"
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
- PostgreSQL 전문 검색용 타입
|
|
249
|
+
|
|
250
|
+
## Relation Types
|
|
251
|
+
|
|
252
|
+
### 14. BelongsToOne (Required: with, relationType)
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"name": "author",
|
|
257
|
+
"type": "relation",
|
|
258
|
+
"with": "User",
|
|
259
|
+
"relationType": "BelongsToOne",
|
|
260
|
+
"desc": "작성자",
|
|
261
|
+
"nullable": true, // Optional
|
|
262
|
+
"customJoinClause": "...", // Optional
|
|
263
|
+
"useConstraint": true, // Optional (기본값: true)
|
|
264
|
+
"onUpdate": "CASCADE", // Optional (기본값: RESTRICT)
|
|
265
|
+
"onDelete": "CASCADE" // Optional (기본값: RESTRICT)
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### 15. HasMany (Required: with, relationType, joinColumn)
|
|
270
|
+
|
|
271
|
+
```json
|
|
272
|
+
{
|
|
273
|
+
"name": "comments",
|
|
274
|
+
"type": "relation",
|
|
275
|
+
"with": "Comment",
|
|
276
|
+
"relationType": "HasMany",
|
|
277
|
+
"joinColumn": "post_id", // Required: 상대 엔티티의 FK 컬럼명
|
|
278
|
+
"fromColumn": "id", // Optional: 이 엔티티의 참조 컬럼 (기본값: id)
|
|
279
|
+
"desc": "댓글 목록"
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 16. ManyToMany (Required: with, relationType, joinTable, onUpdate, onDelete)
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"name": "tags",
|
|
288
|
+
"type": "relation",
|
|
289
|
+
"with": "Tag",
|
|
290
|
+
"relationType": "ManyToMany",
|
|
291
|
+
"joinTable": "posts__tags", // Required: "테이블명__테이블명" 형식
|
|
292
|
+
"onUpdate": "CASCADE", // Required
|
|
293
|
+
"onDelete": "CASCADE", // Required
|
|
294
|
+
"desc": "태그 목록"
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### 17. OneToOne
|
|
299
|
+
|
|
300
|
+
**hasJoinColumn: true인 경우** (FK를 이 엔티티가 소유):
|
|
301
|
+
|
|
302
|
+
```json
|
|
303
|
+
{
|
|
304
|
+
"name": "profile",
|
|
305
|
+
"type": "relation",
|
|
306
|
+
"with": "UserProfile",
|
|
307
|
+
"relationType": "OneToOne",
|
|
308
|
+
"hasJoinColumn": true,
|
|
309
|
+
"customJoinClause": "...", // Optional
|
|
310
|
+
"useConstraint": true, // Optional (기본값: true)
|
|
311
|
+
"onUpdate": "CASCADE", // Optional (기본값: RESTRICT)
|
|
312
|
+
"onDelete": "CASCADE", // Optional (기본값: RESTRICT)
|
|
313
|
+
"desc": "프로필"
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**hasJoinColumn: false인 경우** (FK를 상대 엔티티가 소유):
|
|
318
|
+
|
|
319
|
+
```json
|
|
320
|
+
{
|
|
321
|
+
"name": "profile",
|
|
322
|
+
"type": "relation",
|
|
323
|
+
"with": "UserProfile",
|
|
324
|
+
"relationType": "OneToOne",
|
|
325
|
+
"hasJoinColumn": false,
|
|
326
|
+
"customJoinClause": "ON users.id = user_profiles.user_id", // Optional
|
|
327
|
+
"desc": "프로필"
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Additional Rules
|
|
332
|
+
|
|
333
|
+
### Entity Rules
|
|
334
|
+
|
|
335
|
+
1. 모든 엔티티는 `id`, `created_at` 프로퍼티를 필수로 포함해야 합니다.
|
|
336
|
+
2. 모든 엔티티는 `EntityNameOrderBy`, `EntityNameSearchField` Enum을 필수로 포함해야 합니다.
|
|
337
|
+
3. 엔티티 내에서 사용하는 Enum ID는 엔티티 이름을 접두어로 사용합니다. (예: UserStatus, ProductType)
|
|
338
|
+
4. indexes가 지정되지 않으면 빈 배열로 반환합니다.
|
|
339
|
+
5. subsets이 지정되지 않으면 `{ "A": ["id"] }`로 반환합니다.
|
|
340
|
+
6. relation 필드명은 `_id` 접미어 대신 관련 엔티티를 나타내는 이름을 사용합니다. (예: "user", "author", "category")
|
|
341
|
+
|
|
342
|
+
### Property Rules
|
|
343
|
+
|
|
344
|
+
1. `nullable`이 false(기본값)인 경우 생략합니다.
|
|
345
|
+
2. `dbDefault`가 필요 없는 경우 생략합니다.
|
|
346
|
+
3. `desc`가 엔티티 제목과 중복되는 경우 생략할 수 있습니다.
|
|
347
|
+
4. 단수형은 단일 값, 복수형은 배열 값에 사용합니다. (예: tag vs tags)
|
|
348
|
+
|
|
349
|
+
### Type-specific Required Fields Summary
|
|
350
|
+
|
|
351
|
+
| Type | Required Fields |
|
|
352
|
+
| -------------------------------------------- | ------------------------------------------------- |
|
|
353
|
+
| integer, integer[], bigInteger, bigInteger[] | - |
|
|
354
|
+
| string, string[] | - (length는 optional) |
|
|
355
|
+
| boolean, boolean[] | - |
|
|
356
|
+
| date, date[] | - |
|
|
357
|
+
| number, number[] | - (precision, scale, numberType는 optional) |
|
|
358
|
+
| numeric, numeric[] | - (precision, scale는 optional) |
|
|
359
|
+
| uuid, uuid[] | - |
|
|
360
|
+
| enum | id |
|
|
361
|
+
| enum[] | id |
|
|
362
|
+
| json | id |
|
|
363
|
+
| virtual | id |
|
|
364
|
+
| vector, vector[] | dimensions |
|
|
365
|
+
| tsvector | - |
|
|
366
|
+
| relation (BelongsToOne) | with, relationType |
|
|
367
|
+
| relation (HasMany) | with, relationType, joinColumn |
|
|
368
|
+
| relation (ManyToMany) | with, relationType, joinTable, onUpdate, onDelete |
|
|
369
|
+
| relation (OneToOne, hasJoinColumn: true) | with, relationType, hasJoinColumn |
|
|
370
|
+
| relation (OneToOne, hasJoinColumn: false) | with, relationType, hasJoinColumn |
|
|
371
|
+
|
|
372
|
+
### Subset Rules
|
|
373
|
+
|
|
374
|
+
1. 현재 엔티티의 프로퍼티는 프로퍼티 `name`을 사용합니다.
|
|
375
|
+
2. relation 프로퍼티의 하위 필드는 `${관계명}.${프로퍼티명}` 형식을 사용합니다.
|
|
376
|
+
3. subset 항목은 props에 정의된 순서대로 나열합니다.
|
|
377
|
+
|
|
378
|
+
### Index Rules
|
|
379
|
+
|
|
380
|
+
인덱스는 `name`, `type`, `columns` 필드가 필수입니다.
|
|
381
|
+
|
|
382
|
+
**기본 인덱스:**
|
|
383
|
+
|
|
384
|
+
```json
|
|
385
|
+
{ "name": "users_user_id_index", "type": "index", "columns": [{ "name": "user_id" }] }
|
|
386
|
+
{ "name": "users_email_unique", "type": "unique", "columns": [{ "name": "email" }] }
|
|
387
|
+
{ "name": "users_status_created_at_index", "type": "index", "columns": [{ "name": "status" }, { "name": "created_at" }] }
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
**정렬 순서 및 NULL 순서 지정:**
|
|
391
|
+
|
|
392
|
+
```json
|
|
393
|
+
{
|
|
394
|
+
"name": "users_created_at_index",
|
|
395
|
+
"type": "index",
|
|
396
|
+
"columns": [{ "name": "created_at", "sortOrder": "DESC", "nullsFirst": true }]
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
**인덱스 방식 지정 (using):**
|
|
401
|
+
|
|
402
|
+
```json
|
|
403
|
+
{ "name": "users_tags_index", "type": "index", "columns": [{ "name": "tags" }], "using": "gin" }
|
|
404
|
+
{ "name": "users_content_index", "type": "index", "columns": [{ "name": "content" }], "using": "pgroonga" }
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
- using 옵션: `btree` (기본값), `hash`, `gin`, `gist`, `pgroonga`
|
|
408
|
+
|
|
409
|
+
**Unique 인덱스 NULL 처리:**
|
|
410
|
+
|
|
411
|
+
```json
|
|
412
|
+
{
|
|
413
|
+
"name": "users_email_unique",
|
|
414
|
+
"type": "unique",
|
|
415
|
+
"columns": [{ "name": "email" }],
|
|
416
|
+
"nullsNotDistinct": true
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**벡터 인덱스 (HNSW):** - 권장
|
|
421
|
+
|
|
422
|
+
```json
|
|
423
|
+
{
|
|
424
|
+
"name": "embeddings_hnsw_index",
|
|
425
|
+
"type": "hnsw",
|
|
426
|
+
"columns": [{ "name": "embedding", "vectorOps": "vector_cosine_ops" }],
|
|
427
|
+
"m": 16,
|
|
428
|
+
"efConstruction": 64
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
- `vectorOps`: `vector_cosine_ops` (코사인 거리, 권장), `vector_ip_ops` (내적), `vector_l2_ops` (유클리드 거리)
|
|
433
|
+
- `m`: 각 노드의 최대 연결 수 (기본값: 16, 범위: 2~100)
|
|
434
|
+
- `efConstruction`: 구성 시 탐색 범위 (기본값: 64, 범위: 4~1000)
|
|
435
|
+
|
|
436
|
+
**벡터 인덱스 (IVFFlat):**
|
|
437
|
+
|
|
438
|
+
```json
|
|
439
|
+
{
|
|
440
|
+
"name": "embeddings_ivfflat_index",
|
|
441
|
+
"type": "ivfflat",
|
|
442
|
+
"columns": [{ "name": "embedding", "vectorOps": "vector_cosine_ops" }],
|
|
443
|
+
"lists": 100
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
- `lists`: 클러스터링 리스트 수 (권장값: sqrt(row_count) ~ row_count/1000)
|
|
448
|
+
|
|
449
|
+
## Complete Example
|
|
450
|
+
|
|
451
|
+
```json
|
|
452
|
+
{
|
|
453
|
+
"id": "Product",
|
|
454
|
+
"table": "products",
|
|
455
|
+
"title": "상품",
|
|
456
|
+
"props": [
|
|
457
|
+
{ "name": "id", "type": "integer", "desc": "ID" },
|
|
458
|
+
{
|
|
459
|
+
"name": "created_at",
|
|
460
|
+
"type": "date",
|
|
461
|
+
"desc": "등록일시",
|
|
462
|
+
"dbDefault": "CURRENT_TIMESTAMP"
|
|
463
|
+
},
|
|
464
|
+
{ "name": "name", "type": "string", "desc": "상품명", "length": 255 },
|
|
465
|
+
{
|
|
466
|
+
"name": "price",
|
|
467
|
+
"type": "number",
|
|
468
|
+
"desc": "가격",
|
|
469
|
+
"precision": 10,
|
|
470
|
+
"scale": 2
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"name": "description",
|
|
474
|
+
"type": "string",
|
|
475
|
+
"desc": "설명",
|
|
476
|
+
"nullable": true
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"name": "status",
|
|
480
|
+
"type": "enum",
|
|
481
|
+
"id": "ProductStatus",
|
|
482
|
+
"desc": "상태",
|
|
483
|
+
"dbDefault": "\"active\""
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
"name": "category",
|
|
487
|
+
"type": "relation",
|
|
488
|
+
"with": "Category",
|
|
489
|
+
"relationType": "BelongsToOne",
|
|
490
|
+
"onUpdate": "CASCADE",
|
|
491
|
+
"onDelete": "CASCADE",
|
|
492
|
+
"desc": "카테고리"
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
"name": "tags",
|
|
496
|
+
"type": "relation",
|
|
497
|
+
"with": "Tag",
|
|
498
|
+
"relationType": "ManyToMany",
|
|
499
|
+
"joinTable": "products__tags",
|
|
500
|
+
"onUpdate": "CASCADE",
|
|
501
|
+
"onDelete": "CASCADE",
|
|
502
|
+
"desc": "태그 목록"
|
|
503
|
+
}
|
|
504
|
+
],
|
|
505
|
+
"indexes": [
|
|
506
|
+
{
|
|
507
|
+
"name": "products_category_id_index",
|
|
508
|
+
"type": "index",
|
|
509
|
+
"columns": [{ "name": "category_id" }]
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"name": "products_status_index",
|
|
513
|
+
"type": "index",
|
|
514
|
+
"columns": [{ "name": "status" }]
|
|
515
|
+
}
|
|
516
|
+
],
|
|
517
|
+
"subsets": {
|
|
518
|
+
"A": ["id", "name", "price", "status", "category.id", "category.name"],
|
|
519
|
+
"B": ["id", "name", "price"]
|
|
520
|
+
},
|
|
521
|
+
"enums": {
|
|
522
|
+
"ProductStatus": {
|
|
523
|
+
"active": "판매중",
|
|
524
|
+
"hidden": "숨김",
|
|
525
|
+
"soldout": "품절"
|
|
526
|
+
},
|
|
527
|
+
"ProductOrderBy": {
|
|
528
|
+
"id-desc": "ID최신순",
|
|
529
|
+
"id-asc": "ID오름차순",
|
|
530
|
+
"price-desc": "가격높은순",
|
|
531
|
+
"price-asc": "가격낮은순"
|
|
532
|
+
},
|
|
533
|
+
"ProductSearchField": { "id": "ID", "name": "상품명" }
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
```
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { ExtendedApi } from "../../api/decorators";
|
|
2
|
-
import type { EntityNamesRecord } from "../../entity/entity-manager";
|
|
3
|
-
import type { TemplateOptions } from "../../types/types";
|
|
4
|
-
import { type ApiParam } from "../../types/types";
|
|
5
|
-
import { Template } from "../template";
|
|
6
|
-
export declare class Template__service extends Template {
|
|
7
|
-
constructor();
|
|
8
|
-
getTargetAndPath(names: EntityNamesRecord): {
|
|
9
|
-
target: string;
|
|
10
|
-
path: string;
|
|
11
|
-
};
|
|
12
|
-
render({ namesRecord }: TemplateOptions["service"]): {
|
|
13
|
-
body: string;
|
|
14
|
-
importKeys: string[];
|
|
15
|
-
customHeaders: string[];
|
|
16
|
-
target: string;
|
|
17
|
-
path: string;
|
|
18
|
-
};
|
|
19
|
-
getTypeSource(apis: ExtendedApi[]): {
|
|
20
|
-
lines: string[];
|
|
21
|
-
importKeys: string[];
|
|
22
|
-
};
|
|
23
|
-
renderAxios(api: ExtendedApi, apiBaseUrl: string, typeParamsDef: string, paramsDef: string, returnTypeDef: string, payloadDef: string): string;
|
|
24
|
-
renderAxiosMultipart(api: ExtendedApi, apiBaseUrl: string, typeParamsDef: string, paramsDef: string, returnTypeDef: string, paramsWithoutContext: ApiParam[]): string;
|
|
25
|
-
renderSwr(api: ExtendedApi, apiBaseUrl: string, typeParamsDef: string, paramsDef: string, returnTypeDef: string, payloadDef: string): string;
|
|
26
|
-
renderWindowFetch(api: ExtendedApi, apiBaseUrl: string, typeParamsDef: string, paramsDef: string, payloadDef: string): string;
|
|
27
|
-
renderStream(api: ExtendedApi, apiBaseUrl: string, paramsDefAsObject: string): string;
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=service.template.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"service.template.d.ts","sourceRoot":"","sources":["../../../src/template/implementations/service.template.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAErE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,mBAAmB,CAAC;AAEhE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGvC,qBAAa,iBAAkB,SAAQ,QAAQ;;IAK7C,gBAAgB,CAAC,KAAK,EAAE,iBAAiB;;;;IAOzC,MAAM,CAAC,EAAE,WAAW,EAAE,EAAE,eAAe,CAAC,SAAS,CAAC;;;;;;;IAmClD,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG;QAClC,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB;IAgHD,WAAW,CACT,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM;IA8BpB,oBAAoB,CAClB,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,oBAAoB,EAAE,QAAQ,EAAE;IA2ClC,SAAS,CACP,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM;IAoBpB,iBAAiB,CACf,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE,MAAM,EAClB,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM;IAWpB,YAAY,CAAC,GAAG,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM;CAmB7E"}
|