sonamu 0.7.53 → 0.8.0
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 +9 -1
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/sonamu.d.ts +21 -1
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +159 -65
- package/dist/auth/plugins/entity-definitions/anonymous.d.ts +10 -0
- package/dist/auth/plugins/entity-definitions/anonymous.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/anonymous.js +23 -0
- package/dist/auth/plugins/entity-definitions/api-key.d.ts +9 -0
- package/dist/auth/plugins/entity-definitions/api-key.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/api-key.js +199 -0
- package/dist/auth/plugins/entity-definitions/index.d.ts +6 -0
- package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/index.js +20 -2
- package/dist/auth/plugins/entity-definitions/jwt.d.ts +9 -0
- package/dist/auth/plugins/entity-definitions/jwt.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/jwt.js +67 -0
- package/dist/auth/plugins/entity-definitions/organization.d.ts +9 -0
- package/dist/auth/plugins/entity-definitions/organization.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/organization.js +424 -0
- package/dist/auth/plugins/entity-definitions/passkey.d.ts +10 -0
- package/dist/auth/plugins/entity-definitions/passkey.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/passkey.js +129 -0
- package/dist/auth/plugins/entity-definitions/sso.d.ts +10 -0
- package/dist/auth/plugins/entity-definitions/sso.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/sso.js +110 -0
- package/dist/auth/plugins/entity-definitions/types.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/types.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/types.js +1 -1
- package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/admin.js +2 -4
- package/dist/auth/plugins/wrappers/anonymous.d.ts +18 -0
- package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/anonymous.js +26 -0
- package/dist/auth/plugins/wrappers/api-key.d.ts +18 -0
- package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/api-key.js +38 -0
- package/dist/auth/plugins/wrappers/index.d.ts +6 -0
- package/dist/auth/plugins/wrappers/index.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/index.js +7 -1
- package/dist/auth/plugins/wrappers/jwt.d.ts +18 -0
- package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/jwt.js +30 -0
- package/dist/auth/plugins/wrappers/organization.d.ts +18 -0
- package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/organization.js +67 -0
- package/dist/auth/plugins/wrappers/passkey.d.ts +18 -0
- package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/passkey.js +32 -0
- package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/phone-number.js +2 -4
- package/dist/auth/plugins/wrappers/sso.d.ts +853 -0
- package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/sso.js +36 -0
- package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/two-factor.js +2 -4
- package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/username.js +2 -4
- package/dist/bin/build-config.d.ts +2 -2
- package/dist/bin/build-config.js +6 -7
- package/dist/bin/cli.js +417 -32
- package/dist/bin/fixture.d.ts +27 -0
- package/dist/bin/fixture.d.ts.map +1 -0
- package/dist/bin/fixture.js +245 -0
- package/dist/cache/decorator.d.ts +4 -3
- package/dist/cache/decorator.d.ts.map +1 -1
- package/dist/cache/decorator.js +5 -4
- package/dist/cone/cone-generator.d.ts +33 -0
- package/dist/cone/cone-generator.d.ts.map +1 -0
- package/dist/cone/cone-generator.js +286 -0
- package/dist/database/_batch_update.d.ts.map +1 -1
- package/dist/database/_batch_update.js +16 -2
- package/dist/database/puri-subset.test-d.js +1 -1
- package/dist/database/puri-subset.types.d.ts +1 -1
- package/dist/database/puri-subset.types.d.ts.map +1 -1
- package/dist/database/puri-subset.types.js +1 -1
- package/dist/database/puri.d.ts +4 -0
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +20 -2
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +19 -3
- package/dist/dict/en.d.ts +15 -0
- package/dist/dict/en.d.ts.map +1 -1
- package/dist/dict/en.js +2 -1
- package/dist/dict/ko.d.ts +15 -0
- package/dist/dict/ko.d.ts.map +1 -1
- package/dist/dict/ko.js +2 -1
- package/dist/dict/rc-keys.d.ts +28 -0
- package/dist/dict/rc-keys.d.ts.map +1 -1
- package/dist/dict/rc-keys.js +31 -1
- package/dist/dict/sd.d.ts.map +1 -1
- package/dist/dict/sd.js +20 -4
- package/dist/entity/entity-manager.d.ts +298 -2
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +4 -1
- package/dist/entity/entity-template-cone.d.ts +14 -0
- package/dist/entity/entity-template-cone.d.ts.map +1 -0
- package/dist/entity/entity-template-cone.js +222 -0
- package/dist/entity/entity.d.ts +47 -2
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +161 -14
- package/dist/ssr/renderer.js +3 -3
- package/dist/syncer/api-parser.js +12 -1
- package/dist/syncer/checksum.d.ts +0 -14
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +1 -23
- package/dist/syncer/syncer-actions.d.ts.map +1 -1
- package/dist/syncer/syncer-actions.js +8 -2
- package/dist/syncer/syncer.d.ts +1 -1
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +17 -10
- package/dist/tasks/workflow-manager.d.ts +13 -1
- package/dist/tasks/workflow-manager.d.ts.map +1 -1
- package/dist/tasks/workflow-manager.js +18 -1
- package/dist/template/entity-converter.js +4 -4
- package/dist/template/helpers.d.ts +10 -0
- package/dist/template/helpers.d.ts.map +1 -1
- package/dist/template/helpers.js +48 -1
- package/dist/template/implementations/entry-server.template.d.ts +1 -1
- package/dist/template/implementations/entry-server.template.js +7 -2
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +5 -1
- package/dist/template/implementations/generated_http.template.d.ts +1 -0
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_http.template.js +6 -2
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.js +29 -8
- package/dist/template/implementations/queries.template.d.ts.map +1 -1
- package/dist/template/implementations/queries.template.js +9 -1
- package/dist/template/implementations/sd.template.d.ts +1 -1
- package/dist/template/implementations/sd.template.d.ts.map +1 -1
- package/dist/template/implementations/sd.template.js +28 -4
- package/dist/template/implementations/services.template.d.ts.map +1 -1
- package/dist/template/implementations/services.template.js +12 -12
- package/dist/template/implementations/view_form.template.d.ts +11 -7
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.js +97 -87
- package/dist/template/implementations/view_list.template.d.ts +3 -3
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.js +115 -109
- package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
- package/dist/template/implementations/view_search_input.template.js +18 -14
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +95 -7
- package/dist/testing/_relation-graph.js +1 -1
- package/dist/testing/data-explorer.d.ts +61 -0
- package/dist/testing/data-explorer.d.ts.map +1 -0
- package/dist/testing/data-explorer.js +274 -0
- package/dist/testing/faker-mappings.d.ts +20 -0
- package/dist/testing/faker-mappings.d.ts.map +1 -0
- package/dist/testing/faker-mappings.js +421 -0
- package/dist/testing/fixture-generator.d.ts +161 -0
- package/dist/testing/fixture-generator.d.ts.map +1 -0
- package/dist/testing/fixture-generator.js +954 -0
- package/dist/testing/fixture-manager.d.ts +6 -1
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +72 -4
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +4 -1
- package/dist/types/types.d.ts +1520 -26
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +136 -22
- package/dist/ui/ai-client.d.ts.map +1 -1
- package/dist/ui/ai-client.js +9 -4
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +303 -24
- package/dist/ui-web/assets/index-CsUr-_pV.js +254 -0
- package/dist/ui-web/assets/index-T42zzs1K.css +1 -0
- package/dist/ui-web/index.html +2 -2
- package/dist/utils/fs-utils.d.ts +2 -1
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +14 -3
- package/package.json +19 -11
- package/src/api/config.ts +12 -1
- package/src/api/sonamu.ts +179 -65
- package/src/auth/plugins/entity-definitions/anonymous.ts +17 -0
- package/src/auth/plugins/entity-definitions/api-key.ts +93 -0
- package/src/auth/plugins/entity-definitions/index.ts +18 -0
- package/src/auth/plugins/entity-definitions/jwt.ts +35 -0
- package/src/auth/plugins/entity-definitions/organization.ts +215 -0
- package/src/auth/plugins/entity-definitions/passkey.ts +64 -0
- package/src/auth/plugins/entity-definitions/sso.ts +62 -0
- package/src/auth/plugins/entity-definitions/types.ts +11 -1
- package/src/auth/plugins/wrappers/admin.ts +1 -3
- package/src/auth/plugins/wrappers/anonymous.ts +30 -0
- package/src/auth/plugins/wrappers/api-key.ts +42 -0
- package/src/auth/plugins/wrappers/index.ts +6 -0
- package/src/auth/plugins/wrappers/jwt.ts +34 -0
- package/src/auth/plugins/wrappers/organization.ts +73 -0
- package/src/auth/plugins/wrappers/passkey.ts +36 -0
- package/src/auth/plugins/wrappers/phone-number.ts +1 -3
- package/src/auth/plugins/wrappers/sso.ts +40 -0
- package/src/auth/plugins/wrappers/two-factor.ts +1 -3
- package/src/auth/plugins/wrappers/username.ts +1 -3
- package/src/bin/build-config.ts +6 -6
- package/src/bin/cli.ts +452 -31
- package/src/bin/fixture.ts +302 -0
- package/src/cache/decorator.ts +4 -3
- package/src/cone/cone-generator.ts +363 -0
- package/src/database/_batch_update.ts +11 -0
- package/src/database/puri-subset.test-d.ts +13 -13
- package/src/database/puri-subset.types.ts +1 -1
- package/src/database/puri.ts +43 -1
- package/src/database/upsert-builder.ts +16 -2
- package/src/dict/en.ts +1 -0
- package/src/dict/ko.ts +1 -0
- package/src/dict/rc-keys.ts +32 -0
- package/src/dict/sd.ts +23 -3
- package/src/entity/entity-manager.ts +4 -0
- package/src/entity/entity-template-cone.ts +298 -0
- package/src/entity/entity.ts +189 -13
- package/src/shared/app.shared.ts.txt +5 -0
- package/src/shared/web.shared.ts.txt +9 -5
- package/src/skills/project/README.md +21 -0
- package/src/skills/project/architecture.md +373 -0
- package/src/skills/project/business-logic.md +270 -0
- package/src/skills/project/requirements.md +160 -0
- package/src/skills/sonamu/SKILL.md +168 -3
- package/src/skills/sonamu/api.md +102 -0
- package/src/skills/sonamu/database.md +220 -1
- package/src/skills/sonamu/entity-relations.md +89 -1
- package/src/skills/sonamu/fixture-cli.md +501 -0
- package/src/skills/sonamu/frontend.md +214 -0
- package/src/skills/sonamu/i18n.md +95 -0
- package/src/skills/sonamu/model.md +153 -0
- package/src/skills/sonamu/project-init.md +178 -8
- package/src/skills/sonamu/scaffolding.md +112 -0
- package/src/skills/sonamu/subset.md +9 -3
- package/src/skills/sonamu/testing.md +287 -2
- package/src/skills/sonamu/workflow.md +70 -5
- package/src/ssr/renderer.ts +2 -2
- package/src/syncer/api-parser.ts +12 -0
- package/src/syncer/checksum.ts +0 -38
- package/src/syncer/syncer-actions.ts +7 -1
- package/src/syncer/syncer.ts +16 -5
- package/src/tasks/workflow-manager.ts +29 -8
- package/src/template/entity-converter.ts +3 -3
- package/src/template/helpers.ts +49 -0
- package/src/template/implementations/entry-server.template.ts +1 -1
- package/src/template/implementations/generated.template.ts +4 -0
- package/src/template/implementations/generated_http.template.ts +1 -0
- package/src/template/implementations/generated_sso.template.ts +40 -11
- package/src/template/implementations/queries.template.ts +8 -0
- package/src/template/implementations/sd.template.ts +22 -3
- package/src/template/implementations/services.template.ts +11 -10
- package/src/template/implementations/view_form.template.ts +111 -101
- package/src/template/implementations/view_list.template.ts +120 -119
- package/src/template/implementations/view_search_input.template.ts +17 -13
- package/src/template/zod-converter.ts +103 -6
- package/src/testing/_relation-graph.ts +1 -1
- package/src/testing/data-explorer.ts +427 -0
- package/src/testing/faker-mappings.ts +434 -0
- package/src/testing/fixture-generator.ts +1166 -0
- package/src/testing/fixture-manager.ts +91 -6
- package/src/testing/index.ts +3 -0
- package/src/types/types.ts +222 -26
- package/src/ui/ai-client.ts +9 -1
- package/src/ui/api.ts +429 -23
- package/src/utils/fs-utils.ts +14 -1
- package/dist/template/implementations/view_enums_select.template.d.ts +0 -17
- package/dist/template/implementations/view_enums_select.template.d.ts.map +0 -1
- package/dist/template/implementations/view_enums_select.template.js +0 -62
- package/dist/template/implementations/view_id_async_select.template.d.ts +0 -17
- package/dist/template/implementations/view_id_async_select.template.d.ts.map +0 -1
- package/dist/template/implementations/view_id_async_select.template.js +0 -125
- package/dist/ui-web/assets/index-Bd_2AkLb.css +0 -1
- package/dist/ui-web/assets/index-BpSbhQWo.js +0 -225
- package/src/template/implementations/view_enums_select.template.ts +0 -65
- package/src/template/implementations/view_id_async_select.template.ts +0 -139
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
https://github.com/knex/knex/issues/5716
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { getLogger } from "@logtape/logtape";
|
|
6
7
|
import type { Knex } from "knex";
|
|
7
8
|
|
|
9
|
+
const logger = getLogger(["sonamu", "internal", "batch-update"]);
|
|
10
|
+
|
|
8
11
|
type ColumnValue = string | number | boolean | null;
|
|
9
12
|
export type RowWithId<Id extends string> = {
|
|
10
13
|
[key in Id]: ColumnValue;
|
|
@@ -39,11 +42,19 @@ export async function batchUpdate<Id extends string>(
|
|
|
39
42
|
|
|
40
43
|
if (trx) {
|
|
41
44
|
for (const chunk of chunks) {
|
|
45
|
+
logger.debug("Processing Batch Chunk in Existing Transaction: {current} / {total}", {
|
|
46
|
+
current: chunk.length,
|
|
47
|
+
total: chunks.length,
|
|
48
|
+
});
|
|
42
49
|
await executeUpdate(chunk, trx);
|
|
43
50
|
}
|
|
44
51
|
} else {
|
|
45
52
|
await knex.transaction(async (newTrx) => {
|
|
46
53
|
for (const chunk of chunks) {
|
|
54
|
+
logger.debug("Processing Batch Chunk in New Transaction: {current} / {total}", {
|
|
55
|
+
current: chunk.length,
|
|
56
|
+
total: chunks.length,
|
|
57
|
+
});
|
|
47
58
|
await executeUpdate(chunk, newTrx);
|
|
48
59
|
}
|
|
49
60
|
});
|
|
@@ -109,7 +109,7 @@ describe("LoadersResult", () => {
|
|
|
109
109
|
it("단일 로더의 결과 타입을 생성한다", () => {
|
|
110
110
|
type MockLoaderQb = (
|
|
111
111
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
112
|
-
fromIds: number[],
|
|
112
|
+
fromIds: number[] | string[],
|
|
113
113
|
) => MockPuri<{ id: number; title: string; refId: number }>;
|
|
114
114
|
|
|
115
115
|
type Loaders = [
|
|
@@ -129,12 +129,12 @@ describe("LoadersResult", () => {
|
|
|
129
129
|
it("중첩 로더를 처리한다", () => {
|
|
130
130
|
type CommentLoaderQb = (
|
|
131
131
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
132
|
-
fromIds: number[],
|
|
132
|
+
fromIds: number[] | string[],
|
|
133
133
|
) => MockPuri<{ id: number; content: string; refId: number }>;
|
|
134
134
|
|
|
135
135
|
type PostLoaderQb = (
|
|
136
136
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
137
|
-
fromIds: number[],
|
|
137
|
+
fromIds: number[] | string[],
|
|
138
138
|
) => MockPuri<{ id: number; title: string; refId: number }>;
|
|
139
139
|
|
|
140
140
|
type Loaders = [
|
|
@@ -166,7 +166,7 @@ describe("LoadersResult", () => {
|
|
|
166
166
|
refId: "id";
|
|
167
167
|
qb: (
|
|
168
168
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
169
|
-
fromIds: number[],
|
|
169
|
+
fromIds: number[] | string[],
|
|
170
170
|
) => MockPuri<{ id: number; name: string; refId: number }>;
|
|
171
171
|
};
|
|
172
172
|
type DepartmentLoader = {
|
|
@@ -174,7 +174,7 @@ describe("LoadersResult", () => {
|
|
|
174
174
|
refId: "id";
|
|
175
175
|
qb: (
|
|
176
176
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
177
|
-
fromIds: number[],
|
|
177
|
+
fromIds: number[] | string[],
|
|
178
178
|
) => MockPuri<{ id: number; name: string; company_name: string; refId: number }>;
|
|
179
179
|
};
|
|
180
180
|
type EmployeeLoader = {
|
|
@@ -182,7 +182,7 @@ describe("LoadersResult", () => {
|
|
|
182
182
|
refId: "id";
|
|
183
183
|
qb: (
|
|
184
184
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
185
|
-
fromIds: number[],
|
|
185
|
+
fromIds: number[] | string[],
|
|
186
186
|
) => MockPuri<{ id: number; employee_number: string; refId: number }>;
|
|
187
187
|
loaders: [
|
|
188
188
|
{
|
|
@@ -190,7 +190,7 @@ describe("LoadersResult", () => {
|
|
|
190
190
|
refId: "id";
|
|
191
191
|
qb: (
|
|
192
192
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
193
|
-
fromIds: number[],
|
|
193
|
+
fromIds: number[] | string[],
|
|
194
194
|
) => MockPuri<{ id: number; name: string; email: string; refId: number }>;
|
|
195
195
|
},
|
|
196
196
|
];
|
|
@@ -267,7 +267,7 @@ describe("InferAllSubsets", () => {
|
|
|
267
267
|
refId: "company__id";
|
|
268
268
|
qb: (
|
|
269
269
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
270
|
-
fromIds: number[],
|
|
270
|
+
fromIds: number[] | string[],
|
|
271
271
|
) => MockPuri<{ id: number; name: string; refId: number }>;
|
|
272
272
|
};
|
|
273
273
|
type DepartmentProjectsLoader = {
|
|
@@ -275,7 +275,7 @@ describe("InferAllSubsets", () => {
|
|
|
275
275
|
refId: "department__id";
|
|
276
276
|
qb: (
|
|
277
277
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
278
|
-
fromIds: number[],
|
|
278
|
+
fromIds: number[] | string[],
|
|
279
279
|
) => MockPuri<{ id: number; name: string; status: string; refId: number }>;
|
|
280
280
|
};
|
|
281
281
|
type LoaderQueries = {
|
|
@@ -316,7 +316,7 @@ describe("InferAllSubsets", () => {
|
|
|
316
316
|
refId: "company__id";
|
|
317
317
|
qb: (
|
|
318
318
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
319
|
-
fromIds: number[],
|
|
319
|
+
fromIds: number[] | string[],
|
|
320
320
|
) => MockPuri<{ id: number; name: string; refId: number }>;
|
|
321
321
|
loaders: [
|
|
322
322
|
{
|
|
@@ -324,7 +324,7 @@ describe("InferAllSubsets", () => {
|
|
|
324
324
|
refId: "id";
|
|
325
325
|
qb: (
|
|
326
326
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
327
|
-
fromIds: number[],
|
|
327
|
+
fromIds: number[] | string[],
|
|
328
328
|
) => MockPuri<{ id: number; name: string; status: string; refId: number }>;
|
|
329
329
|
},
|
|
330
330
|
];
|
|
@@ -428,7 +428,7 @@ describe("InferAllSubsets", () => {
|
|
|
428
428
|
refId: "id";
|
|
429
429
|
qb: (
|
|
430
430
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
431
|
-
fromIds: number[],
|
|
431
|
+
fromIds: number[] | string[],
|
|
432
432
|
) => MockPuri<{ id: number; name: string; status: string; refId: number }>;
|
|
433
433
|
loaders: [
|
|
434
434
|
{
|
|
@@ -436,7 +436,7 @@ describe("InferAllSubsets", () => {
|
|
|
436
436
|
refId: "id";
|
|
437
437
|
qb: (
|
|
438
438
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
439
|
-
fromIds: number[],
|
|
439
|
+
fromIds: number[] | string[],
|
|
440
440
|
) => MockPuri<{ id: number; name: string; refId: number }>;
|
|
441
441
|
},
|
|
442
442
|
];
|
|
@@ -41,7 +41,7 @@ export type ExtractPuriResult<T> = T extends Puri<any, any, infer R> ? R : never
|
|
|
41
41
|
*/
|
|
42
42
|
export type PuriLoaderQbFn = (
|
|
43
43
|
qbWrapper: PuriWrapper<DatabaseSchemaExtend>,
|
|
44
|
-
fromIds: number[],
|
|
44
|
+
fromIds: number[] | string[],
|
|
45
45
|
) => Puri<any, any, any>;
|
|
46
46
|
|
|
47
47
|
/**
|
package/src/database/puri.ts
CHANGED
|
@@ -748,9 +748,13 @@ export class Puri<TSchema, TTables extends Record<string, any>, TResult> {
|
|
|
748
748
|
// 기존 ORDER BY clear
|
|
749
749
|
this.knexQuery.clear("order");
|
|
750
750
|
if (distinctOn) {
|
|
751
|
-
// DISTINCT ON은 SELECT 절의 맨 앞에 와야 하므로, 기존 select를 clear하고 다시 추가
|
|
751
|
+
// DISTINCT ON은 SELECT 절의 맨 앞에 와야 하므로, 기존 select(subset 필드들)를 보존 후 clear하고 다시 추가
|
|
752
|
+
const existingSubsetCols = (this.knexQuery as any)._statements
|
|
753
|
+
.filter((s: any) => s.grouping === "columns")
|
|
754
|
+
.flatMap((s: any) => s.value);
|
|
752
755
|
this.knexQuery.clear("select");
|
|
753
756
|
this.knexQuery.select(this.knex.raw(`DISTINCT ON (??) ??`, [distinctOn, distinctOn]));
|
|
757
|
+
existingSubsetCols.map((col: any) => this.knexQuery.select(col));
|
|
754
758
|
this.knexQuery.select(similarityExpr);
|
|
755
759
|
this.knexQuery.orderByRaw(`??, ?? ${operator} ?::vector`, [
|
|
756
760
|
distinctOn,
|
|
@@ -1120,6 +1124,25 @@ export class WhereGroup<TTables extends Record<string, any>> {
|
|
|
1120
1124
|
return this;
|
|
1121
1125
|
}
|
|
1122
1126
|
|
|
1127
|
+
// whereIn / whereNotIn 메서드들
|
|
1128
|
+
whereIn<TColumn extends AvailableColumns<TTables>>(
|
|
1129
|
+
column: TColumn,
|
|
1130
|
+
values: ExtractColumnType<TTables, TColumn & string>[],
|
|
1131
|
+
): this;
|
|
1132
|
+
whereIn(...args: any[]): WhereGroup<TTables> {
|
|
1133
|
+
this.builder.whereIn(args[0], args[1]);
|
|
1134
|
+
return this;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
whereNotIn<TColumn extends AvailableColumns<TTables>>(
|
|
1138
|
+
column: TColumn,
|
|
1139
|
+
values: ExtractColumnType<TTables, TColumn & string>[],
|
|
1140
|
+
): this;
|
|
1141
|
+
whereNotIn(...args: any[]): WhereGroup<TTables> {
|
|
1142
|
+
this.builder.whereNotIn(args[0], args[1]);
|
|
1143
|
+
return this;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1123
1146
|
// orWhere 메서드들
|
|
1124
1147
|
orWhere(conditions: WhereCondition<TTables>): this;
|
|
1125
1148
|
orWhere<TColumn extends AvailableColumns<TTables>>(
|
|
@@ -1136,6 +1159,25 @@ export class WhereGroup<TTables extends Record<string, any>> {
|
|
|
1136
1159
|
return this;
|
|
1137
1160
|
}
|
|
1138
1161
|
|
|
1162
|
+
// orWhereIn / orWhereNotIn 메서드들
|
|
1163
|
+
orWhereIn<TColumn extends AvailableColumns<TTables>>(
|
|
1164
|
+
column: TColumn,
|
|
1165
|
+
values: ExtractColumnType<TTables, TColumn & string>[],
|
|
1166
|
+
): this;
|
|
1167
|
+
orWhereIn(...args: any[]): WhereGroup<TTables> {
|
|
1168
|
+
this.builder.orWhereIn(args[0], args[1]);
|
|
1169
|
+
return this;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
orWhereNotIn<TColumn extends AvailableColumns<TTables>>(
|
|
1173
|
+
column: TColumn,
|
|
1174
|
+
values: ExtractColumnType<TTables, TColumn & string>[],
|
|
1175
|
+
): this;
|
|
1176
|
+
orWhereNotIn(...args: any[]): WhereGroup<TTables> {
|
|
1177
|
+
this.builder.orWhereNotIn(args[0], args[1]);
|
|
1178
|
+
return this;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1139
1181
|
// 중첩 그룹
|
|
1140
1182
|
whereGroup(callback: (g: WhereGroup<TTables>) => void): this;
|
|
1141
1183
|
whereGroup(callback: (g: WhereGroup<TTables>) => void): WhereGroup<TTables> {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getLogger } from "@logtape/logtape";
|
|
1
2
|
import { randomUUID } from "crypto";
|
|
2
3
|
import type { Knex } from "knex";
|
|
3
4
|
import { isArray, unique } from "radashi";
|
|
@@ -8,6 +9,8 @@ import { assertDefined, chunk, nonNullable } from "../utils/utils";
|
|
|
8
9
|
import { batchUpdate, type RowWithId } from "./_batch_update";
|
|
9
10
|
import type { ColumnKeys, ForeignKeyColumns, IdType, TableName } from "./puri.types";
|
|
10
11
|
|
|
12
|
+
const logger = getLogger(["sonamu", "internal", "upsert-builder"]);
|
|
13
|
+
|
|
11
14
|
/**
|
|
12
15
|
* FK 타입 추론을 위해 DatabaseForeignKeys export
|
|
13
16
|
* (module augmentation 자동 로드 보장)
|
|
@@ -256,7 +259,13 @@ export class UpsertBuilder {
|
|
|
256
259
|
const allIds: (number | string)[] = [];
|
|
257
260
|
|
|
258
261
|
// 레벨별로 순차 처리
|
|
259
|
-
for (
|
|
262
|
+
for (let levelIdx = 0; levelIdx < levels.length; levelIdx++) {
|
|
263
|
+
const levelRows = levels[levelIdx];
|
|
264
|
+
logger.debug("Processing Query Level: {current} / {total}", {
|
|
265
|
+
current: levelIdx + 1,
|
|
266
|
+
total: levels.length,
|
|
267
|
+
});
|
|
268
|
+
|
|
260
269
|
// 이전 레벨에서 얻은 ID로 자기 참조 해결
|
|
261
270
|
const resolvedRows = levelRows.map((row) => {
|
|
262
271
|
const resolved = { ...row };
|
|
@@ -284,8 +293,13 @@ export class UpsertBuilder {
|
|
|
284
293
|
const levelChunks = chunkSize ? chunk(resolvedRows, chunkSize) : [resolvedRows];
|
|
285
294
|
const selectFields = unique(["id", ...extractFields]);
|
|
286
295
|
|
|
287
|
-
for (
|
|
296
|
+
for (let index = 0; index < levelChunks.length; index++) {
|
|
297
|
+
const dataChunk = levelChunks[index];
|
|
288
298
|
if (dataChunk.length === 0) continue;
|
|
299
|
+
logger.debug("Processing Chunk: {current} / {total}", {
|
|
300
|
+
current: index + 1,
|
|
301
|
+
total: levelChunks.length,
|
|
302
|
+
});
|
|
289
303
|
|
|
290
304
|
// uuid를 별도로 보관하고, DB에 저장할 데이터에서 제거
|
|
291
305
|
const originalUuids = dataChunk.map((r) => r.uuid as string);
|
package/src/dict/en.ts
CHANGED
package/src/dict/ko.ts
CHANGED
package/src/dict/rc-keys.ts
CHANGED
|
@@ -77,6 +77,22 @@ export const rcKeysKo = {
|
|
|
77
77
|
"rc.calendar.month.10": "11월",
|
|
78
78
|
"rc.calendar.month.11": "12월",
|
|
79
79
|
|
|
80
|
+
// Sonamu Filter Component
|
|
81
|
+
"rc.sonamuFilter.title": "소나무 필터",
|
|
82
|
+
"rc.sonamuFilter.apply": "적용",
|
|
83
|
+
"rc.sonamuFilter.reset": "초기화",
|
|
84
|
+
"rc.sonamuFilter.addRule": "규칙 추가",
|
|
85
|
+
"rc.sonamuFilter.noRulesYet": '아직 규칙이 없습니다. "+ 규칙 추가"를 클릭하여 시작하세요.',
|
|
86
|
+
"rc.sonamuFilter.selectField": "필드 선택",
|
|
87
|
+
"rc.sonamuFilter.selectOperator": "연산자 선택",
|
|
88
|
+
"rc.sonamuFilter.selectOperatorFirst": "먼저 연산자를 선택하세요",
|
|
89
|
+
"rc.sonamuFilter.enterValue": "값 입력",
|
|
90
|
+
"rc.sonamuFilter.enterNumber": "숫자 입력",
|
|
91
|
+
"rc.sonamuFilter.notSupported": "지원하지 않음",
|
|
92
|
+
"rc.sonamuFilter.startDate": "시작일",
|
|
93
|
+
"rc.sonamuFilter.endDate": "종료일",
|
|
94
|
+
"rc.sonamuFilter.appliedFilters": "🌲 적용된 소나무 필터",
|
|
95
|
+
|
|
80
96
|
// Common
|
|
81
97
|
"rc.common.cancel": "취소",
|
|
82
98
|
"rc.common.save": "저장",
|
|
@@ -157,6 +173,22 @@ export const rcKeysEn = {
|
|
|
157
173
|
"rc.calendar.month.10": "November",
|
|
158
174
|
"rc.calendar.month.11": "December",
|
|
159
175
|
|
|
176
|
+
// Sonamu Filter Component
|
|
177
|
+
"rc.sonamuFilter.title": "Sonamu Filter",
|
|
178
|
+
"rc.sonamuFilter.apply": "Apply",
|
|
179
|
+
"rc.sonamuFilter.reset": "Reset",
|
|
180
|
+
"rc.sonamuFilter.addRule": "Add Rule",
|
|
181
|
+
"rc.sonamuFilter.noRulesYet": 'No rules yet. Click "+ Add Rule" to start.',
|
|
182
|
+
"rc.sonamuFilter.selectField": "Select field",
|
|
183
|
+
"rc.sonamuFilter.selectOperator": "Operator",
|
|
184
|
+
"rc.sonamuFilter.selectOperatorFirst": "Select operator first",
|
|
185
|
+
"rc.sonamuFilter.enterValue": "Enter value",
|
|
186
|
+
"rc.sonamuFilter.enterNumber": "Enter number",
|
|
187
|
+
"rc.sonamuFilter.notSupported": "Not supported",
|
|
188
|
+
"rc.sonamuFilter.startDate": "Start date",
|
|
189
|
+
"rc.sonamuFilter.endDate": "End date",
|
|
190
|
+
"rc.sonamuFilter.appliedFilters": "🌲 Applied SonamuFilters",
|
|
191
|
+
|
|
160
192
|
// Common
|
|
161
193
|
"rc.common.cancel": "Cancel",
|
|
162
194
|
"rc.common.save": "Save",
|
package/src/dict/sd.ts
CHANGED
|
@@ -25,11 +25,31 @@ type SDReturnType<K extends DictKey> = SonamuDict[K] extends (...args: infer P)
|
|
|
25
25
|
? (...args: P) => LocalizedString
|
|
26
26
|
: LocalizedString;
|
|
27
27
|
|
|
28
|
-
const FALLBACK_LOCALE = "en";
|
|
29
28
|
function getDictValue<K extends DictKey>(key: K, locale: string): SDReturnType<K> {
|
|
29
|
+
const { defaultLocale, supportedLocales } = Sonamu.config.i18n;
|
|
30
|
+
|
|
31
|
+
// 1. 지정된 locale에서 조회
|
|
30
32
|
const dict = dictionaries[locale];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
if (dict?.[key] !== undefined) {
|
|
34
|
+
return dict[key] as unknown as SDReturnType<K>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 2. default locale에서 조회
|
|
38
|
+
if (locale !== defaultLocale && dictionaries[defaultLocale]?.[key] !== undefined) {
|
|
39
|
+
return dictionaries[defaultLocale][key] as unknown as SDReturnType<K>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 3. supported locales 순회
|
|
43
|
+
for (const supportedLocale of supportedLocales) {
|
|
44
|
+
if (supportedLocale !== locale && supportedLocale !== defaultLocale) {
|
|
45
|
+
if (dictionaries[supportedLocale]?.[key] !== undefined) {
|
|
46
|
+
return dictionaries[supportedLocale][key] as unknown as SDReturnType<K>;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 4. 모두 실패 시 key 반환
|
|
52
|
+
return key as unknown as SDReturnType<K>;
|
|
33
53
|
}
|
|
34
54
|
|
|
35
55
|
/**
|
|
@@ -104,6 +104,10 @@ class EntityManagerClass {
|
|
|
104
104
|
return Array.from(EntityManager.entities.keys()).sort();
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
getAllEntities(): Entity[] {
|
|
108
|
+
return Array.from(this.entities.values());
|
|
109
|
+
}
|
|
110
|
+
|
|
107
111
|
getAllParentIds(): string[] {
|
|
108
112
|
return this.getAllIds().filter((entityId) => {
|
|
109
113
|
const entity = this.get(entityId);
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { type FakerMappingConfig, fakerMappings } from "../testing/faker-mappings";
|
|
2
|
+
import type { Cone, EntityJson, EntityProp, OneToOneRelationProp, SubsetDef } from "../types/types";
|
|
3
|
+
import {
|
|
4
|
+
isBelongsToOneRelationProp,
|
|
5
|
+
isEnumDefWithCone,
|
|
6
|
+
isOneToOneRelationProp,
|
|
7
|
+
isRelationProp,
|
|
8
|
+
isSubsetDefWithCone,
|
|
9
|
+
} from "../types/types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Entity의 템플릿 cone을 생성합니다.
|
|
13
|
+
*
|
|
14
|
+
* LLM을 사용하지 않고 faker-mappings.ts를 활용하여 기본 cone을 생성합니다.
|
|
15
|
+
* stub entity 생성 시 자동으로 호출되어 최소한의 cone 메타데이터를 제공합니다.
|
|
16
|
+
*/
|
|
17
|
+
export function generateTemplateCones(
|
|
18
|
+
entity: EntityJson,
|
|
19
|
+
locale: "ko" | "en" | "ja" = "ko",
|
|
20
|
+
): {
|
|
21
|
+
entityCone?: Cone;
|
|
22
|
+
propCones: Record<string, Cone>;
|
|
23
|
+
subsetCones: Record<string, Cone>;
|
|
24
|
+
enumCones: Record<string, Cone>;
|
|
25
|
+
} {
|
|
26
|
+
const mapping = fakerMappings[locale];
|
|
27
|
+
|
|
28
|
+
if (!mapping) {
|
|
29
|
+
throw new Error(`Unsupported locale: ${locale}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 1. Entity cone
|
|
33
|
+
const entityCone: Cone = {
|
|
34
|
+
desc: getEntityDesc(entity, locale),
|
|
35
|
+
fixtureHint: getEntityFixtureHint(entity, locale),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// 2. Prop cones
|
|
39
|
+
const propCones: Record<string, Cone> = {};
|
|
40
|
+
for (const prop of entity.props) {
|
|
41
|
+
const fakerConfig = findFakerMapping(prop, mapping);
|
|
42
|
+
|
|
43
|
+
propCones[prop.name] = {
|
|
44
|
+
desc: fakerConfig?.comment || getDefaultDesc(prop, locale),
|
|
45
|
+
fixtureGenerator: fakerConfig?.faker,
|
|
46
|
+
fixtureHint: getFixtureHint(prop, fakerConfig, locale),
|
|
47
|
+
dataSource: shouldHaveDataSource(prop)
|
|
48
|
+
? { strategy: "recent", config: { limit: 5 } }
|
|
49
|
+
: undefined,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 3. Subset cones
|
|
54
|
+
const subsetCones: Record<string, Cone> = {};
|
|
55
|
+
for (const [key, subset] of Object.entries(entity.subsets || {})) {
|
|
56
|
+
subsetCones[key] = {
|
|
57
|
+
desc: getSubsetDesc(key, subset, locale),
|
|
58
|
+
note: getSubsetNote(key, subset, locale),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 4. Enum cones
|
|
63
|
+
const enumCones: Record<string, Cone> = {};
|
|
64
|
+
for (const [enumId, enumDef] of Object.entries(entity.enums || {})) {
|
|
65
|
+
const values = isEnumDefWithCone(enumDef) ? enumDef.values : enumDef;
|
|
66
|
+
|
|
67
|
+
enumCones[enumId] = {
|
|
68
|
+
desc: getEnumDesc(enumId, locale),
|
|
69
|
+
note: getEnumNote(enumId, values, locale),
|
|
70
|
+
values: Object.keys(values).reduce(
|
|
71
|
+
(acc, key) => {
|
|
72
|
+
acc[key] = {
|
|
73
|
+
desc: getEnumValueDesc(values[key] || key, locale),
|
|
74
|
+
};
|
|
75
|
+
return acc;
|
|
76
|
+
},
|
|
77
|
+
{} as Record<string, { desc: string }>,
|
|
78
|
+
),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { entityCone, propCones, subsetCones, enumCones };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Prop에 맞는 faker mapping을 찾습니다.
|
|
87
|
+
*
|
|
88
|
+
* 우선순위:
|
|
89
|
+
* 1. field_patterns에서 prop name 매칭 (정확한 이름 매칭)
|
|
90
|
+
* 2. field_patterns에서 부분 매칭 (name 필드가 user_name이면 name 매칭)
|
|
91
|
+
* 3. type_defaults에서 prop type 매칭
|
|
92
|
+
*/
|
|
93
|
+
function findFakerMapping(
|
|
94
|
+
prop: EntityProp,
|
|
95
|
+
mapping: {
|
|
96
|
+
field_patterns: Record<string, FakerMappingConfig>;
|
|
97
|
+
type_defaults: Record<string, FakerMappingConfig>;
|
|
98
|
+
},
|
|
99
|
+
): FakerMappingConfig | undefined {
|
|
100
|
+
if (isRelationProp(prop)) {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const propName = prop.name.toLowerCase();
|
|
105
|
+
|
|
106
|
+
// 1. 정확한 필드명 매칭
|
|
107
|
+
if (mapping.field_patterns[propName]) {
|
|
108
|
+
return mapping.field_patterns[propName];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 2. 부분 매칭 (user_name → name, email_address → email)
|
|
112
|
+
for (const [pattern, config] of Object.entries(mapping.field_patterns)) {
|
|
113
|
+
if (propName.includes(pattern) || propName.endsWith(`_${pattern}`)) {
|
|
114
|
+
return config;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 3. 타입 기본값 매칭
|
|
119
|
+
const typeKey = prop.type;
|
|
120
|
+
if (mapping.type_defaults[typeKey]) {
|
|
121
|
+
return mapping.type_defaults[typeKey];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Entity 설명을 생성합니다.
|
|
129
|
+
*/
|
|
130
|
+
function getEntityDesc(entity: EntityJson, locale: "ko" | "en" | "ja"): string {
|
|
131
|
+
const title = entity.title || entity.id;
|
|
132
|
+
|
|
133
|
+
const templates = {
|
|
134
|
+
ko: `${title} 엔티티`,
|
|
135
|
+
en: `${title} entity`,
|
|
136
|
+
ja: `${title}エンティティ`,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
return templates[locale];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Entity fixture hint를 생성합니다.
|
|
144
|
+
*/
|
|
145
|
+
function getEntityFixtureHint(entity: EntityJson, locale: "ko" | "en" | "ja"): string {
|
|
146
|
+
const title = entity.title || entity.id;
|
|
147
|
+
|
|
148
|
+
const templates = {
|
|
149
|
+
ko: `${title} 테스트 데이터를 생성합니다. 각 필드는 실제 데이터와 유사한 형식으로 생성됩니다.`,
|
|
150
|
+
en: `Generates ${title} test data. Each field is generated in a format similar to real data.`,
|
|
151
|
+
ja: `${title}のテストデータを生成します。各フィールドは実際のデータに似た形式で生成されます。`,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return templates[locale];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Prop 기본 설명을 생성합니다 (faker mapping이 없는 경우).
|
|
159
|
+
*/
|
|
160
|
+
function getDefaultDesc(prop: EntityProp, locale: "ko" | "en" | "ja"): string {
|
|
161
|
+
if (isRelationProp(prop)) {
|
|
162
|
+
const templates = {
|
|
163
|
+
ko: `${prop.with} 참조`,
|
|
164
|
+
en: `Reference to ${prop.with}`,
|
|
165
|
+
ja: `${prop.with}への参照`,
|
|
166
|
+
};
|
|
167
|
+
return templates[locale];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const templates = {
|
|
171
|
+
ko: prop.name,
|
|
172
|
+
en: prop.name,
|
|
173
|
+
ja: prop.name,
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return templates[locale];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Prop fixture hint를 생성합니다.
|
|
181
|
+
*/
|
|
182
|
+
function getFixtureHint(
|
|
183
|
+
prop: EntityProp,
|
|
184
|
+
fakerConfig: FakerMappingConfig | undefined,
|
|
185
|
+
locale: "ko" | "en" | "ja",
|
|
186
|
+
): string | undefined {
|
|
187
|
+
if (isRelationProp(prop)) {
|
|
188
|
+
const templates = {
|
|
189
|
+
ko: `기존 ${prop.with} 데이터를 참조합니다`,
|
|
190
|
+
en: `References existing ${prop.with} data`,
|
|
191
|
+
ja: `既存の${prop.with}データを参照します`,
|
|
192
|
+
};
|
|
193
|
+
return templates[locale];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (fakerConfig?.comment) {
|
|
197
|
+
const templates = {
|
|
198
|
+
ko: `${fakerConfig.comment} 형식으로 생성됩니다`,
|
|
199
|
+
en: `Generated as ${fakerConfig.comment}`,
|
|
200
|
+
ja: `${fakerConfig.comment}形式で生成されます`,
|
|
201
|
+
};
|
|
202
|
+
return templates[locale];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Prop이 dataSource를 가져야 하는지 판단합니다.
|
|
210
|
+
*
|
|
211
|
+
* BelongsToOne 또는 OneToOne (hasJoinColumn: true)인 경우 dataSource가 필요합니다.
|
|
212
|
+
*/
|
|
213
|
+
function shouldHaveDataSource(prop: EntityProp): boolean {
|
|
214
|
+
if (!isRelationProp(prop)) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// BelongsToOne은 항상 dataSource 필요
|
|
219
|
+
if (isBelongsToOneRelationProp(prop)) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// OneToOne은 hasJoinColumn: true인 경우에만 dataSource 필요
|
|
224
|
+
if (isOneToOneRelationProp(prop)) {
|
|
225
|
+
return (prop as OneToOneRelationProp).hasJoinColumn === true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Subset 설명을 생성합니다.
|
|
233
|
+
*/
|
|
234
|
+
function getSubsetDesc(key: string, _subset: SubsetDef, locale: "ko" | "en" | "ja"): string {
|
|
235
|
+
const templates = {
|
|
236
|
+
ko: `${key} 서브셋`,
|
|
237
|
+
en: `${key} subset`,
|
|
238
|
+
ja: `${key}サブセット`,
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
return templates[locale];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Subset note를 생성합니다.
|
|
246
|
+
*/
|
|
247
|
+
function getSubsetNote(_key: string, subset: SubsetDef, locale: "ko" | "en" | "ja"): string {
|
|
248
|
+
const fields = isSubsetDefWithCone(subset) ? subset.fields : subset;
|
|
249
|
+
const fieldNames = fields.map((f) => (typeof f === "string" ? f : f.field)).join(", ");
|
|
250
|
+
|
|
251
|
+
const templates = {
|
|
252
|
+
ko: `포함된 필드: ${fieldNames}`,
|
|
253
|
+
en: `Included fields: ${fieldNames}`,
|
|
254
|
+
ja: `含まれるフィールド: ${fieldNames}`,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return templates[locale];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Enum 설명을 생성합니다.
|
|
262
|
+
*/
|
|
263
|
+
function getEnumDesc(_enumId: string, locale: "ko" | "en" | "ja"): string {
|
|
264
|
+
const templates = {
|
|
265
|
+
ko: `열거형`,
|
|
266
|
+
en: `enum`,
|
|
267
|
+
ja: `列挙型`,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
return templates[locale];
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Enum note를 생성합니다.
|
|
275
|
+
*/
|
|
276
|
+
function getEnumNote(
|
|
277
|
+
_enumId: string,
|
|
278
|
+
values: Record<string, string>,
|
|
279
|
+
locale: "ko" | "en" | "ja",
|
|
280
|
+
): string {
|
|
281
|
+
const valueList = Object.keys(values).join(", ");
|
|
282
|
+
|
|
283
|
+
const templates = {
|
|
284
|
+
ko: `가능한 값: ${valueList}`,
|
|
285
|
+
en: `Possible values: ${valueList}`,
|
|
286
|
+
ja: `可能な値: ${valueList}`,
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
return templates[locale];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Enum value 설명을 생성합니다.
|
|
294
|
+
*/
|
|
295
|
+
function getEnumValueDesc(label: string, _locale: "ko" | "en" | "ja"): string {
|
|
296
|
+
// label을 그대로 사용
|
|
297
|
+
return label;
|
|
298
|
+
}
|