sonamu 0.7.14 → 0.7.16
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/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +4 -3
- 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/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/types/types.d.ts +41 -11
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +27 -3
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +12 -3
- package/dist/ui-web/assets/{index-J9MCfjCd.js → index-BcbbB-BB.js} +42 -42
- package/dist/ui-web/index.html +1 -1
- package/package.json +3 -3
- package/src/api/sonamu.ts +3 -2
- 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/tasks/workflow-manager.ts +16 -12
- package/src/template/implementations/generated.template.ts +17 -3
- package/src/types/types.ts +31 -2
- package/src/ui/api.ts +11 -2
package/src/entity/entity.ts
CHANGED
|
@@ -14,12 +14,15 @@ import {
|
|
|
14
14
|
isBelongsToOneRelationProp,
|
|
15
15
|
isEnumProp,
|
|
16
16
|
isHasManyRelationProp,
|
|
17
|
+
isInternalSubsetField,
|
|
17
18
|
isManyToManyRelationProp,
|
|
18
19
|
isOneToOneRelationProp,
|
|
19
20
|
isRelationProp,
|
|
21
|
+
isVirtualCodeProp,
|
|
20
22
|
isVirtualProp,
|
|
23
|
+
normalizeSubsetField,
|
|
21
24
|
type RelationProp,
|
|
22
|
-
type
|
|
25
|
+
type SubsetField,
|
|
23
26
|
type SubsetQuery,
|
|
24
27
|
} from "../types/types";
|
|
25
28
|
import { importMembers } from "../utils/esm-utils";
|
|
@@ -50,6 +53,9 @@ export class Entity {
|
|
|
50
53
|
subsets: {
|
|
51
54
|
[key: string]: string[];
|
|
52
55
|
};
|
|
56
|
+
subsetsInternal: {
|
|
57
|
+
[key: string]: string[];
|
|
58
|
+
};
|
|
53
59
|
types: {
|
|
54
60
|
[name: string]: z.ZodTypeAny;
|
|
55
61
|
} = {};
|
|
@@ -98,8 +104,13 @@ export class Entity {
|
|
|
98
104
|
// indexes
|
|
99
105
|
this.indexes = indexes ?? [];
|
|
100
106
|
|
|
101
|
-
// subsets
|
|
102
|
-
this.subsets =
|
|
107
|
+
// subsets: SubsetField[]를 파싱하여 subsets(일반)와 subsetsInternal(internal)로 분리
|
|
108
|
+
this.subsets = {};
|
|
109
|
+
this.subsetsInternal = {};
|
|
110
|
+
for (const [key, fields] of Object.entries(subsets ?? {})) {
|
|
111
|
+
this.subsets[key] = fields.filter((f) => !isInternalSubsetField(f)).map(normalizeSubsetField);
|
|
112
|
+
this.subsetsInternal[key] = fields.filter(isInternalSubsetField).map(normalizeSubsetField);
|
|
113
|
+
}
|
|
103
114
|
|
|
104
115
|
// enums
|
|
105
116
|
this.enumLabels = enums ?? {};
|
|
@@ -117,11 +128,18 @@ export class Entity {
|
|
|
117
128
|
};
|
|
118
129
|
}
|
|
119
130
|
|
|
131
|
+
/**
|
|
132
|
+
* 쿼리용 서브셋 필드를 반환합니다 (subsets + subsetsInternal 합침)
|
|
133
|
+
*/
|
|
134
|
+
getSubsetFieldsForQuery(subsetKey: string): string[] {
|
|
135
|
+
return [...(this.subsets[subsetKey] ?? []), ...(this.subsetsInternal[subsetKey] ?? [])];
|
|
136
|
+
}
|
|
137
|
+
|
|
120
138
|
/**
|
|
121
139
|
* 주어진 이름(subsetKey)의 subset을 실제로 가져오는 Puri 코드 구현체 string을 반환합니다.
|
|
122
140
|
*/
|
|
123
141
|
getPuriSubsetQuery(subsetKey: string): string {
|
|
124
|
-
const subset = this.
|
|
142
|
+
const subset = this.getSubsetFieldsForQuery(subsetKey);
|
|
125
143
|
const subsetQuery = this.resolveSubsetQuery("", subset);
|
|
126
144
|
|
|
127
145
|
const lines: string[] = [];
|
|
@@ -148,7 +166,6 @@ export class Entity {
|
|
|
148
166
|
|
|
149
167
|
// select - 입체적 구조로 생성
|
|
150
168
|
const selectObj = this.buildNestedSelectObject(subsetQuery.select);
|
|
151
|
-
|
|
152
169
|
lines.push(`.select(${this.stringifyNestedSelectObject(selectObj)});`);
|
|
153
170
|
|
|
154
171
|
return lines.join("\n");
|
|
@@ -263,7 +280,7 @@ export class Entity {
|
|
|
263
280
|
}
|
|
264
281
|
|
|
265
282
|
getPuriLoaderQuery(subsetKey: string): string {
|
|
266
|
-
const subset = this.
|
|
283
|
+
const subset = this.getSubsetFieldsForQuery(subsetKey);
|
|
267
284
|
const { loaders } = this.resolveSubsetQuery("", subset);
|
|
268
285
|
|
|
269
286
|
const lines: string[] = [`[`];
|
|
@@ -370,7 +387,7 @@ export class Entity {
|
|
|
370
387
|
subset SELECT/JOIN/LOADER 결과 리턴
|
|
371
388
|
*/
|
|
372
389
|
getSubsetQuery(subsetKey: string): SubsetQuery {
|
|
373
|
-
const subset = this.
|
|
390
|
+
const subset = this.getSubsetFieldsForQuery(subsetKey);
|
|
374
391
|
|
|
375
392
|
const result: SubsetQuery = this.resolveSubsetQuery("", subset);
|
|
376
393
|
return result;
|
|
@@ -404,12 +421,16 @@ export class Entity {
|
|
|
404
421
|
// 현재 테이블 필드셋은 select, virtual에 추가하고 리턴
|
|
405
422
|
if (groupKey === "") {
|
|
406
423
|
const realFields = fields.filter((field) => !isVirtualProp(this.propsDict[field]));
|
|
407
|
-
|
|
424
|
+
// virtualType: "code" (또는 undefined)인 virtual prop만 r.virtual에 추가
|
|
425
|
+
// virtualType: "query"인 경우 사용자가 appendSelect로 직접 추가하므로 제외
|
|
426
|
+
const virtualCodeFields = fields.filter((field) =>
|
|
427
|
+
isVirtualCodeProp(this.propsDict[field]),
|
|
428
|
+
);
|
|
408
429
|
|
|
409
430
|
if (prefix === "") {
|
|
410
431
|
// 현재 테이블인 경우
|
|
411
432
|
r.select = r.select.concat(realFields.map((field) => `${this.table}.${field}`));
|
|
412
|
-
r.virtual = r.virtual.concat(
|
|
433
|
+
r.virtual = r.virtual.concat(virtualCodeFields);
|
|
413
434
|
} else {
|
|
414
435
|
// 넘어온 테이블인 경우
|
|
415
436
|
r.select = r.select.concat(
|
|
@@ -605,25 +626,12 @@ export class Entity {
|
|
|
605
626
|
},
|
|
606
627
|
);
|
|
607
628
|
|
|
608
|
-
return Object.keys(groups).flatMap((key) => {
|
|
629
|
+
return Object.keys(groups).flatMap<EntityPropNode, EntityPropNode[]>((key) => {
|
|
609
630
|
const group = groups[key];
|
|
610
631
|
|
|
611
632
|
// 일반 prop 처리
|
|
612
633
|
if (key === "") {
|
|
613
634
|
return group.map((propName) => {
|
|
614
|
-
// FIXME: 이거 나중에 없애야함
|
|
615
|
-
if (propName === "말도안되는프롭명__이거왜타입처리가꼬여서이러지?") {
|
|
616
|
-
return {
|
|
617
|
-
nodeType: "plain" as const,
|
|
618
|
-
prop: {
|
|
619
|
-
type: "string",
|
|
620
|
-
name: "uuid",
|
|
621
|
-
length: 128,
|
|
622
|
-
} as StringProp,
|
|
623
|
-
children: [],
|
|
624
|
-
} as EntityPropNode;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
635
|
const prop = entity.props.find((p) => p.name === propName);
|
|
628
636
|
if (prop === undefined) {
|
|
629
637
|
console.log({ propName, groups });
|
|
@@ -632,7 +640,6 @@ export class Entity {
|
|
|
632
640
|
return {
|
|
633
641
|
nodeType: "plain" as const,
|
|
634
642
|
prop,
|
|
635
|
-
children: [],
|
|
636
643
|
};
|
|
637
644
|
});
|
|
638
645
|
}
|
|
@@ -656,7 +663,6 @@ export class Entity {
|
|
|
656
663
|
name: `${key}_id`,
|
|
657
664
|
nullable: prop.nullable,
|
|
658
665
|
},
|
|
659
|
-
children: [],
|
|
660
666
|
};
|
|
661
667
|
}
|
|
662
668
|
}
|
|
@@ -671,9 +677,9 @@ export class Entity {
|
|
|
671
677
|
: ("array" as const);
|
|
672
678
|
|
|
673
679
|
return {
|
|
680
|
+
nodeType,
|
|
674
681
|
prop,
|
|
675
682
|
children,
|
|
676
|
-
nodeType,
|
|
677
683
|
};
|
|
678
684
|
});
|
|
679
685
|
}
|
|
@@ -793,6 +799,17 @@ export class Entity {
|
|
|
793
799
|
}
|
|
794
800
|
|
|
795
801
|
toJson(): EntityJson {
|
|
802
|
+
// subsets와 subsetsInternal을 SubsetField[] 형태로 복원
|
|
803
|
+
const subsets: { [key: string]: SubsetField[] } = {};
|
|
804
|
+
for (const key of Object.keys(this.subsets)) {
|
|
805
|
+
const normalFields: SubsetField[] = this.subsets[key];
|
|
806
|
+
const internalFields: SubsetField[] = (this.subsetsInternal[key] ?? []).map((field) => ({
|
|
807
|
+
field,
|
|
808
|
+
internal: true,
|
|
809
|
+
}));
|
|
810
|
+
subsets[key] = [...normalFields, ...internalFields];
|
|
811
|
+
}
|
|
812
|
+
|
|
796
813
|
return {
|
|
797
814
|
id: this.id,
|
|
798
815
|
parentId: this.parentId,
|
|
@@ -800,7 +817,7 @@ export class Entity {
|
|
|
800
817
|
title: this.title,
|
|
801
818
|
props: this.props,
|
|
802
819
|
indexes: this.indexes,
|
|
803
|
-
subsets
|
|
820
|
+
subsets,
|
|
804
821
|
enums: this.enumLabels,
|
|
805
822
|
};
|
|
806
823
|
}
|
|
@@ -810,7 +827,12 @@ export class Entity {
|
|
|
810
827
|
const subsetRows = this.getSubsetRows();
|
|
811
828
|
this.subsets = Object.fromEntries(
|
|
812
829
|
Object.entries(this.subsets).map(([subsetKey]) => {
|
|
813
|
-
return [subsetKey, this.subsetRowsToSubsetFields(subsetRows, subsetKey)];
|
|
830
|
+
return [subsetKey, this.subsetRowsToSubsetFields(subsetRows, subsetKey, false)];
|
|
831
|
+
}),
|
|
832
|
+
);
|
|
833
|
+
this.subsetsInternal = Object.fromEntries(
|
|
834
|
+
Object.entries(this.subsetsInternal).map(([subsetKey]) => {
|
|
835
|
+
return [subsetKey, this.subsetRowsToSubsetFields(subsetRows, subsetKey, true)];
|
|
814
836
|
}),
|
|
815
837
|
);
|
|
816
838
|
|
|
@@ -828,6 +850,7 @@ export class Entity {
|
|
|
828
850
|
|
|
829
851
|
getSubsetRows(
|
|
830
852
|
_subsets?: { [key: string]: string[] },
|
|
853
|
+
_subsetsInternal?: { [key: string]: string[] },
|
|
831
854
|
prefixes: string[] = [],
|
|
832
855
|
): EntitySubsetRow[] {
|
|
833
856
|
if (prefixes.length > 10) {
|
|
@@ -835,16 +858,23 @@ export class Entity {
|
|
|
835
858
|
}
|
|
836
859
|
|
|
837
860
|
const subsets = _subsets ?? this.subsets;
|
|
861
|
+
const subsetsInternal = _subsetsInternal ?? this.subsetsInternal;
|
|
838
862
|
const subsetKeys = Object.keys(subsets);
|
|
839
863
|
const allFields = unique(subsetKeys.flatMap((key) => subsets[key]));
|
|
864
|
+
// internal 필드도 allFields에 포함 (relation 탐색용)
|
|
865
|
+
const allInternalFields = unique(subsetKeys.flatMap((key) => subsetsInternal[key] ?? []));
|
|
866
|
+
const combinedFields = unique([...allFields, ...allInternalFields]);
|
|
840
867
|
|
|
841
868
|
return this.props.map((prop) => {
|
|
842
869
|
if (
|
|
843
870
|
prop.type === "relation" &&
|
|
844
|
-
|
|
871
|
+
combinedFields.find((f) => f.startsWith(`${[...prefixes, prop.name].join(".")}.`))
|
|
845
872
|
) {
|
|
846
873
|
const relEntity = EntityManager.get(prop.with);
|
|
847
|
-
const children = relEntity.getSubsetRows(subsets,
|
|
874
|
+
const children = relEntity.getSubsetRows(subsets, subsetsInternal, [
|
|
875
|
+
...prefixes,
|
|
876
|
+
`${prop.name}`,
|
|
877
|
+
]);
|
|
848
878
|
|
|
849
879
|
return {
|
|
850
880
|
field: prop.name,
|
|
@@ -857,9 +887,15 @@ export class Entity {
|
|
|
857
887
|
return [subsetKey, children.every((child) => child.has[subsetKey] === true)];
|
|
858
888
|
}),
|
|
859
889
|
),
|
|
890
|
+
isInternal: Object.fromEntries(
|
|
891
|
+
subsetKeys.map((subsetKey) => {
|
|
892
|
+
return [subsetKey, children.every((child) => child.isInternal[subsetKey] === true)];
|
|
893
|
+
}),
|
|
894
|
+
),
|
|
860
895
|
};
|
|
861
896
|
}
|
|
862
897
|
|
|
898
|
+
const field = [...prefixes, prop.name].join(".");
|
|
863
899
|
return {
|
|
864
900
|
field: prop.name,
|
|
865
901
|
children: [],
|
|
@@ -869,22 +905,35 @@ export class Entity {
|
|
|
869
905
|
subsetKeys.map((subsetKey) => {
|
|
870
906
|
const subsetFields = subsets[subsetKey];
|
|
871
907
|
const has = subsetFields.some((f) => {
|
|
872
|
-
const field = [...prefixes, prop.name].join(".");
|
|
873
908
|
return f === field || f.startsWith(`${field}.`);
|
|
874
909
|
});
|
|
875
910
|
return [subsetKey, has];
|
|
876
911
|
}),
|
|
877
912
|
),
|
|
913
|
+
isInternal: Object.fromEntries(
|
|
914
|
+
subsetKeys.map((subsetKey) => {
|
|
915
|
+
const internalFields = subsetsInternal[subsetKey] ?? [];
|
|
916
|
+
const isInternal = internalFields.some((f) => {
|
|
917
|
+
return f === field || f.startsWith(`${field}.`);
|
|
918
|
+
});
|
|
919
|
+
return [subsetKey, isInternal];
|
|
920
|
+
}),
|
|
921
|
+
),
|
|
878
922
|
};
|
|
879
923
|
});
|
|
880
924
|
}
|
|
881
925
|
|
|
882
|
-
subsetRowsToSubsetFields(
|
|
926
|
+
subsetRowsToSubsetFields(
|
|
927
|
+
subsetRows: EntitySubsetRow[],
|
|
928
|
+
subsetKey: string,
|
|
929
|
+
internal: boolean = false,
|
|
930
|
+
): string[] {
|
|
931
|
+
const hasKey = internal ? "isInternal" : "has";
|
|
883
932
|
return subsetRows
|
|
884
933
|
.map((subsetRow) => {
|
|
885
934
|
if (subsetRow.children.length > 0) {
|
|
886
|
-
return this.subsetRowsToSubsetFields(subsetRow.children, subsetKey);
|
|
887
|
-
} else if (subsetRow
|
|
935
|
+
return this.subsetRowsToSubsetFields(subsetRow.children, subsetKey, internal);
|
|
936
|
+
} else if (subsetRow[hasKey][subsetKey]) {
|
|
888
937
|
return subsetRow.prefixes.concat(subsetRow.field).join(".");
|
|
889
938
|
} else {
|
|
890
939
|
return null;
|
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type * from "./api/context";
|
|
|
4
4
|
export * from "./api/decorators";
|
|
5
5
|
export * from "./api/sonamu";
|
|
6
6
|
export * from "./database/base-model";
|
|
7
|
+
export * from "./database/base-model.types";
|
|
7
8
|
export * from "./database/db";
|
|
8
9
|
export * from "./database/puri";
|
|
9
10
|
export * from "./database/puri.types";
|
|
@@ -77,10 +77,14 @@ export class WorkflowManager {
|
|
|
77
77
|
}
|
|
78
78
|
>;
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
// BackendPostgres에서 처리하는 것들이 있어서 Knex 커넥션이 아니라 설정값을 넣어줘야함.
|
|
81
|
+
constructor(dbConf: Knex.Config, runMigrations: boolean = true) {
|
|
82
|
+
const backend = new BackendPostgres(dbConf, { runMigrations });
|
|
83
|
+
|
|
81
84
|
this.#backend = backend;
|
|
82
85
|
this.#ow = new OpenWorkflow({ backend });
|
|
83
86
|
this.#worker = null;
|
|
87
|
+
|
|
84
88
|
this.#workflowsMap = new Map();
|
|
85
89
|
this.#scheduledTasks = new Map();
|
|
86
90
|
}
|
|
@@ -283,9 +287,18 @@ export class WorkflowManager {
|
|
|
283
287
|
}
|
|
284
288
|
|
|
285
289
|
// Worker를 설정 후 시작
|
|
286
|
-
|
|
290
|
+
setupWorker(options: WorkflowOptions) {
|
|
287
291
|
this.#worker = this.#ow.newWorker(options);
|
|
288
|
-
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Worker를 초기화
|
|
295
|
+
async startWorker() {
|
|
296
|
+
if (!this.#worker) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
await this.#backend.initialize();
|
|
301
|
+
await this.#worker?.start();
|
|
289
302
|
}
|
|
290
303
|
|
|
291
304
|
// Worker를 중지
|
|
@@ -318,13 +331,4 @@ export class WorkflowManager {
|
|
|
318
331
|
[Symbol.asyncDispose]() {
|
|
319
332
|
return this.destroy();
|
|
320
333
|
}
|
|
321
|
-
|
|
322
|
-
// BackendPostgres에서 처리하는 것들이 있어서 Knex 커넥션이 아니라 설정값을 넣어줘야함.
|
|
323
|
-
static async create(
|
|
324
|
-
dbConf: Knex.Config,
|
|
325
|
-
runMigrations: boolean = true,
|
|
326
|
-
): Promise<WorkflowManager> {
|
|
327
|
-
const backend = await BackendPostgres.connect(dbConf, { runMigrations });
|
|
328
|
-
return new WorkflowManager(backend);
|
|
329
|
-
}
|
|
330
334
|
}
|
|
@@ -4,7 +4,12 @@ import { Sonamu } from "../../api";
|
|
|
4
4
|
import type { Entity } from "../../entity/entity";
|
|
5
5
|
import { EntityManager } from "../../entity/entity-manager";
|
|
6
6
|
import { Naite } from "../../naite/naite";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
type EntityIndex,
|
|
9
|
+
type EntityPropNode,
|
|
10
|
+
isVirtualCodeProp,
|
|
11
|
+
isVirtualQueryProp,
|
|
12
|
+
} from "../../types/types";
|
|
8
13
|
import { nonNullable } from "../../utils/utils";
|
|
9
14
|
import { Template } from "../template";
|
|
10
15
|
import { propNodeToZodTypeDef, zodTypeToZodCode } from "../zod-converter";
|
|
@@ -171,9 +176,14 @@ export class Template__generated extends Template {
|
|
|
171
176
|
// TODO: GIN/GiST 인덱스 생성된 컬럼 추출
|
|
172
177
|
const fulltextColumns: EntityIndex["columns"][] = [];
|
|
173
178
|
|
|
174
|
-
// virtual props
|
|
179
|
+
// virtual props (virtualType: "code" 또는 undefined인 것만 포함)
|
|
175
180
|
const virtualProps = entity.props
|
|
176
|
-
.filter((prop) =>
|
|
181
|
+
.filter((prop) => isVirtualCodeProp(prop))
|
|
182
|
+
.map((prop) => prop.name);
|
|
183
|
+
|
|
184
|
+
// query virtual props (virtualType: "query"인 것만 포함)
|
|
185
|
+
const virtualQueryProps = entity.props
|
|
186
|
+
.filter((prop) => isVirtualQueryProp(prop))
|
|
177
187
|
.map((prop) => prop.name);
|
|
178
188
|
|
|
179
189
|
/**
|
|
@@ -209,6 +219,7 @@ export class Template__generated extends Template {
|
|
|
209
219
|
const hasMetadata =
|
|
210
220
|
fulltextColumns.length > 0 ||
|
|
211
221
|
virtualProps.length > 0 ||
|
|
222
|
+
virtualQueryProps.length > 0 ||
|
|
212
223
|
hasDefaultColumns.length > 0 ||
|
|
213
224
|
generatedColumns.length > 0 ||
|
|
214
225
|
hasVectorColumns.length > 0;
|
|
@@ -226,6 +237,9 @@ export class Template__generated extends Template {
|
|
|
226
237
|
(virtualProps.length > 0
|
|
227
238
|
? `readonly __virtual__: readonly [${virtualProps.map((prop) => `"${prop}"`).join(", ")}],`
|
|
228
239
|
: "") +
|
|
240
|
+
(virtualQueryProps.length > 0
|
|
241
|
+
? `readonly __virtual_query__: readonly [${virtualQueryProps.map((prop) => `"${prop}"`).join(", ")}],`
|
|
242
|
+
: "") +
|
|
229
243
|
(hasDefaultColumns.length > 0
|
|
230
244
|
? `readonly __hasDefault__: readonly [${hasDefaultColumns
|
|
231
245
|
.map((col) => `"${col}"`)
|
package/src/types/types.ts
CHANGED
|
@@ -104,6 +104,7 @@ export type UuidArrayProp = CommonProp & {
|
|
|
104
104
|
export type VirtualProp = CommonProp & {
|
|
105
105
|
type: "virtual";
|
|
106
106
|
id: string;
|
|
107
|
+
virtualType?: "query" | "code"; // default: "code"
|
|
107
108
|
}; // PG: none / TS: any(id) / JSON: any
|
|
108
109
|
export type VectorProp = CommonProp & {
|
|
109
110
|
type: "vector";
|
|
@@ -264,6 +265,17 @@ export type EntityIndex = {
|
|
|
264
265
|
*/
|
|
265
266
|
lists?: number;
|
|
266
267
|
};
|
|
268
|
+
|
|
269
|
+
// SubsetField 타입: string 또는 internal 옵션이 있는 객체
|
|
270
|
+
export type SubsetField = string | { field: string; internal?: boolean };
|
|
271
|
+
|
|
272
|
+
export function normalizeSubsetField(f: SubsetField): string {
|
|
273
|
+
return typeof f === "string" ? f : f.field;
|
|
274
|
+
}
|
|
275
|
+
export function isInternalSubsetField(f: SubsetField): boolean {
|
|
276
|
+
return typeof f !== "string" && f.internal === true;
|
|
277
|
+
}
|
|
278
|
+
|
|
267
279
|
export type EntityJson = {
|
|
268
280
|
id: string;
|
|
269
281
|
parentId?: string;
|
|
@@ -272,7 +284,7 @@ export type EntityJson = {
|
|
|
272
284
|
props: EntityProp[];
|
|
273
285
|
indexes: EntityIndex[];
|
|
274
286
|
subsets: {
|
|
275
|
-
[subset: string]:
|
|
287
|
+
[subset: string]: SubsetField[];
|
|
276
288
|
};
|
|
277
289
|
enums: {
|
|
278
290
|
[enumId: string]: {
|
|
@@ -285,6 +297,9 @@ export type EntitySubsetRow = {
|
|
|
285
297
|
has: {
|
|
286
298
|
[key: string]: boolean;
|
|
287
299
|
};
|
|
300
|
+
isInternal: {
|
|
301
|
+
[key: string]: boolean;
|
|
302
|
+
};
|
|
288
303
|
children: EntitySubsetRow[];
|
|
289
304
|
prefixes: string[];
|
|
290
305
|
relationEntity?: string;
|
|
@@ -413,6 +428,14 @@ export function isJsonProp(p: unknown): p is JsonProp {
|
|
|
413
428
|
export function isVirtualProp(p: unknown): p is VirtualProp {
|
|
414
429
|
return (p as VirtualProp)?.type === "virtual";
|
|
415
430
|
}
|
|
431
|
+
export function isVirtualCodeProp(p: unknown): p is VirtualProp {
|
|
432
|
+
if (!isVirtualProp(p)) return false;
|
|
433
|
+
return p.virtualType !== "query"; // undefined도 "code"로 취급
|
|
434
|
+
}
|
|
435
|
+
export function isVirtualQueryProp(p: unknown): p is VirtualProp {
|
|
436
|
+
if (!isVirtualProp(p)) return false;
|
|
437
|
+
return p.virtualType === "query";
|
|
438
|
+
}
|
|
416
439
|
export function isVectorSingleProp(p: unknown): p is VectorProp {
|
|
417
440
|
return (p as VectorProp)?.type === "vector";
|
|
418
441
|
}
|
|
@@ -951,6 +974,7 @@ const VirtualPropSchema = z
|
|
|
951
974
|
...BasePropFields,
|
|
952
975
|
type: z.literal("virtual"),
|
|
953
976
|
id: z.string(),
|
|
977
|
+
virtualType: z.enum(["query", "code"]).optional(),
|
|
954
978
|
})
|
|
955
979
|
.strict();
|
|
956
980
|
|
|
@@ -1172,7 +1196,12 @@ export const EntityJsonSchema = z
|
|
|
1172
1196
|
parentId: z.string().optional().describe("부모 Entity ID"),
|
|
1173
1197
|
props: z.array(EntityPropSchema),
|
|
1174
1198
|
indexes: z.array(EntityIndexSchema),
|
|
1175
|
-
subsets: z.record(
|
|
1199
|
+
subsets: z.record(
|
|
1200
|
+
z.string(),
|
|
1201
|
+
z.array(
|
|
1202
|
+
z.union([z.string(), z.object({ field: z.string(), internal: z.boolean().optional() })]),
|
|
1203
|
+
),
|
|
1204
|
+
),
|
|
1176
1205
|
enums: z.record(z.string(), z.record(z.string(), z.string())),
|
|
1177
1206
|
})
|
|
1178
1207
|
.strict();
|
package/src/ui/api.ts
CHANGED
|
@@ -345,15 +345,23 @@ export async function sonamuUIApiPlugin(fastify: FastifyInstance) {
|
|
|
345
345
|
entityId: string;
|
|
346
346
|
subsetKey: string;
|
|
347
347
|
fields: string[];
|
|
348
|
+
fieldsInternal?: string[];
|
|
348
349
|
};
|
|
349
350
|
}>("/api/entity/modifySubset", async (request) => {
|
|
350
351
|
return await waitForHMRCompleted(async () => {
|
|
351
|
-
const { entityId, subsetKey, fields } = request.body;
|
|
352
|
+
const { entityId, subsetKey, fields, fieldsInternal } = request.body;
|
|
352
353
|
const entity = EntityManager.get(entityId);
|
|
353
354
|
entity.subsets[subsetKey] = fields;
|
|
355
|
+
if (fieldsInternal !== undefined) {
|
|
356
|
+
if (fieldsInternal.length > 0) {
|
|
357
|
+
entity.subsetsInternal[subsetKey] = fieldsInternal;
|
|
358
|
+
} else {
|
|
359
|
+
delete entity.subsetsInternal[subsetKey];
|
|
360
|
+
}
|
|
361
|
+
}
|
|
354
362
|
await entity.save();
|
|
355
363
|
|
|
356
|
-
return { updated: fields };
|
|
364
|
+
return { updated: fields, updatedInternal: fieldsInternal };
|
|
357
365
|
});
|
|
358
366
|
});
|
|
359
367
|
|
|
@@ -367,6 +375,7 @@ export async function sonamuUIApiPlugin(fastify: FastifyInstance) {
|
|
|
367
375
|
const { entityId, subsetKey } = request.body;
|
|
368
376
|
const entity = EntityManager.get(entityId);
|
|
369
377
|
delete entity.subsets[subsetKey];
|
|
378
|
+
delete entity.subsetsInternal[subsetKey];
|
|
370
379
|
await entity.save();
|
|
371
380
|
|
|
372
381
|
return 1;
|