@cartanova/qgrid-cli 1.0.2 → 1.0.4

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 (114) hide show
  1. package/bundle/dist/dist/_virtual/_@oxc-project_runtime@0.122.0/helpers/decorate.js +9 -0
  2. package/bundle/dist/dist/_virtual/_@oxc-project_runtime@0.122.0/helpers/decorateMetadata.js +6 -0
  3. package/bundle/dist/dist/_virtual/_rolldown/runtime.js +15 -0
  4. package/bundle/dist/dist/application/qgrid/oauth.js +140 -0
  5. package/bundle/dist/dist/application/qgrid/pool.js +100 -0
  6. package/bundle/dist/dist/application/qgrid/qgrid.frame.js +280 -0
  7. package/bundle/dist/dist/application/qgrid/qgrid.types.js +79 -0
  8. package/bundle/dist/dist/application/qgrid/worker.js +183 -0
  9. package/bundle/dist/dist/application/queries.generated.js +31 -0
  10. package/bundle/dist/dist/application/request-log/request-log.model.js +113 -0
  11. package/bundle/dist/dist/application/request-log/request-log.types.js +13 -0
  12. package/bundle/dist/dist/application/sonamu.generated.js +99 -0
  13. package/bundle/dist/dist/application/sonamu.generated.sso.js +34 -0
  14. package/bundle/dist/dist/application/token/token.model.js +157 -0
  15. package/bundle/dist/dist/application/token/token.types.js +22 -0
  16. package/bundle/dist/dist/i18n/en.js +63 -0
  17. package/bundle/dist/dist/i18n/ko.js +62 -0
  18. package/bundle/dist/dist/i18n/sd.generated.js +272 -0
  19. package/bundle/dist/dist/index.js +11 -0
  20. package/bundle/dist/dist/migrations/20260410171405_create__request_logs.js +26 -0
  21. package/bundle/dist/dist/migrations/20260410171406_create__tokens.js +24 -0
  22. package/bundle/dist/dist/migrations/20260411120624_alter_tokens_alter2.js +16 -0
  23. package/bundle/dist/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +155 -0
  24. package/bundle/dist/dist/node_modules/.pnpm/@vitest_expect@4.1.2/node_modules/@vitest/expect/dist/index.js +1484 -0
  25. package/bundle/dist/dist/node_modules/.pnpm/@vitest_pretty-format@4.1.2/node_modules/@vitest/pretty-format/dist/index.js +887 -0
  26. package/bundle/dist/dist/node_modules/.pnpm/@vitest_runner@4.1.2/node_modules/@vitest/runner/dist/chunk-artifact.js +2746 -0
  27. package/bundle/dist/dist/node_modules/.pnpm/@vitest_runner@4.1.2/node_modules/@vitest/runner/dist/index.js +8 -0
  28. package/bundle/dist/dist/node_modules/.pnpm/@vitest_runner@4.1.2/node_modules/@vitest/runner/dist/utils.js +8 -0
  29. package/bundle/dist/dist/node_modules/.pnpm/@vitest_snapshot@4.1.2/node_modules/@vitest/snapshot/dist/index.js +662 -0
  30. package/bundle/dist/dist/node_modules/.pnpm/@vitest_spy@4.1.2/node_modules/@vitest/spy/dist/index.js +386 -0
  31. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.js +82 -0
  32. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/constants.js +48 -0
  33. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/diff.js +1307 -0
  34. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/display.js +558 -0
  35. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/error.js +33 -0
  36. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/helpers.js +226 -0
  37. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/offset.js +27 -0
  38. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/serialize.js +77 -0
  39. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/source-map.js +374 -0
  40. package/bundle/dist/dist/node_modules/.pnpm/@vitest_utils@4.1.2/node_modules/@vitest/utils/dist/timers.js +37 -0
  41. package/bundle/dist/dist/node_modules/.pnpm/chai@6.2.2/node_modules/chai/index.js +2886 -0
  42. package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.js +10 -0
  43. package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.js +109 -0
  44. package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.js +10 -0
  45. package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.js +10 -0
  46. package/bundle/dist/dist/node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.js +10 -0
  47. package/bundle/dist/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +1127 -0
  48. package/bundle/dist/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/index.js +21 -0
  49. package/bundle/dist/dist/node_modules/.pnpm/pathe@2.0.3/node_modules/pathe/dist/shared/pathe.M-eThtNZ.js +432 -0
  50. package/bundle/dist/dist/node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.js +506 -0
  51. package/bundle/dist/dist/node_modules/.pnpm/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.js +86 -0
  52. package/bundle/dist/dist/node_modules/.pnpm/vite@8.0.3_@emnapi_core@1.9.2_@emnapi_runtime@1.9.2_@types_node@25.0.7_esbuild@0.27.4_jiti@2.6.1_sass@1.98.0_tsx@4.21.0/node_modules/vite/dist/node/module-runner.js +426 -0
  53. package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/_commonjsHelpers.D26ty3Ew.js +6 -0
  54. package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/benchmark.CX_oY03V.js +41 -0
  55. package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/evaluatedModules.Dg1zASAC.js +19 -0
  56. package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/index.Chj8NDwU.js +202 -0
  57. package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/index.DOa3dzoN.js +16 -0
  58. package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/rpc.MzXet3jl.js +59 -0
  59. package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/test.p_J6dB8a.js +2910 -0
  60. package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/chunks/utils.BX5Fg8C4.js +44 -0
  61. package/bundle/dist/dist/node_modules/.pnpm/vitest@4.1.2_@opentelemetry_api@1.9.0_@types_node@25.0.7_vite@8.0.3_@emnapi_core@1.9.2__bb31087278f97a39b31b92010beab980/node_modules/vitest/dist/index.js +28 -0
  62. package/bundle/dist/dist/sonamu.config.js +196 -0
  63. package/bundle/dist/dist/testing/fixture.js +8 -0
  64. package/bundle/dist/dist/testing/global.js +9 -0
  65. package/bundle/dist/dist/testing/setup-mocks.js +17 -0
  66. package/bundle/dist/dist/utils/subset-loaders.js +12 -0
  67. package/bundle/src/src/application/qgrid/oauth.ts +181 -0
  68. package/bundle/src/src/application/qgrid/pool.ts +132 -0
  69. package/bundle/src/src/application/qgrid/qgrid.frame.ts +275 -0
  70. package/bundle/src/src/application/qgrid/qgrid.types.ts +117 -0
  71. package/bundle/src/src/application/qgrid/worker.ts +234 -0
  72. package/bundle/src/src/application/queries.generated.ts +59 -0
  73. package/bundle/src/src/application/request-log/request-log.entity.json +75 -0
  74. package/bundle/src/src/application/request-log/request-log.model.ts +155 -0
  75. package/bundle/src/src/application/request-log/request-log.types.ts +15 -0
  76. package/bundle/src/src/application/sonamu.generated.http +187 -0
  77. package/bundle/src/src/application/sonamu.generated.sso.ts +66 -0
  78. package/bundle/src/src/application/sonamu.generated.ts +135 -0
  79. package/bundle/src/src/application/token/token.entity.json +37 -0
  80. package/bundle/src/src/application/token/token.model.ts +160 -0
  81. package/bundle/src/src/application/token/token.types.ts +26 -0
  82. package/bundle/src/src/i18n/en.ts +59 -0
  83. package/bundle/src/src/i18n/ko.ts +59 -0
  84. package/bundle/src/src/i18n/sd.generated.ts +384 -0
  85. package/bundle/src/src/index.ts +6 -0
  86. package/bundle/src/src/migrations/20260410171405_create__request_logs.ts +23 -0
  87. package/bundle/src/src/migrations/20260410171406_create__tokens.ts +21 -0
  88. package/bundle/src/src/migrations/20260411120624_alter_tokens_alter2.ts +15 -0
  89. package/bundle/src/src/sonamu.config.ts +248 -0
  90. package/bundle/src/src/testing/fixture.ts +6 -0
  91. package/bundle/src/src/testing/global.ts +6 -0
  92. package/bundle/src/src/testing/setup-mocks.ts +44 -0
  93. package/bundle/src/src/typings/fastify.d.ts +8 -0
  94. package/bundle/src/src/typings/sonamu.d.ts +17 -0
  95. package/bundle/src/src/utils/subset-loaders.ts +11 -0
  96. package/bundle/web-dist/web-dist/client/assets/index-DlPchHBY.js +116 -0
  97. package/bundle/web-dist/web-dist/client/assets/index-vZsvyuFn.css +1 -0
  98. package/bundle/web-dist/web-dist/client/assets/rolldown-runtime-Dw2cE7zH.js +1 -0
  99. package/bundle/web-dist/web-dist/client/assets/routes-CagDz23B.js +1 -0
  100. package/bundle/web-dist/web-dist/client/assets/sd.generated-BrQoQXvv.js +47 -0
  101. package/bundle/web-dist/web-dist/client/assets/services.generated-BspO2L0j.js +1 -0
  102. package/bundle/web-dist/web-dist/client/assets/show-aXUGa0gm.js +29 -0
  103. package/bundle/web-dist/web-dist/client/assets/tokens-DRkXtZ3Q.js +1 -0
  104. package/bundle/web-dist/web-dist/client/assets/vendor-react-DH4kUfK2.js +9 -0
  105. package/bundle/web-dist/web-dist/client/assets/vendor-tanstack-CUxlo6YN.js +1 -0
  106. package/bundle/web-dist/web-dist/client/index.html +18 -0
  107. package/bundle/web-dist/web-dist/server/assets/react-dom-CGIzOSuj.js +335 -0
  108. package/bundle/web-dist/web-dist/server/assets/routes-ClzReRep.js +490 -0
  109. package/bundle/web-dist/web-dist/server/assets/sd.generated-BnonqqKr.js +21694 -0
  110. package/bundle/web-dist/web-dist/server/assets/services.generated-D5ABD5fr.js +836 -0
  111. package/bundle/web-dist/web-dist/server/assets/show-C9pcdSxc.js +23364 -0
  112. package/bundle/web-dist/web-dist/server/assets/tokens-JH6FsnWZ.js +860 -0
  113. package/bundle/web-dist/web-dist/server/entry-server.generated.js +44646 -0
  114. package/package.json +10 -9
@@ -0,0 +1,59 @@
1
+ import { josa } from "sonamu/dict";
2
+
3
+ /**
4
+ * Project KO Dictionary
5
+ */
6
+ export default {
7
+ "common.all": "전체",
8
+ "common.backToList": "목록으로",
9
+ "common.cancel": "취소",
10
+ "common.close": "닫기",
11
+ "common.confirm": "확인",
12
+ "common.create": "생성",
13
+ "common.createdAt": "등록",
14
+ "common.delete": "삭제",
15
+ "common.edit": "수정",
16
+ "common.login": "로그인",
17
+ "common.logout": "로그아웃",
18
+ "common.manage": "관리",
19
+ "common.results": (count: number) => `${count}개 결과`,
20
+ "common.save": "저장",
21
+ "common.search": "검색",
22
+ "common.searchPlaceholder": "검색...",
23
+ "common.searchType": "검색 유형",
24
+ "common.sort": "정렬",
25
+ "confirm.delete": "정말 삭제하시겠습니까?",
26
+ "confirm.save": "저장하시겠습니까?",
27
+ "dashboard.adminMenu": "관리 메뉴",
28
+ "dashboard.createdAt": "가입일",
29
+ "dashboard.email": "이메일",
30
+ "dashboard.loginRequired": "로그인이 필요합니다.",
31
+ "dashboard.name": "이름",
32
+ "dashboard.role": "역할",
33
+ "dashboard.title": "관리자 대시보드",
34
+ "dashboard.welcome": "환영합니다!",
35
+ "delete.confirm.description": "이 작업은 취소할 수 없습니다. 항목이 영구적으로 삭제됩니다.",
36
+ "delete.confirm.title": "정말 삭제하시겠습니까?",
37
+ "entity.create": (name: string) => `${name} 생성`,
38
+ "entity.edit": (name: string, id: number) => `${name} 수정 (#${id})`,
39
+ "entity.list": (name: string) => `${name} 목록`,
40
+ "entity.listManage": (name: string) => `${name} 목록 관리`,
41
+ "error.alreadyProcessed": "이미 처리되었습니다",
42
+ "error.badRequest": "잘못된 요청입니다",
43
+ "error.duplicateRow": "중복된 데이터입니다",
44
+ "error.entityNotFound": (name: string, id: number | string) => `존재하지 않는 ${name} ID ${id}`,
45
+ "error.forbidden": "권한이 없습니다",
46
+ "error.internalServerError": "서버 오류가 발생했습니다",
47
+ "error.notFound": "찾을 수 없습니다",
48
+ "error.serviceUnavailable": "서비스를 사용할 수 없습니다",
49
+ "error.unauthorized": "인증이 필요합니다",
50
+ "error.unknownSearchField": (field: string) => `구현되지 않은 검색 필드 ${field}`,
51
+ notFound: (name: string, id: number) => `존재하지 않는 ${name} ID ${id}`,
52
+ "validation.email": "올바른 이메일 형식이 아닙니다",
53
+ "validation.maxLength": (field: string, max: number) =>
54
+ `${field}${josa(field, "은는")} 최대 ${max}자까지 입력할 수 있습니다`,
55
+ "validation.minLength": (field: string, min: number) =>
56
+ `${field}${josa(field, "은는")} 최소 ${min}자 이상이어야 합니다`,
57
+ "validation.required": (field: string) => `${josa(field, "은는")} 필수입니다`,
58
+ "validation.url": "올바른 URL 형식이 아닙니다",
59
+ } as const;
@@ -0,0 +1,384 @@
1
+ /**
2
+ * @generated
3
+ * 직접 수정하지 마세요.
4
+ */
5
+
6
+ import { Sonamu } from "sonamu";
7
+
8
+ const DEFAULT_LOCALE = "ko" as const;
9
+ const SUPPORTED_LOCALES = ["ko", "en"] as const;
10
+ function getCurrentLocale(): (typeof SUPPORTED_LOCALES)[number] {
11
+ try {
12
+ const ctx = Sonamu.getContext();
13
+ return (ctx.locale as (typeof SUPPORTED_LOCALES)[number]) ?? DEFAULT_LOCALE;
14
+ } catch (_) {
15
+ return DEFAULT_LOCALE;
16
+ }
17
+ }
18
+
19
+ import en from "./en";
20
+ import ko from "./ko";
21
+
22
+ // react-components i18n keys
23
+ const rcKeysKo = {
24
+ // AsyncSelect Component
25
+ "rc.asyncSelect.loading": "로딩 중...",
26
+ "rc.asyncSelect.noOptions": "옵션이 없습니다",
27
+ "rc.asyncSelect.noResults": "검색 결과가 없습니다",
28
+ "rc.asyncSelect.selectPlaceholder": "선택하세요...",
29
+
30
+ // Combobox Component
31
+ "rc.combobox.noResults": "검색 결과가 없습니다",
32
+ "rc.combobox.selectPlaceholder": "선택하세요...",
33
+
34
+ // DatePicker Component
35
+ "rc.datePicker.pickDate": "날짜 선택",
36
+ "rc.datePicker.placeholder": "날짜 선택",
37
+ "rc.datePicker.selectDate": "날짜 선택",
38
+
39
+ // DateSelectorMultiple Component
40
+ "rc.dateSelectorMultiple.addDate": "날짜 추가",
41
+ "rc.dateSelectorMultiple.dateRange": "기간",
42
+ "rc.dateSelectorMultiple.placeholder": "날짜 선택",
43
+ "rc.dateSelectorMultiple.singleDate": "단일",
44
+
45
+ // FileInput Component
46
+ "rc.fileInput.browseFiles": "파일 찾기",
47
+ "rc.fileInput.dropZone": "파일을 드래그하거나 클릭하여 업로드",
48
+ "rc.fileInput.filePlaceholder": "파일",
49
+ "rc.fileInput.imagePlaceholder": "이미지",
50
+ "rc.fileInput.maxFilesExceeded": (maxFiles: number) =>
51
+ `최대 ${maxFiles}개 파일만 업로드 가능합니다`,
52
+ "rc.fileInput.pending": "대기 중",
53
+ "rc.fileInput.remove": "제거",
54
+ "rc.fileInput.uploadFailed": "업로드 실패",
55
+ "rc.fileInput.uploading": "업로드 중...",
56
+
57
+ // MonthPickerMultiple Component
58
+ "rc.monthPickerMultiple.addMonth": "월 추가",
59
+ "rc.monthPickerMultiple.dateRange": "기간",
60
+ "rc.monthPickerMultiple.endDate": "종료일",
61
+ "rc.monthPickerMultiple.placeholder": "월 선택",
62
+ "rc.monthPickerMultiple.singleDate": "단일",
63
+ "rc.monthPickerMultiple.startDate": "시작일",
64
+
65
+ // MultiSelect Component
66
+ "rc.multiSelect.clear": "전체 해제",
67
+ "rc.multiSelect.close": "닫기",
68
+ "rc.multiSelect.moreItems": (count: number) => `+${count}개 더보기`,
69
+ "rc.multiSelect.noOptions": "옵션이 없습니다",
70
+ "rc.multiSelect.noOptionsSelected": "선택된 옵션이 없습니다",
71
+ "rc.multiSelect.noResults": "검색 결과가 없습니다",
72
+ "rc.multiSelect.optionsCount": (count: number) => `${count}개 옵션`,
73
+ "rc.multiSelect.selectAll": "전체 선택",
74
+ "rc.multiSelect.selectPlaceholder": "선택하세요...",
75
+
76
+ // Pagination Component
77
+ "rc.pagination.next": "다음",
78
+ "rc.pagination.previous": "이전",
79
+ "rc.pagination.showing": (start: number, end: number, total: number) =>
80
+ `${total}개 중 ${start}-${end}`,
81
+
82
+ // Calendar Component
83
+ "rc.calendar.month.0": "1월",
84
+ "rc.calendar.month.1": "2월",
85
+ "rc.calendar.month.2": "3월",
86
+ "rc.calendar.month.3": "4월",
87
+ "rc.calendar.month.4": "5월",
88
+ "rc.calendar.month.5": "6월",
89
+ "rc.calendar.month.6": "7월",
90
+ "rc.calendar.month.7": "8월",
91
+ "rc.calendar.month.8": "9월",
92
+ "rc.calendar.month.9": "10월",
93
+ "rc.calendar.month.10": "11월",
94
+ "rc.calendar.month.11": "12월",
95
+
96
+ // Sonamu Filter Component
97
+ "rc.sonamuFilter.title": "소나무 필터",
98
+ "rc.sonamuFilter.apply": "적용",
99
+ "rc.sonamuFilter.reset": "초기화",
100
+ "rc.sonamuFilter.addRule": "규칙 추가",
101
+ "rc.sonamuFilter.noRulesYet": '아직 규칙이 없습니다. "+ 규칙 추가"를 클릭하여 시작하세요.',
102
+ "rc.sonamuFilter.selectField": "필드 선택",
103
+ "rc.sonamuFilter.selectOperator": "연산자 선택",
104
+ "rc.sonamuFilter.selectOperatorFirst": "먼저 연산자를 선택하세요",
105
+ "rc.sonamuFilter.enterValue": "값 입력",
106
+ "rc.sonamuFilter.enterNumber": "숫자 입력",
107
+ "rc.sonamuFilter.notSupported": "지원하지 않음",
108
+ "rc.sonamuFilter.startDate": "시작일",
109
+ "rc.sonamuFilter.endDate": "종료일",
110
+ "rc.sonamuFilter.appliedFilters": "🌲 적용된 소나무 필터",
111
+
112
+ // Common
113
+ "rc.common.cancel": "취소",
114
+ "rc.common.save": "저장",
115
+ };
116
+
117
+ const rcKeysEn = {
118
+ // AsyncSelect Component
119
+ "rc.asyncSelect.loading": "Loading...",
120
+ "rc.asyncSelect.noOptions": "No options",
121
+ "rc.asyncSelect.noResults": "No results",
122
+ "rc.asyncSelect.selectPlaceholder": "Select...",
123
+
124
+ // Combobox Component
125
+ "rc.combobox.noResults": "No results",
126
+ "rc.combobox.selectPlaceholder": "Select...",
127
+
128
+ // DatePicker Component
129
+ "rc.datePicker.pickDate": "Pick date",
130
+ "rc.datePicker.placeholder": "Pick date",
131
+ "rc.datePicker.selectDate": "Select date",
132
+
133
+ // DateSelectorMultiple Component
134
+ "rc.dateSelectorMultiple.addDate": "Add date",
135
+ "rc.dateSelectorMultiple.dateRange": "Range",
136
+ "rc.dateSelectorMultiple.placeholder": "Pick date",
137
+ "rc.dateSelectorMultiple.singleDate": "Single",
138
+
139
+ // FileInput Component
140
+ "rc.fileInput.browseFiles": "Browse files",
141
+ "rc.fileInput.dropZone": "Drag files here or click to upload",
142
+ "rc.fileInput.filePlaceholder": "File",
143
+ "rc.fileInput.imagePlaceholder": "Image",
144
+ "rc.fileInput.maxFilesExceeded": (maxFiles: number) => `Maximum ${maxFiles} files allowed`,
145
+ "rc.fileInput.pending": "Pending",
146
+ "rc.fileInput.remove": "Remove",
147
+ "rc.fileInput.uploadFailed": "Upload failed",
148
+ "rc.fileInput.uploading": "Uploading...",
149
+
150
+ // MonthPickerMultiple Component
151
+ "rc.monthPickerMultiple.addMonth": "Add month",
152
+ "rc.monthPickerMultiple.dateRange": "Range",
153
+ "rc.monthPickerMultiple.endDate": "End date",
154
+ "rc.monthPickerMultiple.placeholder": "Pick month",
155
+ "rc.monthPickerMultiple.singleDate": "Single",
156
+ "rc.monthPickerMultiple.startDate": "Start date",
157
+
158
+ // MultiSelect Component
159
+ "rc.multiSelect.clear": "Clear all",
160
+ "rc.multiSelect.close": "Close",
161
+ "rc.multiSelect.moreItems": (count: number) => `+${count} more`,
162
+ "rc.multiSelect.noOptions": "No options",
163
+ "rc.multiSelect.noOptionsSelected": "No options selected",
164
+ "rc.multiSelect.noResults": "No results",
165
+ "rc.multiSelect.optionsCount": (count: number) => `${count} options`,
166
+ "rc.multiSelect.selectAll": "Select all",
167
+ "rc.multiSelect.selectPlaceholder": "Select...",
168
+
169
+ // Pagination Component
170
+ "rc.pagination.next": "Next",
171
+ "rc.pagination.previous": "Previous",
172
+ "rc.pagination.showing": (start: number, end: number, total: number) =>
173
+ `Showing ${start}-${end} of ${total}`,
174
+
175
+ // Calendar Component
176
+ "rc.calendar.month.0": "January",
177
+ "rc.calendar.month.1": "February",
178
+ "rc.calendar.month.2": "March",
179
+ "rc.calendar.month.3": "April",
180
+ "rc.calendar.month.4": "May",
181
+ "rc.calendar.month.5": "June",
182
+ "rc.calendar.month.6": "July",
183
+ "rc.calendar.month.7": "August",
184
+ "rc.calendar.month.8": "September",
185
+ "rc.calendar.month.9": "October",
186
+ "rc.calendar.month.10": "November",
187
+ "rc.calendar.month.11": "December",
188
+
189
+ // Sonamu Filter Component
190
+ "rc.sonamuFilter.title": "Sonamu Filter",
191
+ "rc.sonamuFilter.apply": "Apply",
192
+ "rc.sonamuFilter.reset": "Reset",
193
+ "rc.sonamuFilter.addRule": "Add Rule",
194
+ "rc.sonamuFilter.noRulesYet": 'No rules yet. Click "+ Add Rule" to start.',
195
+ "rc.sonamuFilter.selectField": "Select field",
196
+ "rc.sonamuFilter.selectOperator": "Operator",
197
+ "rc.sonamuFilter.selectOperatorFirst": "Select operator first",
198
+ "rc.sonamuFilter.enterValue": "Enter value",
199
+ "rc.sonamuFilter.enterNumber": "Enter number",
200
+ "rc.sonamuFilter.notSupported": "Not supported",
201
+ "rc.sonamuFilter.startDate": "Start date",
202
+ "rc.sonamuFilter.endDate": "End date",
203
+ "rc.sonamuFilter.appliedFilters": "🌲 Applied SonamuFilters",
204
+
205
+ // Common
206
+ "rc.common.cancel": "Cancel",
207
+ "rc.common.save": "Save",
208
+ } as const;
209
+
210
+ // entity.json에서 추출한 entity labels (defaultLocale 전용)
211
+ const entityLabels = {
212
+ "entity.RequestLog": "RequestLog",
213
+ "entity.RequestLog.id": "ID",
214
+ "entity.RequestLog.created_at": "등록일시",
215
+ "entity.RequestLog.token_name": "토큰 이름",
216
+ "entity.RequestLog.query": "prompt 전문",
217
+ "entity.RequestLog.response": "llm 응답 전문",
218
+ "entity.RequestLog.input_tokens": "입력 토큰",
219
+ "entity.RequestLog.output_tokens": "출력 토큰",
220
+ "entity.RequestLog.cache_read_tokens": "캐시 읽기 토큰",
221
+ "entity.RequestLog.cache_creation_tokens": "캐시 생성 토큰",
222
+ "entity.RequestLog.duration_ms": "응답 소요시간(ms)",
223
+ "enum.RequestLogOrderBy.id-desc": "ID최신순",
224
+ "enum.RequestLogSearchField.id": "ID",
225
+ "enum.RequestLogSearchField.token_name": "토큰이름",
226
+ "enum.RequestLogSearchField.query": "쿼리",
227
+ "entity.Token": "Token",
228
+ "entity.Token.id": "ID",
229
+ "entity.Token.created_at": "등록일시",
230
+ "entity.Token.token": "access_token",
231
+ "entity.Token.name": "토큰 이름",
232
+ "entity.Token.refresh_token": "refresh_token",
233
+ "entity.Token.expires_at": "만료 시각 (epoch ms)",
234
+ "entity.Token.account_uuid": "Anthropic 계정 UUID",
235
+ "entity.Token.active": "활성 여부",
236
+ "enum.TokenOrderBy.id-desc": "ID최신순",
237
+ "enum.TokenSearchField.id": "ID",
238
+ "enum.TokenSearchField.name": "이름",
239
+ } as const;
240
+
241
+ // defaultLocale의 dictionary를 기준으로 키 추출
242
+ type RCKeys = typeof rcKeysKo;
243
+ type ProjectDictionary = typeof ko;
244
+ type EntityLabels = typeof entityLabels;
245
+ type RawMergedDictionary = RCKeys &
246
+ Omit<EntityLabels, keyof (RCKeys & ProjectDictionary)> &
247
+ ProjectDictionary;
248
+
249
+ // 키는 유지하되, 값 타입은 string 또는 함수로 일반화 (다른 locale의 리터럴 타입 충돌 방지)
250
+ export type MergedDictionary = {
251
+ [K in keyof RawMergedDictionary]: RawMergedDictionary[K] extends (...args: infer P) => string
252
+ ? (...args: P) => string
253
+ : string;
254
+ };
255
+ export type DictKey = keyof MergedDictionary;
256
+ export type LocalizedString = string & { __brand: "LocalizedString" };
257
+
258
+ export function defineLocale(dict: Partial<MergedDictionary>) {
259
+ return dict;
260
+ }
261
+
262
+ // 각 locale별로 rc-keys + entity labels + 프로젝트 dict 합침
263
+ const dictionaries: Record<string, Partial<MergedDictionary>> = {
264
+ ko: { ...rcKeysKo, ...entityLabels, ...ko },
265
+ en: { ...rcKeysEn, ...en },
266
+ };
267
+
268
+ type SDReturnType<K extends DictKey> = MergedDictionary[K] extends (...args: infer P) => string
269
+ ? (...args: P) => LocalizedString
270
+ : LocalizedString;
271
+
272
+ function getDictValue<K extends DictKey>(key: K, locale: string): SDReturnType<K> {
273
+ // 1. 지정된 locale에서 조회
274
+ const dict = dictionaries[locale];
275
+ if (dict?.[key] !== undefined) {
276
+ return dict[key] as unknown as SDReturnType<K>;
277
+ }
278
+
279
+ // 2. default locale에서 조회
280
+ if (locale !== DEFAULT_LOCALE && dictionaries[DEFAULT_LOCALE]?.[key] !== undefined) {
281
+ return dictionaries[DEFAULT_LOCALE][key] as unknown as SDReturnType<K>;
282
+ }
283
+
284
+ // 3. supported locales 순회
285
+ for (const supportedLocale of SUPPORTED_LOCALES) {
286
+ if (supportedLocale !== locale && supportedLocale !== DEFAULT_LOCALE) {
287
+ if (dictionaries[supportedLocale]?.[key] !== undefined) {
288
+ return dictionaries[supportedLocale][key] as unknown as SDReturnType<K>;
289
+ }
290
+ }
291
+ }
292
+
293
+ // 4. 모두 실패 시 key 반환
294
+ return key as unknown as SDReturnType<K>;
295
+ }
296
+
297
+ /**
298
+ * Sonamu Dictionary 함수
299
+ * locale에 맞는 번역 텍스트를 반환합니다.
300
+ *
301
+ * @example
302
+ * SD("common.save") // → "저장" (LocalizedString)
303
+ * SD("user.notFound")(1) // → "존재하지 않는 User ID 1" (LocalizedString)
304
+ */
305
+ export function SD<K extends DictKey>(key: K): SDReturnType<K> {
306
+ const locale = getCurrentLocale();
307
+ return getDictValue(key, locale);
308
+ }
309
+
310
+ /**
311
+ * 특정 locale의 번역 텍스트를 반환하는 함수를 생성합니다.
312
+ *
313
+ * @example
314
+ * const EN = SD.locale("en");
315
+ * EN("common.save") // → "Save"
316
+ */
317
+ SD.locale =
318
+ (locale: (typeof SUPPORTED_LOCALES)[number]) =>
319
+ <K extends DictKey>(key: K): SDReturnType<K> => {
320
+ return getDictValue(key, locale);
321
+ };
322
+
323
+ // Localized 가능한 Column 타입 계산
324
+ type LocalizedBaseColumn<T> = {
325
+ [K in keyof T & string]: K extends `${infer Base}_${(typeof SUPPORTED_LOCALES)[number]}`
326
+ ? Base
327
+ : K;
328
+ }[keyof T & string];
329
+
330
+ /**
331
+ * locale에 따라 적절한 컬럼 값을 반환합니다.
332
+ * DB에 name, name_ko, name_en처럼 localized column이 있을 때 사용합니다.
333
+ *
334
+ * 우선순위 (지원 로케일은 ko/jp/en이고, 서비스의 기본 로케일은 ko, 사용자의 로케일은 jp일 때): column_jp → column → column_ko → column_en
335
+ * 우선순위 (지원 로케일은 ko/jp/en이고, 서비스의 기본 로케일은 en, 사용자의 로케일은 ko일 때): column_ko → column → column_en → column_jp
336
+ *
337
+ * @example
338
+ * localizedColumn(tag, "name")
339
+ */
340
+ export function localizedColumn<
341
+ T extends Record<string, unknown>,
342
+ K extends LocalizedBaseColumn<T>,
343
+ >(row: T, column: K): string | undefined {
344
+ const locale = getCurrentLocale();
345
+ const otherLocales = SUPPORTED_LOCALES.filter(
346
+ (l: string) => l !== locale && l !== DEFAULT_LOCALE,
347
+ );
348
+ const localizedKey = (column: K, locale: (typeof SUPPORTED_LOCALES)[number]) =>
349
+ `${column}_${locale}`;
350
+ const keys = [
351
+ localizedKey(column, locale),
352
+ column,
353
+ localizedKey(column, DEFAULT_LOCALE),
354
+ ...otherLocales.map((l) => localizedKey(column, l)),
355
+ ];
356
+
357
+ for (const key of keys) {
358
+ if (!(key in row)) {
359
+ continue;
360
+ }
361
+
362
+ if (row[key] !== null && row[key] !== undefined && row[key] !== "") {
363
+ return String(row[key]);
364
+ }
365
+ }
366
+
367
+ return undefined;
368
+ }
369
+
370
+ /**
371
+ * Enum의 localized labels를 Proxy로 반환합니다.
372
+ * Select 컴포넌트 등에서 EnumLabel[key] 대신 사용합니다.
373
+ *
374
+ * @example
375
+ * SD.enumLabels("TagOrderBy")[key] // → 현재 locale의 라벨
376
+ */
377
+ SD.enumLabels = (enumName: string): Record<string, LocalizedString> => {
378
+ return new Proxy({} as Record<string, LocalizedString>, {
379
+ get(_, key: string) {
380
+ const dictKey = `enum.${enumName}.${key}` as DictKey;
381
+ return getDictValue(dictKey, getCurrentLocale());
382
+ },
383
+ });
384
+ };
@@ -0,0 +1,6 @@
1
+ import { Sonamu } from "sonamu";
2
+
3
+ async function bootstrap() {
4
+ await Sonamu.createServer();
5
+ }
6
+ bootstrap();
@@ -0,0 +1,23 @@
1
+ import type { Knex } from "knex";
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.createTable("request_logs", (table) => {
5
+ table.increments().primary();
6
+ table
7
+ .timestamp("created_at", { useTz: true, precision: 3 })
8
+ .notNullable()
9
+ .defaultTo(knex.raw("CURRENT_TIMESTAMP"));
10
+ table.string("token_name", 100).notNullable();
11
+ table.text("query").notNullable();
12
+ table.text("response").notNullable();
13
+ table.integer("input_tokens").notNullable();
14
+ table.integer("output_tokens").notNullable();
15
+ table.integer("cache_read_tokens").notNullable();
16
+ table.integer("cache_creation_tokens").notNullable();
17
+ table.integer("duration_ms").notNullable();
18
+ });
19
+ }
20
+
21
+ export async function down(knex: Knex): Promise<void> {
22
+ return knex.schema.dropTable("request_logs");
23
+ }
@@ -0,0 +1,21 @@
1
+ import type { Knex } from "knex";
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.createTable("tokens", (table) => {
5
+ table.increments().primary();
6
+ table
7
+ .timestamp("created_at", { useTz: true, precision: 3 })
8
+ .notNullable()
9
+ .defaultTo(knex.raw("CURRENT_TIMESTAMP"));
10
+ table.text("token").notNullable();
11
+ table.text("name").nullable();
12
+ table.text("refresh_token").nullable();
13
+ table.bigInteger("expires_at").nullable();
14
+ table.text("account_uuid").nullable();
15
+ table.boolean("active").notNullable().defaultTo(knex.raw("true"));
16
+ });
17
+ }
18
+
19
+ export async function down(knex: Knex): Promise<void> {
20
+ return knex.schema.dropTable("tokens");
21
+ }
@@ -0,0 +1,15 @@
1
+ import type { Knex } from "knex";
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.alterTable("tokens", (table) => {
5
+ // alter column
6
+ table.text("name").notNullable().alter();
7
+ });
8
+ }
9
+
10
+ export async function down(knex: Knex): Promise<void> {
11
+ await knex.schema.alterTable("tokens", (table) => {
12
+ // rollback - alter column
13
+ table.text("name").nullable().alter();
14
+ });
15
+ }