@simplysm/sd-cli 14.0.89 → 14.0.90

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 (82) hide show
  1. package/dist/commands/init/generators/client-common.d.ts.map +1 -1
  2. package/dist/commands/init/generators/client-common.js +6 -2
  3. package/dist/commands/init/generators/client-common.js.map +1 -1
  4. package/dist/commands/init/generators/client.d.ts.map +1 -1
  5. package/dist/commands/init/generators/client.js +13 -2
  6. package/dist/commands/init/generators/client.js.map +1 -1
  7. package/dist/commands/init/generators/common.d.ts.map +1 -1
  8. package/dist/commands/init/generators/common.js +16 -1
  9. package/dist/commands/init/generators/common.js.map +1 -1
  10. package/dist/commands/init/generators/server.d.ts.map +1 -1
  11. package/dist/commands/init/generators/server.js +9 -0
  12. package/dist/commands/init/generators/server.js.map +1 -1
  13. package/dist/commands/init/init.d.ts.map +1 -1
  14. package/dist/commands/init/init.js +11 -2
  15. package/dist/commands/init/init.js.map +1 -1
  16. package/dist/commands/init/normalize.d.ts.map +1 -1
  17. package/dist/commands/init/normalize.js +40 -4
  18. package/dist/commands/init/normalize.js.map +1 -1
  19. package/dist/commands/init/prompts.d.ts +1 -1
  20. package/dist/commands/init/prompts.d.ts.map +1 -1
  21. package/dist/commands/init/prompts.js +34 -3
  22. package/dist/commands/init/prompts.js.map +1 -1
  23. package/dist/commands/init/types.d.ts +16 -1
  24. package/dist/commands/init/types.d.ts.map +1 -1
  25. package/dist/commands/init/validate.d.ts.map +1 -1
  26. package/dist/commands/init/validate.js +3 -0
  27. package/dist/commands/init/validate.js.map +1 -1
  28. package/package.json +4 -4
  29. package/src/commands/init/generators/client-common.ts +18 -5
  30. package/src/commands/init/generators/client.ts +41 -2
  31. package/src/commands/init/generators/common.ts +56 -2
  32. package/src/commands/init/generators/server.ts +30 -0
  33. package/src/commands/init/init.ts +12 -2
  34. package/src/commands/init/normalize.ts +49 -4
  35. package/src/commands/init/prompts.ts +34 -3
  36. package/src/commands/init/templates/client/login-public/assets/logo-landscape.png +0 -0
  37. package/src/commands/init/templates/client/login-public/assets/logo.png +0 -0
  38. package/src/commands/init/templates/client/package.json.hbs +3 -2
  39. package/src/commands/init/templates/client/src/app/home/home.view.ts.hbs +137 -0
  40. package/src/commands/init/templates/client/src/app/home/main/main.view.ts.hbs +16 -0
  41. package/src/commands/init/templates/client/src/app/home/my-info/my-info.detail.ts.hbs +265 -0
  42. package/src/commands/init/templates/client/src/app/login/login.view.ts.hbs +144 -0
  43. package/src/commands/init/templates/client/src/app.root.ts.hbs +64 -0
  44. package/src/commands/init/templates/client/src/index.html.hbs +75 -1
  45. package/src/commands/init/templates/client/src/main.ts.hbs +147 -7
  46. package/src/commands/init/templates/client/src/modals/dev.modal.ts.hbs +63 -0
  47. package/src/commands/init/templates/client/src/routes.ts.hbs +29 -0
  48. package/src/commands/init/templates/client-common/package.json.hbs +1 -0
  49. package/src/commands/init/templates/client-common/src/index.ts.hbs +6 -2
  50. package/src/commands/init/templates/client-common/src/providers/app-auth.provider.ts.hbs +90 -0
  51. package/src/commands/init/templates/client-common/src/providers/{AppOrmProvider.ts.hbs → app-orm.provider.ts.hbs} +5 -11
  52. package/src/commands/init/templates/client-common/src/providers/app-service.provider.ts.hbs +68 -0
  53. package/src/commands/init/templates/client-common/src/providers/app-shared-data.provider.ts.hbs +100 -0
  54. package/src/commands/init/templates/common/package.json.hbs +2 -1
  55. package/src/commands/init/templates/common/src/app-structure.ts.hbs +26 -0
  56. package/src/commands/init/templates/common/src/auth-info-changed.event.ts.hbs +3 -0
  57. package/src/commands/init/templates/common/src/db/db-context.ts.hbs +20 -0
  58. package/src/commands/init/templates/common/src/db/system-data-log.ext.ts.hbs +138 -0
  59. package/src/commands/init/templates/common/src/db/tables/master/user-config.ts.hbs +15 -0
  60. package/src/commands/init/templates/common/src/db/tables/master/user.ts.hbs +20 -0
  61. package/src/commands/init/templates/common/src/db/tables/system/role-permission.ts.hbs +16 -0
  62. package/src/commands/init/templates/common/src/db/tables/system/role.ts.hbs +16 -0
  63. package/src/commands/init/templates/common/src/db/tables/system/system-data-log.ts.hbs +23 -0
  64. package/src/commands/init/templates/common/src/db/tables/system/system-log.ts.hbs +21 -0
  65. package/src/commands/init/templates/common/src/index.ts.hbs +14 -1
  66. package/src/commands/init/templates/server/package.json.hbs +7 -3
  67. package/src/commands/init/templates/server/public-dev//354/264/210/352/270/260/355/231/224.xlsx +0 -0
  68. package/src/commands/init/templates/server/src/index.ts.hbs +4 -0
  69. package/src/commands/init/templates/server/src/main.ts.hbs +11 -1
  70. package/src/commands/init/templates/server/src/services/auth.service.ts.hbs +284 -0
  71. package/src/commands/init/templates/server/src/services/dev.service.ts.hbs +112 -0
  72. package/src/commands/init/templates/server/src/utils/orm.utils.ts.hbs +8 -0
  73. package/src/commands/init/templates/workspace-root/sd.config.ts.hbs +2 -2
  74. package/src/commands/init/types.ts +16 -1
  75. package/src/commands/init/validate.ts +6 -0
  76. package/tests/init/__snapshots__/render.spec.ts.snap +36 -9
  77. package/tests/init/normalize.spec.ts +95 -1
  78. package/tests/init/render.spec.ts +951 -10
  79. package/src/commands/init/templates/client/src/AppPage.ts.hbs +0 -18
  80. package/src/commands/init/templates/client/src/routes.ts +0 -3
  81. package/src/commands/init/templates/client-common/src/providers/AppServiceProvider.ts +0 -27
  82. package/src/commands/init/templates/common/src/DbContext.ts.hbs +0 -4
@@ -0,0 +1,112 @@
1
+ {{#if hasAuth}}
2
+ import path from "node:path";
3
+ {{/if}}
4
+ import { defineService, type ServiceMethods } from "@simplysm/service-server";
5
+ {{#if hasAuth}}
6
+ import { getFlatPermissions } from "@simplysm/service-common";
7
+ import { ExcelWorkbook } from "@simplysm/excel";
8
+ import bcrypt from "bcrypt";
9
+ import { fsx } from "@simplysm/core-node";
10
+ {{/if}}
11
+ import { getOrm } from "../utils/orm.utils";
12
+ {{#if hasAuth}}
13
+ import { {{#each appStructureNames}}{{this}}{{#unless @last}}, {{/unless}}{{/each}} } from "@{{workspaceName}}/common";
14
+ {{/if}}
15
+
16
+ export const DevService = defineService("DevService", (ctx) => {
17
+ {{#if hasAuth}}
18
+ async function loadInitXlsx() {
19
+ const excelPath = path.resolve(ctx.server.options.rootPath, "초기화.xlsx");
20
+ if (!(await fsx.exists(excelPath))) {
21
+ throw new Error(`초기화 파일을 찾을 수 없습니다: ${excelPath}`);
22
+ }
23
+
24
+ const xlsxBytes = await fsx.readBytes(excelPath);
25
+ const wb = new ExcelWorkbook(xlsxBytes);
26
+ const excelData: Record<string, Record<string, any>[]> = {};
27
+ try {
28
+ for (const wsName of await wb.getWorksheetNames()) {
29
+ const ws = await wb.getWorksheet(wsName);
30
+ excelData[wsName] = await ws.getDataTable();
31
+ }
32
+ } finally {
33
+ await wb.close();
34
+ }
35
+
36
+ return excelData;
37
+ }
38
+ {{/if}}
39
+
40
+ return {
41
+ initDb: async (): Promise<void> => {
42
+ {{#if hasAuth}}
43
+ const excelData = await loadInitXlsx();
44
+
45
+ {{/if}}
46
+ const orm = await getOrm(ctx);
47
+ await orm.connectWithoutTransaction(async (db) => {
48
+ await db.initialize({ force: true });
49
+
50
+ {{#if hasAuth}}
51
+ await db.transaction(async () => {
52
+ if ("역할" in excelData) {
53
+ await db
54
+ .role()
55
+ .insert(
56
+ excelData["역할"]
57
+ .filter((r) => r["ID"] != null)
58
+ .map((r) => ({ id: r["ID"], name: r["명칭"], isDeleted: false })),
59
+ );
60
+ await db.role().insertDataLog({ action: "초기화" });
61
+ }
62
+
63
+ if ("직원" in excelData) {
64
+ await db.{{userEntityCamel}}().insert(
65
+ await excelData["직원"]
66
+ .filter((r) => r["ID"] != null)
67
+ .mapAsync(async (r) => ({
68
+ id: r["ID"],
69
+ name: r["이름"],
70
+ loginId: r["로그인ID"],
71
+ encryptedPassword: await bcrypt.hash(r["비밀번호"], 10),
72
+ roleId: r["역할.ID"],
73
+ isDeleted: false,
74
+ })),
75
+ );
76
+ await db.{{userEntityCamel}}().insertDataLog({ action: "초기화" });
77
+ }
78
+
79
+ if ("역할권한" in excelData) {
80
+ const allPermCodes = getFlatPermissions(
81
+ [{{#each appStructureNames}}...{{this}}{{#unless @last}}, {{/unless}}{{/each}}],
82
+ undefined,
83
+ )
84
+ .map((p) => p.codeChain.join("."))
85
+ .distinct();
86
+ await db.rolePermission().insert(
87
+ excelData["역할권한"]
88
+ .filter((r) => r["역할.ID"] != null)
89
+ .flatMap((r) =>
90
+ r["코드"] === "ALL"
91
+ ? allPermCodes.map((code) => ({
92
+ roleId: r["역할.ID"],
93
+ code,
94
+ valueJson: r["값"],
95
+ }))
96
+ : [{ roleId: r["역할.ID"], code: r["코드"], valueJson: r["값"] }],
97
+ ),
98
+ );
99
+ await db.rolePermission().insertDataLog({ action: "초기화" });
100
+ }
101
+
102
+ // TODO: 업무 테이블 초기 데이터 시드 추가
103
+ });
104
+ {{else}}
105
+ // TODO: 초기 데이터 시드 추가
106
+ {{/if}}
107
+ });
108
+ },
109
+ };
110
+ });
111
+
112
+ export type DevServiceMethods = ServiceMethods<typeof DevService>;
@@ -0,0 +1,8 @@
1
+ import { createOrm, type DbConnConfig } from "@simplysm/orm-node";
2
+ import { {{dbContextClassName}} } from "@{{workspaceName}}/common";
3
+ import type { ServiceContext } from "@simplysm/service-server";
4
+
5
+ export async function getOrm(ctx: ServiceContext) {
6
+ const ormConfig = await ctx.getConfig<{ {{dbContextNameUpper}}: DbConnConfig }>("orm");
7
+ return createOrm({{dbContextClassName}}, ormConfig.{{dbContextNameUpper}});
8
+ }
@@ -15,8 +15,8 @@ const config: SdConfigFn = () => ({
15
15
  dialect: "{{dbDialect}}",
16
16
  host: "localhost",
17
17
  port: {{dbPort}},
18
- username: "root",
19
- password: "1234",
18
+ username: "{{dbUsername}}",
19
+ password: "{{dbPassword}}",
20
20
  database: "{{workspaceNameUpper}}",
21
21
  defaultIsolationLevel: "READ_UNCOMMITTED",
22
22
  },
@@ -1,5 +1,5 @@
1
1
  export type ClientType = "web" | "mobile";
2
- export type DbDialect = "mysql" | "postgres" | "mssql";
2
+ export type DbDialect = "mysql" | "postgresql" | "mssql";
3
3
 
4
4
  export interface ClientInputSpec {
5
5
  name: string;
@@ -15,6 +15,9 @@ export interface InitInput {
15
15
  hasDb: boolean;
16
16
  dbDialect?: DbDialect;
17
17
  dbContextName?: string;
18
+ hasAuth?: boolean;
19
+ userEntityName?: string;
20
+ userEntityLabel?: string;
18
21
  mobileAppId?: string;
19
22
  serverPort?: number;
20
23
  }
@@ -24,6 +27,8 @@ export interface ClientSpec {
24
27
  type: ClientType;
25
28
  hasRouter: boolean;
26
29
  isMobile: boolean;
30
+ appStructureName: string;
31
+ needsNgIcons: boolean;
27
32
  }
28
33
 
29
34
  export interface NormalizedInput {
@@ -34,11 +39,21 @@ export interface NormalizedInput {
34
39
  hasDb: boolean;
35
40
  dbDialect?: DbDialect;
36
41
  dbPort: number;
42
+ dbUsername: string;
43
+ dbPassword: string;
37
44
  isMysql: boolean;
38
45
  isPostgres: boolean;
39
46
  isMssql: boolean;
40
47
  dbContextClassName: string;
41
48
  dbContextNameUpper: string;
49
+ dbContextFileName: string;
50
+ dbFolderName: string;
51
+ hasAuth: boolean;
52
+ userEntityPascal: string;
53
+ userEntityCamel: string;
54
+ userEntityKebab: string;
55
+ userEntityLabel: string;
56
+ appStructureNames: string[];
42
57
  serverPort: number;
43
58
  mobileAppId?: string;
44
59
  firstMobileClientName?: string;
@@ -26,6 +26,12 @@ export function validateInput(input: InitInput): void {
26
26
  );
27
27
  }
28
28
 
29
+ if (input.hasAuth && input.userEntityName != null && !KEBAB_CASE_RE.test(input.userEntityName)) {
30
+ throw new Error(
31
+ `사용자 엔티티 영문 식별자는 kebab-case 여야 합니다 (예: employee). 입력값: "${input.userEntityName}"`,
32
+ );
33
+ }
34
+
29
35
  const clientNames = new Set<string>();
30
36
  for (const c of input.clients) {
31
37
  if (!KEBAB_CASE_RE.test(c.name)) {
@@ -1,50 +1,68 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`client/src/main.ts.hbs > 라우팅 N (mobile) 1`] = `
4
- "import { enableProdMode } from "@angular/core";
4
+ "import consola from "consola";
5
+ import { enableProdMode, inject, provideAppInitializer } from "@angular/core";
5
6
  import { bootstrapApplication } from "@angular/platform-browser";
6
7
  import { provideHttpClient, withFetch } from "@angular/common/http";
7
8
  import { provideSdAngular } from "@simplysm/angular";
8
- import { AppPage } from "./AppPage";
9
+ import { AppServiceProvider } from "@demo/client-common";
10
+ import { AppRoot } from "./app.root";
9
11
  import "./styles.scss";
10
12
 
11
13
  if (typeof ngDevMode !== "undefined" && !ngDevMode) {
12
14
  enableProdMode();
15
+ } else {
16
+ consola.log("dev mode");
13
17
  }
14
18
 
15
- bootstrapApplication(AppPage, {
19
+ bootstrapApplication(AppRoot, {
16
20
  providers: [
17
21
  provideHttpClient(withFetch()),
18
22
  provideSdAngular({ clientName: "client-pda" }),
23
+
24
+ //-- app-service 연결
25
+ provideAppInitializer(async () => {
26
+ await inject(AppServiceProvider).connectAsync();
27
+ }),
19
28
  ],
20
29
  }).catch((err: unknown) => {
21
- console.error(err);
30
+ consola.error(err);
22
31
  });
23
32
  "
24
33
  `;
25
34
 
26
35
  exports[`client/src/main.ts.hbs > 라우팅 Y (web) 1`] = `
27
- "import { enableProdMode } from "@angular/core";
36
+ "import consola from "consola";
37
+ import { enableProdMode, inject, provideAppInitializer } from "@angular/core";
28
38
  import { bootstrapApplication } from "@angular/platform-browser";
29
39
  import { provideHttpClient, withFetch } from "@angular/common/http";
30
40
  import { provideRouter, withHashLocation } from "@angular/router";
31
41
  import { provideSdAngular } from "@simplysm/angular";
32
- import { AppPage } from "./AppPage";
42
+ import { AppServiceProvider } from "@demo/client-common";
43
+ import { AppRoot } from "./app.root";
33
44
  import { routes } from "./routes";
34
45
  import "./styles.scss";
35
46
 
36
47
  if (typeof ngDevMode !== "undefined" && !ngDevMode) {
37
48
  enableProdMode();
49
+ } else {
50
+ consola.log("dev mode");
38
51
  }
39
52
 
40
- bootstrapApplication(AppPage, {
53
+ bootstrapApplication(AppRoot, {
41
54
  providers: [
42
55
  provideHttpClient(withFetch()),
43
56
  provideRouter(routes, withHashLocation()),
44
57
  provideSdAngular({ clientName: "client-admin" }),
58
+
59
+ //-- app-service 연결
60
+ provideAppInitializer(async () => {
61
+ await inject(AppServiceProvider).connectAsync();
62
+ }),
45
63
  ],
46
64
  }).catch((err: unknown) => {
47
- console.error(err);
65
+ consola.error(err);
48
66
  });
49
67
  "
50
68
  `;
@@ -159,6 +177,10 @@ exports[`server/src/main.ts.hbs > DB=N — OrmService 없음 1`] = `
159
177
  import { env, num, parseBoolEnv } from "@simplysm/core-common";
160
178
  import { setupConsola } from "@simplysm/core-node";
161
179
  import { createServiceServer, getConfig } from "@simplysm/service-server";
180
+ import {EventEmitter} from "node:events";
181
+
182
+ Error.stackTraceLimit = Infinity;
183
+ EventEmitter.defaultMaxListeners = 0;
162
184
 
163
185
  setupConsola();
164
186
 
@@ -188,6 +210,11 @@ exports[`server/src/main.ts.hbs > DB=Y — OrmService 포함 1`] = `
188
210
  import { env, num, parseBoolEnv } from "@simplysm/core-common";
189
211
  import { setupConsola } from "@simplysm/core-node";
190
212
  import { createServiceServer, getConfig, OrmService } from "@simplysm/service-server";
213
+ import {EventEmitter} from "node:events";
214
+ import { DevService } from "./services/dev.service";
215
+
216
+ Error.stackTraceLimit = Infinity;
217
+ EventEmitter.defaultMaxListeners = 0;
191
218
 
192
219
  setupConsola();
193
220
 
@@ -201,7 +228,7 @@ if (authConfig?.jwtSecret == null) {
201
228
 
202
229
  export const server = createServiceServer({
203
230
  rootPath: import.meta.dirname,
204
- services: [OrmService],
231
+ services: [OrmService, ...(parseBoolEnv(env("DEV")) ? [DevService] : [])],
205
232
  port: num.parseInt(env("SERVER_PORT"))!,
206
233
  auth: { jwtSecret: authConfig.jwtSecret },
207
234
  });
@@ -76,7 +76,7 @@ describe("normalize", () => {
76
76
  normalize({ ...base, hasServer: true, hasDb: true, dbDialect: "mysql" }).dbPort,
77
77
  ).toBe(3306);
78
78
  expect(
79
- normalize({ ...base, hasServer: true, hasDb: true, dbDialect: "postgres" }).dbPort,
79
+ normalize({ ...base, hasServer: true, hasDb: true, dbDialect: "postgresql" }).dbPort,
80
80
  ).toBe(5432);
81
81
  expect(
82
82
  normalize({ ...base, hasServer: true, hasDb: true, dbDialect: "mssql" }).dbPort,
@@ -143,6 +143,26 @@ describe("normalize", () => {
143
143
  expect(normalize(base).dbContextClassName).toBe("");
144
144
  });
145
145
 
146
+ it("DB=Y default → dbContextFileName=main.db-context", () => {
147
+ const r = normalize({ ...base, hasServer: true, hasDb: true, dbDialect: "mysql" });
148
+ expect(r.dbContextFileName).toBe("main.db-context");
149
+ });
150
+
151
+ it("DB=Y dbContextName=userOrder → dbContextFileName=user-order.db-context", () => {
152
+ const r = normalize({
153
+ ...base,
154
+ hasServer: true,
155
+ hasDb: true,
156
+ dbDialect: "mysql",
157
+ dbContextName: "userOrder",
158
+ });
159
+ expect(r.dbContextFileName).toBe("user-order.db-context");
160
+ });
161
+
162
+ it("DB=N 시 dbContextFileName 은 빈 문자열", () => {
163
+ expect(normalize(base).dbContextFileName).toBe("");
164
+ });
165
+
146
166
  it("DB=Y default → dbContextNameUpper=MAIN", () => {
147
167
  const r = normalize({ ...base, hasServer: true, hasDb: true, dbDialect: "mysql" });
148
168
  expect(r.dbContextNameUpper).toBe("MAIN");
@@ -169,4 +189,78 @@ describe("normalize", () => {
169
189
  });
170
190
  expect(r.dbContextNameUpper).toBe("MAIN");
171
191
  });
192
+
193
+ it("DB=Y default → dbFolderName=db-main", () => {
194
+ const r = normalize({ ...base, hasServer: true, hasDb: true, dbDialect: "mysql" });
195
+ expect(r.dbFolderName).toBe("db-main");
196
+ });
197
+
198
+ it("DB=Y dbContextName=userOrder → dbFolderName=db-user-order", () => {
199
+ const r = normalize({
200
+ ...base,
201
+ hasServer: true,
202
+ hasDb: true,
203
+ dbDialect: "mysql",
204
+ dbContextName: "userOrder",
205
+ });
206
+ expect(r.dbFolderName).toBe("db-user-order");
207
+ });
208
+
209
+ it("DB=N 시 dbFolderName 은 빈 문자열", () => {
210
+ expect(normalize(base).dbFolderName).toBe("");
211
+ });
212
+
213
+ it("DB=N 이면 hasAuth=Y 입력해도 강제 false", () => {
214
+ const r = normalize({ ...base, hasServer: true, hasDb: false, hasAuth: true });
215
+ expect(r.hasAuth).toBe(false);
216
+ });
217
+
218
+ it("DB=Y + hasAuth 미입력 → hasAuth=false", () => {
219
+ const r = normalize({ ...base, hasServer: true, hasDb: true, dbDialect: "mysql" });
220
+ expect(r.hasAuth).toBe(false);
221
+ expect(r.userEntityPascal).toBe("");
222
+ expect(r.userEntityCamel).toBe("");
223
+ expect(r.userEntityKebab).toBe("");
224
+ expect(r.userEntityLabel).toBe("");
225
+ });
226
+
227
+ it("hasAuth=Y default → user/사용자 파생", () => {
228
+ const r = normalize({ ...base, hasServer: true, hasDb: true, dbDialect: "mysql", hasAuth: true });
229
+ expect(r.hasAuth).toBe(true);
230
+ expect(r.userEntityPascal).toBe("User");
231
+ expect(r.userEntityCamel).toBe("user");
232
+ expect(r.userEntityKebab).toBe("user");
233
+ expect(r.userEntityLabel).toBe("사용자");
234
+ });
235
+
236
+ it("hasAuth=Y + userEntityName=employee/직원 → Employee/employee 파생", () => {
237
+ const r = normalize({
238
+ ...base,
239
+ hasServer: true,
240
+ hasDb: true,
241
+ dbDialect: "mysql",
242
+ hasAuth: true,
243
+ userEntityName: "employee",
244
+ userEntityLabel: "직원",
245
+ });
246
+ expect(r.userEntityPascal).toBe("Employee");
247
+ expect(r.userEntityCamel).toBe("employee");
248
+ expect(r.userEntityKebab).toBe("employee");
249
+ expect(r.userEntityLabel).toBe("직원");
250
+ });
251
+
252
+ it("hasAuth=Y + 복합어 userEntityName=staff-member → StaffMember/staffMember 파생", () => {
253
+ const r = normalize({
254
+ ...base,
255
+ hasServer: true,
256
+ hasDb: true,
257
+ dbDialect: "mysql",
258
+ hasAuth: true,
259
+ userEntityName: "staff-member",
260
+ userEntityLabel: "직원",
261
+ });
262
+ expect(r.userEntityPascal).toBe("StaffMember");
263
+ expect(r.userEntityCamel).toBe("staffMember");
264
+ expect(r.userEntityKebab).toBe("staff-member");
265
+ });
172
266
  });