sonamu 0.7.3 → 0.7.5
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/api/config.d.ts +1 -4
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/sonamu.d.ts +2 -0
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +19 -47
- package/dist/bin/cli.js +6 -6
- package/dist/database/base-model.d.ts +1 -1
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +15 -4
- package/dist/database/code-generator.d.ts.map +1 -1
- package/dist/database/code-generator.js +3 -3
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +1 -1
- package/dist/database/puri-wrapper.d.ts +11 -11
- package/dist/database/puri-wrapper.d.ts.map +1 -1
- package/dist/database/puri-wrapper.js +7 -11
- package/dist/database/puri.d.ts +36 -17
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +54 -7
- package/dist/database/puri.types.d.ts +54 -17
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +2 -4
- package/dist/database/puri.types.test-d.js +129 -0
- package/dist/database/upsert-builder.d.ts +16 -10
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +10 -19
- package/dist/entity/entity-manager.d.ts +113 -22
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +1 -1
- package/dist/entity/entity.d.ts +34 -0
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +110 -37
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +341 -149
- package/dist/migration/migration-set.d.ts.map +1 -1
- package/dist/migration/migration-set.js +21 -5
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +7 -1
- package/dist/migration/postgresql-schema-reader.d.ts +11 -1
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -1
- package/dist/migration/postgresql-schema-reader.js +111 -10
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +5 -4
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +12 -2
- package/dist/template/implementations/generated_sso.template.d.ts +3 -3
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.js +50 -2
- package/dist/template/implementations/model.template.js +6 -6
- package/dist/template/implementations/model_test.template.js +4 -4
- package/dist/template/implementations/view_enums_dropdown.template.js +2 -2
- package/dist/template/implementations/view_enums_select.template.js +2 -2
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.js +12 -9
- package/dist/template/implementations/view_id_async_select.template.js +4 -4
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.js +12 -9
- package/dist/template/implementations/view_search_input.template.js +2 -2
- package/dist/template/template.js +2 -2
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +17 -2
- package/dist/testing/fixture-manager.d.ts +2 -1
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +29 -29
- package/dist/types/types.d.ts +593 -68
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +113 -9
- package/dist/vector/chunking.d.ts +25 -0
- package/dist/vector/chunking.d.ts.map +1 -0
- package/dist/vector/chunking.js +97 -0
- package/dist/vector/config.d.ts +12 -0
- package/dist/vector/config.d.ts.map +1 -0
- package/dist/vector/config.js +83 -0
- package/dist/vector/embedding.d.ts +42 -0
- package/dist/vector/embedding.d.ts.map +1 -0
- package/dist/vector/embedding.js +147 -0
- package/dist/vector/types.d.ts +105 -0
- package/dist/vector/types.d.ts.map +1 -0
- package/dist/vector/types.js +5 -0
- package/dist/vector/vector-search.d.ts +47 -0
- package/dist/vector/vector-search.d.ts.map +1 -0
- package/dist/vector/vector-search.js +176 -0
- package/package.json +11 -11
- package/src/api/config.ts +0 -4
- package/src/api/sonamu.ts +21 -36
- package/src/bin/cli.ts +5 -5
- package/src/database/base-model.ts +20 -11
- package/src/database/code-generator.ts +6 -2
- package/src/database/db.ts +1 -0
- package/src/database/puri-wrapper.ts +22 -16
- package/src/database/puri.ts +150 -27
- package/src/database/puri.types.test-d.ts +457 -0
- package/src/database/puri.types.ts +231 -33
- package/src/database/upsert-builder.ts +43 -34
- package/src/entity/entity-manager.ts +2 -2
- package/src/entity/entity.ts +134 -44
- package/src/index.ts +6 -0
- package/src/migration/code-generation.ts +377 -174
- package/src/migration/migration-set.ts +22 -3
- package/src/migration/migrator.ts +6 -0
- package/src/migration/postgresql-schema-reader.ts +121 -21
- package/src/syncer/syncer.ts +4 -3
- package/src/template/implementations/generated.template.ts +51 -9
- package/src/template/implementations/generated_sso.template.ts +71 -2
- package/src/template/implementations/model.template.ts +5 -5
- package/src/template/implementations/model_test.template.ts +3 -3
- package/src/template/implementations/view_enums_dropdown.template.ts +1 -1
- package/src/template/implementations/view_enums_select.template.ts +1 -1
- package/src/template/implementations/view_form.template.ts +11 -8
- package/src/template/implementations/view_id_async_select.template.ts +3 -3
- package/src/template/implementations/view_list.template.ts +11 -8
- package/src/template/implementations/view_search_input.template.ts +1 -1
- package/src/template/template.ts +1 -1
- package/src/template/zod-converter.ts +20 -0
- package/src/testing/fixture-manager.ts +31 -30
- package/src/types/types.ts +226 -48
- package/src/vector/chunking.ts +115 -0
- package/src/vector/config.ts +68 -0
- package/src/vector/embedding.ts +193 -0
- package/src/vector/types.ts +122 -0
- package/src/vector/vector-search.ts +261 -0
- package/dist/template/implementations/view_enums_buttonset.template.d.ts +0 -17
- package/dist/template/implementations/view_enums_buttonset.template.d.ts.map +0 -1
- package/dist/template/implementations/view_enums_buttonset.template.js +0 -31
- package/dist/template/implementations/view_list_columns.template.d.ts +0 -17
- package/dist/template/implementations/view_list_columns.template.d.ts.map +0 -1
- package/dist/template/implementations/view_list_columns.template.js +0 -49
- package/src/template/implementations/view_enums_buttonset.template.ts +0 -34
- package/src/template/implementations/view_list_columns.template.ts +0 -53
package/src/entity/entity.ts
CHANGED
|
@@ -117,9 +117,9 @@ export class Entity {
|
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
/**
|
|
121
|
+
* 주어진 이름(subsetKey)의 subset을 실제로 가져오는 Puri 코드 구현체 string을 반환합니다.
|
|
122
|
+
*/
|
|
123
123
|
getPuriSubsetQuery(subsetKey: string): string {
|
|
124
124
|
const subset = this.subsets[subsetKey];
|
|
125
125
|
const subsetQuery = this.resolveSubsetQuery("", subset);
|
|
@@ -132,6 +132,8 @@ export class Entity {
|
|
|
132
132
|
|
|
133
133
|
// join
|
|
134
134
|
for (const join of subsetQuery.joins) {
|
|
135
|
+
// join 메서드 결정: inner → join, outer → leftJoin
|
|
136
|
+
// FK nullable 여부는 leftJoin 타입 시그니처에서 자동으로 판단됨
|
|
135
137
|
const joinMethod = join.join === "inner" ? "join" : "leftJoin";
|
|
136
138
|
|
|
137
139
|
if ("custom" in join) {
|
|
@@ -144,25 +146,120 @@ export class Entity {
|
|
|
144
146
|
}
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
// select
|
|
148
|
-
const selectObj
|
|
149
|
-
|
|
150
|
-
|
|
149
|
+
// select - 입체적 구조로 생성
|
|
150
|
+
const selectObj = this.buildNestedSelectObject(subsetQuery.select);
|
|
151
|
+
|
|
152
|
+
lines.push(`.select(${this.stringifyNestedSelectObject(selectObj)});`);
|
|
153
|
+
|
|
154
|
+
return lines.join("\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* *.entity.json의 subset에 들어있는 필드 배열을 받아서,
|
|
159
|
+
* Puri의 SelectObject 타입으로 변환합니다.
|
|
160
|
+
*
|
|
161
|
+
* 예: ["users.id", "parent.id", "parent.name"]
|
|
162
|
+
* → { id: "users.id", parent: { id: "parent.id", name: "parent.name" } }
|
|
163
|
+
*
|
|
164
|
+
* 언더바가 아닌 중첩 객체로 변환함에 유의하세요.
|
|
165
|
+
* 이렇게 중첩 객체로 변환하여 select에 넘겨주면 ParseSelectObject 타입이 join된 객체의 타입을 잘 잡아줄 수 있습니다.
|
|
166
|
+
* 즉, enhancer에서 row를 받았을 때 hydrate된 객체 자체의 nullity와 그 안쪽 필드의 nullity가 fk nullable 여부에 따라 잘 추론됩니다.
|
|
167
|
+
*/
|
|
168
|
+
private buildNestedSelectObject(
|
|
169
|
+
selectItems: string[],
|
|
170
|
+
// biome-ignore lint/suspicious/noExplicitAny: 반환 오브젝트의 값은 string일 수도 있고 또다른 오브젝트일 수도 있는데, 이를 재귀 타입으로 나타낼 수 없어 any로 처리합니다.
|
|
171
|
+
): Record<string, any> {
|
|
172
|
+
const result: ReturnType<typeof this.buildNestedSelectObject> = {};
|
|
173
|
+
|
|
174
|
+
for (const selectItem of selectItems) {
|
|
175
|
+
// "users.id" 또는 "users.id as user__id" 형태 파싱
|
|
151
176
|
const match = selectItem.match(/^(.+?)(?: as (.+))?$/);
|
|
152
|
-
if (match)
|
|
153
|
-
|
|
177
|
+
if (!match) continue;
|
|
178
|
+
|
|
179
|
+
const [, column, alias] = match;
|
|
180
|
+
const columnValue = `"${column.trim()}"`;
|
|
181
|
+
|
|
182
|
+
if (!alias || !alias.includes("__")) {
|
|
183
|
+
// alias가 없거나 __를 포함하지 않으면 최상위 필드
|
|
154
184
|
const key = alias ?? assertDefined(column.split(".").pop());
|
|
155
|
-
|
|
185
|
+
result[key] = columnValue;
|
|
186
|
+
} else {
|
|
187
|
+
// alias가 __를 포함하면 입체 구조로 그룹화
|
|
188
|
+
const parts = alias.split("__");
|
|
189
|
+
let current = result;
|
|
190
|
+
|
|
191
|
+
// 마지막 파트 전까지 중첩 객체 생성
|
|
192
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
193
|
+
const part = parts[i];
|
|
194
|
+
if (part in current) {
|
|
195
|
+
if (typeof current[part] === "string") {
|
|
196
|
+
// 입력이 ["user", "user__id"] 같은 경우!
|
|
197
|
+
// 애초에 말도 안 되지만 안전하게 예외를 던집니다.
|
|
198
|
+
throw new Error(
|
|
199
|
+
`Conflict detected in select items: parent path "${parts.slice(0, i + 1).join("__")}" is already set as a field, cannot nest "${alias}" under it.`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
current[part] = {};
|
|
204
|
+
}
|
|
205
|
+
current = current[part];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 마지막 파트에 값 설정
|
|
209
|
+
const lastPart = parts[parts.length - 1];
|
|
210
|
+
current[lastPart] = columnValue;
|
|
156
211
|
}
|
|
157
212
|
}
|
|
158
213
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* JSON.stringify와 유사한 일을 합니다.
|
|
219
|
+
* 다만 주어진 객체를 JSON이 아닌 TypeScript 객체 리터럴 스트링으로 만들어줍니다.
|
|
220
|
+
* key에 따옴표가 없어요.
|
|
221
|
+
* 출력 예시:
|
|
222
|
+
* ```typescript
|
|
223
|
+
* {
|
|
224
|
+
* id: "users.id",
|
|
225
|
+
* parent: {
|
|
226
|
+
* id: "parent.id",
|
|
227
|
+
* name: "parent.name",
|
|
228
|
+
* },
|
|
229
|
+
* }
|
|
230
|
+
* ```
|
|
231
|
+
* @param obj 변환할 객체
|
|
232
|
+
* @param indent 들여쓰기 레벨
|
|
233
|
+
* @param withBraces true면 중괄호 포함, false면 내용만 반환
|
|
234
|
+
*/
|
|
235
|
+
private stringifyNestedSelectObject(
|
|
236
|
+
// biome-ignore lint/suspicious/noExplicitAny: 중첩 오브젝트의 값은 string일 수도 있고 또다른 오브젝트일 수도 있는데, 이를 재귀 타입으로 나타낼 수 없어 any로 처리합니다.
|
|
237
|
+
obj: Record<string, any>,
|
|
238
|
+
indent: number = 0,
|
|
239
|
+
withBraces: boolean = true,
|
|
240
|
+
): string {
|
|
241
|
+
const spaces = " ".repeat(indent);
|
|
242
|
+
const innerSpaces = " ".repeat(indent + 1);
|
|
243
|
+
|
|
244
|
+
const entries = Object.entries(obj);
|
|
245
|
+
if (entries.length === 0) return withBraces ? "{}" : "";
|
|
246
|
+
|
|
247
|
+
const lines = entries.map(([key, value]) => {
|
|
248
|
+
if (typeof value === "string") {
|
|
249
|
+
// 컬럼 경로 (이미 따옴표 포함)
|
|
250
|
+
return `${innerSpaces}${key}: ${value},`;
|
|
251
|
+
} else {
|
|
252
|
+
// 중첩 객체 (항상 중괄호 포함)
|
|
253
|
+
return `${innerSpaces}${key}: ${this.stringifyNestedSelectObject(value, indent + 1, true)},`;
|
|
254
|
+
}
|
|
162
255
|
});
|
|
163
|
-
lines.push(`});`);
|
|
164
256
|
|
|
165
|
-
|
|
257
|
+
if (withBraces) {
|
|
258
|
+
return `{\n${lines.join("\n")}\n${spaces}}`;
|
|
259
|
+
} else {
|
|
260
|
+
// 중괄호 없이 내용만 반환 (앞뒤 개행 제외)
|
|
261
|
+
return lines.join("\n");
|
|
262
|
+
}
|
|
166
263
|
}
|
|
167
264
|
|
|
168
265
|
getPuriLoaderQuery(subsetKey: string): string {
|
|
@@ -171,19 +268,6 @@ export class Entity {
|
|
|
171
268
|
|
|
172
269
|
const lines: string[] = [`[`];
|
|
173
270
|
|
|
174
|
-
const parseSelect = (select: string, table: string) => {
|
|
175
|
-
const tablePrefix = `${table}.`;
|
|
176
|
-
if (select.startsWith(tablePrefix)) {
|
|
177
|
-
return `${select.replace(tablePrefix, "")}: "${select}"`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (select.includes(" as ")) {
|
|
181
|
-
const [column, alias] = select.split(" as ");
|
|
182
|
-
return `${alias}: "${column}"`;
|
|
183
|
-
}
|
|
184
|
-
return `${select}: "${select}"`;
|
|
185
|
-
};
|
|
186
|
-
|
|
187
271
|
// 재귀적으로 loader 생성하는 헬퍼 함수
|
|
188
272
|
const generateLoaderCode = (loaders: SubsetQuery["loaders"]): string[] => {
|
|
189
273
|
const loaderLines: string[] = [];
|
|
@@ -206,27 +290,28 @@ export class Entity {
|
|
|
206
290
|
);
|
|
207
291
|
|
|
208
292
|
loader.oneJoins.forEach((join: SubsetQuery["joins"][number]) => {
|
|
209
|
-
|
|
293
|
+
// FK nullable 여부는 leftJoin 타입 시그니처에서 자동으로 판단됨
|
|
294
|
+
const joinMethod = join.join === "inner" ? "join" : "leftJoin";
|
|
210
295
|
if ("custom" in join) {
|
|
211
296
|
// FIXME: 검증 필요
|
|
212
297
|
loaderLines.push(
|
|
213
|
-
`.${
|
|
298
|
+
`.${joinMethod}({ ${join.as}: "${join.table}" }, (j) => {`,
|
|
214
299
|
`j.on(Puri.rawString("${join.custom}"));`,
|
|
215
300
|
"})",
|
|
216
301
|
);
|
|
217
302
|
} else {
|
|
218
303
|
loaderLines.push(
|
|
219
|
-
`.${
|
|
304
|
+
`.${joinMethod}({ ${join.as}: "${join.table}" }, "${join.from}", "${join.to}")`,
|
|
220
305
|
);
|
|
221
306
|
}
|
|
222
307
|
});
|
|
223
308
|
|
|
309
|
+
// 입체적 select 구조 생성 (refId 포함)
|
|
310
|
+
const selectObj = this.buildNestedSelectObject(loader.select);
|
|
311
|
+
selectObj.refId = `"${toTable}.${toCol}"`;
|
|
224
312
|
loaderLines.push(
|
|
225
313
|
`.whereIn("${toTable}.${toCol}", fromIds)`,
|
|
226
|
-
`.select({
|
|
227
|
-
`${loader.select.map((select: string) => parseSelect(select, toTable)).join(",")},`,
|
|
228
|
-
`refId: "${toTable}.${toCol}",`,
|
|
229
|
-
`});`,
|
|
314
|
+
`.select(${this.stringifyNestedSelectObject(selectObj)});`,
|
|
230
315
|
);
|
|
231
316
|
} else {
|
|
232
317
|
// ManyToMany
|
|
@@ -237,26 +322,28 @@ export class Entity {
|
|
|
237
322
|
);
|
|
238
323
|
|
|
239
324
|
loader.oneJoins.forEach((join: SubsetQuery["joins"][number]) => {
|
|
240
|
-
|
|
325
|
+
// FK nullable 여부는 leftJoin 타입 시그니처에서 자동으로 판단됨
|
|
326
|
+
const joinMethod = join.join === "inner" ? "join" : "leftJoin";
|
|
241
327
|
if ("custom" in join) {
|
|
242
328
|
// FIXME: 검증 필요
|
|
243
329
|
loaderLines.push(
|
|
244
|
-
`.${
|
|
330
|
+
`.${joinMethod}({ ${join.as}: "${join.table}" }, (j) => {`,
|
|
245
331
|
`j.on(Puri.rawString("${join.custom}"));`,
|
|
246
332
|
"})",
|
|
247
333
|
);
|
|
248
334
|
} else {
|
|
249
335
|
loaderLines.push(
|
|
250
|
-
`.${
|
|
336
|
+
`.${joinMethod}({ ${join.as}: "${join.table}" }, "${join.from}", "${join.to}")`,
|
|
251
337
|
);
|
|
252
338
|
}
|
|
253
339
|
});
|
|
340
|
+
|
|
341
|
+
// 입체적 select 구조 생성 (refId 포함)
|
|
342
|
+
const selectObj = this.buildNestedSelectObject(loader.select);
|
|
343
|
+
selectObj.refId = `"${through.table}.${through.fromCol}"`;
|
|
254
344
|
loaderLines.push(
|
|
255
345
|
`.whereIn("${through.table}.${through.fromCol}", fromIds)`,
|
|
256
|
-
`.select({
|
|
257
|
-
`${loader.select.map((select: string) => parseSelect(select, toTable)).join(",")},`,
|
|
258
|
-
`refId: "${through.table}.${through.fromCol}",`,
|
|
259
|
-
`});`,
|
|
346
|
+
`.select(${this.stringifyNestedSelectObject(selectObj)});`,
|
|
260
347
|
);
|
|
261
348
|
}
|
|
262
349
|
|
|
@@ -675,7 +762,10 @@ export class Entity {
|
|
|
675
762
|
}
|
|
676
763
|
|
|
677
764
|
registerTableSpecs(): void {
|
|
678
|
-
|
|
765
|
+
// 조인 테이블 인덱스 제외 (컬럼 이름에 '.'이 포함된 경우)
|
|
766
|
+
const uniqueIndexes = this.indexes
|
|
767
|
+
.filter((idx) => idx.type === "unique")
|
|
768
|
+
.filter((idx) => idx.columns.every((col) => !col.name.includes(".")));
|
|
679
769
|
|
|
680
770
|
EntityManager.setTableSpec({
|
|
681
771
|
name: this.table,
|
|
@@ -904,7 +994,7 @@ export class Entity {
|
|
|
904
994
|
|
|
905
995
|
// 현재 엔티티의 인덱스에서 제외
|
|
906
996
|
for (const index of EntityManager.get(this.id).indexes) {
|
|
907
|
-
index.columns = index.columns.filter((col) => col !== oldName);
|
|
997
|
+
index.columns = index.columns.filter((col) => col.name !== oldName);
|
|
908
998
|
}
|
|
909
999
|
|
|
910
1000
|
// 프롭 삭제
|
package/src/index.ts
CHANGED
|
@@ -30,6 +30,12 @@ export * from "./utils/controller";
|
|
|
30
30
|
export * from "./utils/model";
|
|
31
31
|
export * from "./utils/type-utils";
|
|
32
32
|
export * from "./utils/utils";
|
|
33
|
+
// Vector (pgvector integration)
|
|
34
|
+
export * from "./vector/chunking";
|
|
35
|
+
export * from "./vector/config";
|
|
36
|
+
export * from "./vector/embedding";
|
|
37
|
+
export * from "./vector/types";
|
|
38
|
+
export * from "./vector/vector-search";
|
|
33
39
|
|
|
34
40
|
// export * from "./api/code-converters";
|
|
35
41
|
// export * from "./syncer/syncer";
|