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.
Files changed (96) hide show
  1. package/dist/ai/providers/rtzr/error.d.ts +1 -1
  2. package/dist/ai/providers/rtzr/error.d.ts.map +1 -1
  3. package/dist/api/config.d.ts +1 -0
  4. package/dist/api/config.d.ts.map +1 -1
  5. package/dist/api/config.js +1 -1
  6. package/dist/api/decorators.d.ts +1 -1
  7. package/dist/api/decorators.d.ts.map +1 -1
  8. package/dist/api/decorators.js +1 -1
  9. package/dist/api/sonamu.d.ts +3 -1
  10. package/dist/api/sonamu.d.ts.map +1 -1
  11. package/dist/api/sonamu.js +51 -40
  12. package/dist/database/base-model.d.ts +16 -6
  13. package/dist/database/base-model.d.ts.map +1 -1
  14. package/dist/database/base-model.js +44 -3
  15. package/dist/database/base-model.types.d.ts +29 -48
  16. package/dist/database/base-model.types.d.ts.map +1 -1
  17. package/dist/database/base-model.types.js +12 -2
  18. package/dist/database/puri.d.ts +2 -1
  19. package/dist/database/puri.d.ts.map +1 -1
  20. package/dist/database/puri.js +2 -1
  21. package/dist/database/puri.types.d.ts +3 -3
  22. package/dist/database/puri.types.d.ts.map +1 -1
  23. package/dist/database/puri.types.js +1 -1
  24. package/dist/entity/entity-manager.d.ts +8 -4
  25. package/dist/entity/entity-manager.d.ts.map +1 -1
  26. package/dist/entity/entity.d.ts +10 -1
  27. package/dist/entity/entity.d.ts.map +1 -1
  28. package/dist/entity/entity.js +84 -39
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +2 -1
  32. package/dist/syncer/checksum.d.ts +8 -3
  33. package/dist/syncer/checksum.d.ts.map +1 -1
  34. package/dist/syncer/checksum.js +17 -9
  35. package/dist/syncer/code-generator.js +7 -2
  36. package/dist/syncer/syncer.d.ts +6 -6
  37. package/dist/syncer/syncer.d.ts.map +1 -1
  38. package/dist/syncer/syncer.js +27 -13
  39. package/dist/tasks/workflow-manager.d.ts +3 -3
  40. package/dist/tasks/workflow-manager.d.ts.map +1 -1
  41. package/dist/tasks/workflow-manager.js +15 -11
  42. package/dist/template/implementations/generated.template.d.ts.map +1 -1
  43. package/dist/template/implementations/generated.template.js +8 -6
  44. package/dist/template/implementations/model.template.js +5 -5
  45. package/dist/template/implementations/services.template.d.ts +17 -0
  46. package/dist/template/implementations/services.template.d.ts.map +1 -0
  47. package/dist/template/implementations/services.template.js +159 -0
  48. package/dist/template/implementations/view_form.template.js +2 -2
  49. package/dist/template/implementations/view_id_async_select.template.js +2 -2
  50. package/dist/template/implementations/view_list.template.js +5 -5
  51. package/dist/types/types.d.ts +43 -25
  52. package/dist/types/types.d.ts.map +1 -1
  53. package/dist/types/types.js +29 -17
  54. package/dist/ui/ai-api.d.ts +2 -0
  55. package/dist/ui/ai-api.d.ts.map +1 -1
  56. package/dist/ui/ai-api.js +43 -49
  57. package/dist/ui/ai-client.d.ts +10 -0
  58. package/dist/ui/ai-client.d.ts.map +1 -1
  59. package/dist/ui/ai-client.js +457 -437
  60. package/dist/ui/api.d.ts.map +1 -1
  61. package/dist/ui/api.js +14 -3
  62. package/dist/ui-web/assets/{index-J9MCfjCd.js → index-DzqUrTB-.js} +56 -59
  63. package/dist/ui-web/index.html +1 -1
  64. package/package.json +12 -8
  65. package/src/api/config.ts +3 -0
  66. package/src/api/decorators.ts +6 -1
  67. package/src/api/sonamu.ts +71 -52
  68. package/src/database/base-model.ts +66 -11
  69. package/src/database/base-model.types.ts +79 -76
  70. package/src/database/puri.ts +5 -1
  71. package/src/database/puri.types.ts +3 -6
  72. package/src/entity/entity.ts +83 -34
  73. package/src/index.ts +1 -0
  74. package/src/shared/app.shared.ts.txt +1 -1
  75. package/src/shared/web.shared.ts.txt +0 -43
  76. package/src/syncer/checksum.ts +31 -9
  77. package/src/syncer/code-generator.ts +8 -1
  78. package/src/syncer/syncer.ts +38 -26
  79. package/src/tasks/workflow-manager.ts +16 -12
  80. package/src/template/implementations/generated.template.ts +17 -3
  81. package/src/template/implementations/model.template.ts +4 -4
  82. package/src/template/implementations/services.template.ts +226 -0
  83. package/src/template/implementations/view_form.template.ts +1 -1
  84. package/src/template/implementations/view_id_async_select.template.ts +1 -1
  85. package/src/template/implementations/view_list.template.ts +4 -4
  86. package/src/types/types.ts +33 -16
  87. package/src/ui/ai-api.ts +61 -60
  88. package/src/ui/ai-client.ts +535 -499
  89. package/src/ui/api.ts +14 -2
  90. package/src/ui/entity.instructions.md +536 -0
  91. package/dist/template/implementations/service.template.d.ts +0 -29
  92. package/dist/template/implementations/service.template.d.ts.map +0 -1
  93. package/dist/template/implementations/service.template.js +0 -202
  94. package/dist/ui-web/assets/provider-utils_false-BKJD46kk.js +0 -1
  95. package/dist/ui-web/assets/provider-utils_false-Bu5lmX18.js +0 -1
  96. 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"}