@simplysm/orm-common 13.0.100 → 14.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 (246) hide show
  1. package/README.md +90 -147
  2. package/dist/create-db-context.d.ts +10 -10
  3. package/dist/create-db-context.js +312 -276
  4. package/dist/create-db-context.js.map +1 -6
  5. package/dist/ddl/column-ddl.d.ts +4 -4
  6. package/dist/ddl/column-ddl.js +41 -35
  7. package/dist/ddl/column-ddl.js.map +1 -6
  8. package/dist/ddl/initialize.d.ts +17 -17
  9. package/dist/ddl/initialize.js +200 -142
  10. package/dist/ddl/initialize.js.map +1 -6
  11. package/dist/ddl/relation-ddl.d.ts +6 -6
  12. package/dist/ddl/relation-ddl.js +55 -48
  13. package/dist/ddl/relation-ddl.js.map +1 -6
  14. package/dist/ddl/schema-ddl.d.ts +4 -4
  15. package/dist/ddl/schema-ddl.js +21 -15
  16. package/dist/ddl/schema-ddl.js.map +1 -6
  17. package/dist/ddl/table-ddl.d.ts +20 -20
  18. package/dist/ddl/table-ddl.js +139 -93
  19. package/dist/ddl/table-ddl.js.map +1 -6
  20. package/dist/define-db-context.js +10 -13
  21. package/dist/define-db-context.js.map +1 -6
  22. package/dist/errors/db-transaction-error.d.ts +15 -15
  23. package/dist/errors/db-transaction-error.d.ts.map +1 -1
  24. package/dist/errors/db-transaction-error.js +53 -19
  25. package/dist/errors/db-transaction-error.js.map +1 -6
  26. package/dist/exec/executable.d.ts +23 -23
  27. package/dist/exec/executable.js +94 -40
  28. package/dist/exec/executable.js.map +1 -6
  29. package/dist/exec/queryable.d.ts +97 -97
  30. package/dist/exec/queryable.js +1310 -1204
  31. package/dist/exec/queryable.js.map +1 -6
  32. package/dist/exec/search-parser.d.ts +31 -31
  33. package/dist/exec/search-parser.d.ts.map +1 -1
  34. package/dist/exec/search-parser.js +158 -59
  35. package/dist/exec/search-parser.js.map +1 -6
  36. package/dist/expr/expr-unit.d.ts +4 -4
  37. package/dist/expr/expr-unit.js +24 -18
  38. package/dist/expr/expr-unit.js.map +1 -6
  39. package/dist/expr/expr.d.ts +108 -108
  40. package/dist/expr/expr.js +1872 -1844
  41. package/dist/expr/expr.js.map +1 -6
  42. package/dist/index.js +23 -1
  43. package/dist/index.js.map +1 -6
  44. package/dist/models/system-migration.js +7 -7
  45. package/dist/models/system-migration.js.map +1 -6
  46. package/dist/query-builder/base/expr-renderer-base.d.ts +10 -10
  47. package/dist/query-builder/base/expr-renderer-base.js +27 -21
  48. package/dist/query-builder/base/expr-renderer-base.js.map +1 -6
  49. package/dist/query-builder/base/query-builder-base.d.ts +21 -21
  50. package/dist/query-builder/base/query-builder-base.d.ts.map +1 -1
  51. package/dist/query-builder/base/query-builder-base.js +90 -80
  52. package/dist/query-builder/base/query-builder-base.js.map +1 -6
  53. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +5 -5
  54. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
  55. package/dist/query-builder/mssql/mssql-expr-renderer.js +447 -420
  56. package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -6
  57. package/dist/query-builder/mssql/mssql-query-builder.d.ts +2 -2
  58. package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
  59. package/dist/query-builder/mssql/mssql-query-builder.js +483 -443
  60. package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -6
  61. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +5 -5
  62. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
  63. package/dist/query-builder/mysql/mysql-expr-renderer.js +451 -419
  64. package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -6
  65. package/dist/query-builder/mysql/mysql-query-builder.d.ts +10 -10
  66. package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
  67. package/dist/query-builder/mysql/mysql-query-builder.js +570 -479
  68. package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -6
  69. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +5 -5
  70. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
  71. package/dist/query-builder/postgresql/postgresql-expr-renderer.js +449 -422
  72. package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -6
  73. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +8 -8
  74. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
  75. package/dist/query-builder/postgresql/postgresql-query-builder.js +511 -460
  76. package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -6
  77. package/dist/query-builder/query-builder.d.ts +1 -1
  78. package/dist/query-builder/query-builder.js +13 -13
  79. package/dist/query-builder/query-builder.js.map +1 -6
  80. package/dist/schema/factory/column-builder.d.ts +84 -84
  81. package/dist/schema/factory/column-builder.js +248 -185
  82. package/dist/schema/factory/column-builder.js.map +1 -6
  83. package/dist/schema/factory/index-builder.d.ts +38 -38
  84. package/dist/schema/factory/index-builder.js +144 -85
  85. package/dist/schema/factory/index-builder.js.map +1 -6
  86. package/dist/schema/factory/relation-builder.d.ts +99 -99
  87. package/dist/schema/factory/relation-builder.d.ts.map +1 -1
  88. package/dist/schema/factory/relation-builder.js +274 -136
  89. package/dist/schema/factory/relation-builder.js.map +1 -6
  90. package/dist/schema/procedure-builder.d.ts +51 -51
  91. package/dist/schema/procedure-builder.d.ts.map +1 -1
  92. package/dist/schema/procedure-builder.js +205 -131
  93. package/dist/schema/procedure-builder.js.map +1 -6
  94. package/dist/schema/table-builder.d.ts +55 -55
  95. package/dist/schema/table-builder.d.ts.map +1 -1
  96. package/dist/schema/table-builder.js +274 -205
  97. package/dist/schema/table-builder.js.map +1 -6
  98. package/dist/schema/view-builder.d.ts +44 -44
  99. package/dist/schema/view-builder.d.ts.map +1 -1
  100. package/dist/schema/view-builder.js +189 -116
  101. package/dist/schema/view-builder.js.map +1 -6
  102. package/dist/types/column.d.ts +21 -21
  103. package/dist/types/column.js +60 -30
  104. package/dist/types/column.js.map +1 -6
  105. package/dist/types/db-context-def.d.ts +9 -9
  106. package/dist/types/db-context-def.js +2 -1
  107. package/dist/types/db-context-def.js.map +1 -6
  108. package/dist/types/db.d.ts +47 -47
  109. package/dist/types/db.js +15 -5
  110. package/dist/types/db.js.map +1 -6
  111. package/dist/types/expr.d.ts +81 -81
  112. package/dist/types/expr.d.ts.map +1 -1
  113. package/dist/types/expr.js +3 -1
  114. package/dist/types/expr.js.map +1 -6
  115. package/dist/types/query-def.d.ts +46 -46
  116. package/dist/types/query-def.d.ts.map +1 -1
  117. package/dist/types/query-def.js +31 -24
  118. package/dist/types/query-def.js.map +1 -6
  119. package/dist/utils/result-parser.d.ts +11 -11
  120. package/dist/utils/result-parser.js +362 -221
  121. package/dist/utils/result-parser.js.map +1 -6
  122. package/docs/core.md +117 -145
  123. package/docs/expression.md +186 -203
  124. package/docs/query-builder.md +75 -42
  125. package/docs/queryable.md +189 -151
  126. package/docs/schema-builders.md +172 -283
  127. package/docs/types.md +229 -173
  128. package/package.json +7 -5
  129. package/src/create-db-context.ts +31 -31
  130. package/src/ddl/column-ddl.ts +4 -4
  131. package/src/ddl/initialize.ts +38 -38
  132. package/src/ddl/relation-ddl.ts +6 -6
  133. package/src/ddl/schema-ddl.ts +4 -4
  134. package/src/ddl/table-ddl.ts +24 -24
  135. package/src/errors/db-transaction-error.ts +13 -13
  136. package/src/exec/executable.ts +25 -25
  137. package/src/exec/queryable.ts +152 -152
  138. package/src/exec/search-parser.ts +50 -50
  139. package/src/expr/expr-unit.ts +4 -4
  140. package/src/expr/expr.ts +118 -118
  141. package/src/index.ts +8 -8
  142. package/src/models/system-migration.ts +1 -1
  143. package/src/query-builder/base/expr-renderer-base.ts +21 -21
  144. package/src/query-builder/base/query-builder-base.ts +33 -33
  145. package/src/query-builder/mssql/mssql-expr-renderer.ts +28 -28
  146. package/src/query-builder/mssql/mssql-query-builder.ts +37 -37
  147. package/src/query-builder/mysql/mysql-expr-renderer.ts +29 -29
  148. package/src/query-builder/mysql/mysql-query-builder.ts +70 -70
  149. package/src/query-builder/postgresql/postgresql-expr-renderer.ts +22 -22
  150. package/src/query-builder/postgresql/postgresql-query-builder.ts +54 -54
  151. package/src/query-builder/query-builder.ts +1 -1
  152. package/src/schema/factory/column-builder.ts +86 -86
  153. package/src/schema/factory/index-builder.ts +38 -38
  154. package/src/schema/factory/relation-builder.ts +102 -102
  155. package/src/schema/procedure-builder.ts +52 -52
  156. package/src/schema/table-builder.ts +56 -56
  157. package/src/schema/view-builder.ts +47 -47
  158. package/src/types/column.ts +24 -24
  159. package/src/types/db-context-def.ts +15 -15
  160. package/src/types/db.ts +50 -50
  161. package/src/types/expr.ts +103 -103
  162. package/src/types/query-def.ts +50 -50
  163. package/src/utils/result-parser.ts +88 -88
  164. package/docs/utilities.md +0 -27
  165. package/tests/db-context/create-db-context.spec.ts +0 -193
  166. package/tests/db-context/define-db-context.spec.ts +0 -17
  167. package/tests/ddl/basic.expected.ts +0 -341
  168. package/tests/ddl/basic.spec.ts +0 -557
  169. package/tests/ddl/column-builder.expected.ts +0 -310
  170. package/tests/ddl/column-builder.spec.ts +0 -525
  171. package/tests/ddl/index-builder.expected.ts +0 -38
  172. package/tests/ddl/index-builder.spec.ts +0 -148
  173. package/tests/ddl/procedure-builder.expected.ts +0 -52
  174. package/tests/ddl/procedure-builder.spec.ts +0 -128
  175. package/tests/ddl/relation-builder.expected.ts +0 -36
  176. package/tests/ddl/relation-builder.spec.ts +0 -171
  177. package/tests/ddl/table-builder.expected.ts +0 -113
  178. package/tests/ddl/table-builder.spec.ts +0 -399
  179. package/tests/ddl/view-builder.expected.ts +0 -38
  180. package/tests/ddl/view-builder.spec.ts +0 -116
  181. package/tests/dml/delete.expected.ts +0 -96
  182. package/tests/dml/delete.spec.ts +0 -127
  183. package/tests/dml/insert.expected.ts +0 -192
  184. package/tests/dml/insert.spec.ts +0 -210
  185. package/tests/dml/update.expected.ts +0 -176
  186. package/tests/dml/update.spec.ts +0 -222
  187. package/tests/dml/upsert.expected.ts +0 -215
  188. package/tests/dml/upsert.spec.ts +0 -190
  189. package/tests/errors/queryable-errors.spec.ts +0 -126
  190. package/tests/escape.spec.ts +0 -59
  191. package/tests/examples/pivot.expected.ts +0 -211
  192. package/tests/examples/pivot.spec.ts +0 -200
  193. package/tests/examples/sampling.expected.ts +0 -69
  194. package/tests/examples/sampling.spec.ts +0 -42
  195. package/tests/examples/unpivot.expected.ts +0 -120
  196. package/tests/examples/unpivot.spec.ts +0 -161
  197. package/tests/exec/search-parser.spec.ts +0 -267
  198. package/tests/executable/basic.expected.ts +0 -18
  199. package/tests/executable/basic.spec.ts +0 -54
  200. package/tests/expr/comparison.expected.ts +0 -282
  201. package/tests/expr/comparison.spec.ts +0 -334
  202. package/tests/expr/conditional.expected.ts +0 -134
  203. package/tests/expr/conditional.spec.ts +0 -249
  204. package/tests/expr/date.expected.ts +0 -332
  205. package/tests/expr/date.spec.ts +0 -459
  206. package/tests/expr/math.expected.ts +0 -62
  207. package/tests/expr/math.spec.ts +0 -59
  208. package/tests/expr/string.expected.ts +0 -218
  209. package/tests/expr/string.spec.ts +0 -300
  210. package/tests/expr/utility.expected.ts +0 -147
  211. package/tests/expr/utility.spec.ts +0 -155
  212. package/tests/select/basic.expected.ts +0 -322
  213. package/tests/select/basic.spec.ts +0 -433
  214. package/tests/select/filter.expected.ts +0 -357
  215. package/tests/select/filter.spec.ts +0 -954
  216. package/tests/select/group.expected.ts +0 -169
  217. package/tests/select/group.spec.ts +0 -159
  218. package/tests/select/join.expected.ts +0 -582
  219. package/tests/select/join.spec.ts +0 -692
  220. package/tests/select/order.expected.ts +0 -150
  221. package/tests/select/order.spec.ts +0 -140
  222. package/tests/select/recursive-cte.expected.ts +0 -244
  223. package/tests/select/recursive-cte.spec.ts +0 -514
  224. package/tests/select/result-meta.spec.ts +0 -270
  225. package/tests/select/subquery.expected.ts +0 -363
  226. package/tests/select/subquery.spec.ts +0 -441
  227. package/tests/select/view.expected.ts +0 -155
  228. package/tests/select/view.spec.ts +0 -235
  229. package/tests/select/window.expected.ts +0 -345
  230. package/tests/select/window.spec.ts +0 -433
  231. package/tests/setup/MockExecutor.ts +0 -18
  232. package/tests/setup/TestDbContext.ts +0 -59
  233. package/tests/setup/models/Company.ts +0 -13
  234. package/tests/setup/models/Employee.ts +0 -10
  235. package/tests/setup/models/MonthlySales.ts +0 -11
  236. package/tests/setup/models/Post.ts +0 -16
  237. package/tests/setup/models/Sales.ts +0 -10
  238. package/tests/setup/models/User.ts +0 -19
  239. package/tests/setup/procedure/GetAllUsers.ts +0 -9
  240. package/tests/setup/procedure/GetUserById.ts +0 -12
  241. package/tests/setup/test-utils.ts +0 -72
  242. package/tests/setup/views/ActiveUsers.ts +0 -8
  243. package/tests/setup/views/UserSummary.ts +0 -11
  244. package/tests/types/nullable-queryable-record.spec.ts +0 -97
  245. package/tests/utils/result-parser-perf.spec.ts +0 -143
  246. package/tests/utils/result-parser.spec.ts +0 -667
@@ -5,19 +5,19 @@ import type { ResultMeta } from "../types/db";
5
5
  declare function setImmediate(callback: () => void): void;
6
6
 
7
7
  // ============================================
8
- // Type Parsers
8
+ // 타입 파서
9
9
  // ============================================
10
10
 
11
11
  /**
12
- * Parse value to specified type
12
+ * 값을 지정된 타입으로 파싱
13
13
  *
14
- * @param value - value to parse
15
- * @param type - target type (ColumnPrimitiveStr)
16
- * @returns parsed value
17
- * @throws Error if parsing fails
14
+ * @param value - 파싱할
15
+ * @param type - 대상 타입 (ColumnPrimitiveStr)
16
+ * @returns 파싱된
17
+ * @throws 파싱 실패 Error
18
18
  */
19
19
  function parseValue(value: unknown, type: ColumnPrimitiveStr): unknown {
20
- // null/undefined returned as-is (key removal handled by caller)
20
+ // null/undefined 그대로 반환 (key 제거는 호출자가 처리)
21
21
  if (value == null) {
22
22
  return undefined;
23
23
  }
@@ -26,7 +26,7 @@ function parseValue(value: unknown, type: ColumnPrimitiveStr): unknown {
26
26
  case "number": {
27
27
  const num = Number(value);
28
28
  if (Number.isNaN(num)) {
29
- throw new Error(`Failed to parse number: ${String(value)}`);
29
+ throw new Error(`숫자 파싱 실패: ${String(value)}`);
30
30
  }
31
31
  return num;
32
32
  }
@@ -35,7 +35,7 @@ function parseValue(value: unknown, type: ColumnPrimitiveStr): unknown {
35
35
  return String(value);
36
36
 
37
37
  case "boolean":
38
- // Handle 0, 1, "0", "1", true, false, etc.
38
+ // 0, 1, "0", "1", true, false 등 처리
39
39
  if (value === 0 || value === "0" || value === false) return false;
40
40
  if (value === 1 || value === "1" || value === true) return true;
41
41
  return Boolean(value);
@@ -56,22 +56,22 @@ function parseValue(value: unknown, type: ColumnPrimitiveStr): unknown {
56
56
  case "Bytes":
57
57
  if (value instanceof Uint8Array) return value;
58
58
  if (typeof value === "string") return bytes.fromHex(value);
59
- throw new Error(`Failed to parse Bytes: ${typeof value}`);
59
+ throw new Error(`Bytes 파싱 실패: ${typeof value}`);
60
60
  }
61
61
  }
62
62
 
63
63
  // ============================================
64
- // Grouping Utilities
64
+ // 그룹핑 유틸리티
65
65
  // ============================================
66
66
 
67
- /** Precomputed column metadata for flatToNested */
67
+ /** flatToNested용 사전 계산된 column 메타데이터 */
68
68
  interface ColumnInfo {
69
69
  key: string;
70
70
  type: ColumnPrimitiveStr;
71
- parts: string[] | undefined; // undefined for simple keys, string[] for nested keys
71
+ parts: string[] | undefined; // 단순 key는 undefined, 중첩 key는 string[]
72
72
  }
73
73
 
74
- /** Precompute column info once per unique columns object */
74
+ /** 고유한 columns 객체마다 column 정보를 번만 사전 계산 */
75
75
  function buildColumnInfos(columns: Record<string, ColumnPrimitiveStr>): ColumnInfo[] {
76
76
  return Object.entries(columns).map(([key, type]) => ({
77
77
  key,
@@ -81,7 +81,7 @@ function buildColumnInfos(columns: Record<string, ColumnPrimitiveStr>): ColumnIn
81
81
  }
82
82
 
83
83
  /**
84
- * Transform flat record to nested object
84
+ * 플랫 레코드를 중첩 객체로 변환
85
85
  *
86
86
  * @example
87
87
  * { "posts.id": 1, "posts.title": "Hi" } → { posts: { id: 1, title: "Hi" } }
@@ -96,11 +96,11 @@ function flatToNested(
96
96
  const rawValue = record[key];
97
97
  const parsedValue = parseValue(rawValue, type);
98
98
 
99
- // undefined values are not added as keys
99
+ // undefined 값은 key로 추가하지 않음
100
100
  if (parsedValue === undefined) continue;
101
101
 
102
102
  if (parts != null) {
103
- // Nested key: "posts.id" → { posts: { id: ... } }
103
+ // 중첩 key: "posts.id" → { posts: { id: ... } }
104
104
  let current = result;
105
105
  for (let i = 0; i < parts.length - 1; i++) {
106
106
  const part = parts[i];
@@ -111,7 +111,7 @@ function flatToNested(
111
111
  }
112
112
  current[parts[parts.length - 1]] = parsedValue;
113
113
  } else {
114
- // Simple key
114
+ // 단순 key
115
115
  result[key] = parsedValue;
116
116
  }
117
117
  }
@@ -120,48 +120,48 @@ function flatToNested(
120
120
  }
121
121
 
122
122
  /**
123
- * Check if object is empty (all values are undefined)
123
+ * 객체가 비어있는지 확인 (모든 값이 undefined)
124
124
  */
125
125
  function isEmptyObject(record: Record<string, unknown>): boolean {
126
126
  return Object.keys(record).length === 0;
127
127
  }
128
128
 
129
129
  // ============================================
130
- // Main Function
130
+ // 메인 함수
131
131
  // ============================================
132
132
 
133
- /** Yield interval: yield to event loop every N records */
133
+ /** 양보 간격: N개 레코드마다 이벤트 루프에 양보 */
134
134
  const YIELD_INTERVAL = 100;
135
135
 
136
- /** Event loop yield: setImmediate for Node.js, setTimeout fallback for browser */
136
+ /** 이벤트 루프 양보: Node.js는 setImmediate, 브라우저는 setTimeout 폴백 */
137
137
  const yieldToEventLoop: () => Promise<void> =
138
138
  typeof setImmediate !== "undefined"
139
139
  ? () => new Promise<void>((resolve) => setImmediate(resolve))
140
140
  : () => new Promise<void>((resolve) => setTimeout(resolve, 0));
141
141
 
142
142
  /**
143
- * Transform DB query result to TypeScript object via ResultMeta
143
+ * ResultMeta를 통해 DB 쿼리 결과를 TypeScript 객체로 변환
144
144
  *
145
- * @param rawResults - Raw result array from database
146
- * @param meta - Type transformation and JOIN structure information (required)
147
- * @returns Type-transformed and nested result array. Returns undefined if input is empty or no valid results
148
- * @throws Error if type parsing fails
145
+ * @param rawResults - 데이터베이스에서 반환된 원시 결과 배열
146
+ * @param meta - 타입 변환 JOIN 구조 정보 (필수)
147
+ * @returns 타입 변환 중첩된 결과 배열. 입력이 비어있거나 유효한 결과가 없으면 undefined 반환
148
+ * @throws 타입 파싱 실패 Error
149
149
  *
150
150
  * @remarks
151
- * - meta required: no need to call this function without meta (input = output)
152
- * - async only: no synchronous version provided for large-scale processing to allow external interrupts
153
- * - browser/node compatible: yields via setTimeout(resolve, 0)
154
- * - empty result handling: returns undefined if input array is empty or all records are empty objects after parsing
151
+ * - meta 필수: meta 없이는 함수를 호출할 필요 없음 (입력 = 출력)
152
+ * - async 전용: 대규모 처리 외부 인터럽트 허용을 위해 동기 버전 미제공
153
+ * - 브라우저/Node 호환: setTimeout(resolve, 0)으로 양보
154
+ * - 결과 처리: 입력 배열이 비어있거나 파싱 모든 레코드가 객체이면 undefined 반환
155
155
  *
156
156
  * @example
157
157
  * ```typescript
158
- * // 1. Simple type parsing
158
+ * // 1. 단순 타입 파싱
159
159
  * const raw = [{ id: "1", createdAt: "2026-01-07T10:00:00.000Z" }];
160
160
  * const meta = { columns: { id: "number", createdAt: "DateTime" }, joins: {} };
161
161
  * const result = await parseQueryResult(raw, meta);
162
162
  * // [{ id: 1, createdAt: DateTime(...) }]
163
163
  *
164
- * // 2. JOIN result nesting
164
+ * // 2. JOIN 결과 중첩
165
165
  * const raw = [
166
166
  * { id: 1, name: "User1", "posts.id": 10, "posts.title": "Post1" },
167
167
  * { id: 1, name: "User1", "posts.id": 11, "posts.title": "Post2" },
@@ -178,24 +178,24 @@ export async function parseQueryResult<TRecord>(
178
178
  rawResults: Record<string, unknown>[],
179
179
  meta: ResultMeta,
180
180
  ): Promise<TRecord[] | undefined> {
181
- // Handle empty input
181
+ // 입력 처리
182
182
  if (rawResults.length === 0) {
183
183
  return undefined;
184
184
  }
185
185
 
186
186
  const joinKeys = Object.keys(meta.joins);
187
187
 
188
- // No JOINs: simple type parsing only
188
+ // JOIN 없음: 단순 타입 파싱만 수행
189
189
  if (joinKeys.length === 0) {
190
190
  return parseSimpleRecords<TRecord>(rawResults, meta.columns);
191
191
  }
192
192
 
193
- // With JOINs: grouping + nesting
193
+ // JOIN 있음: 그룹핑 + 중첩
194
194
  return parseJoinedRecords<TRecord>(rawResults, meta);
195
195
  }
196
196
 
197
197
  /**
198
- * Parse simple records without JOINs
198
+ * JOIN이 없는 단순 레코드 파싱
199
199
  */
200
200
  async function parseSimpleRecords<TRecord>(
201
201
  rawResults: Record<string, unknown>[],
@@ -205,43 +205,43 @@ async function parseSimpleRecords<TRecord>(
205
205
  const results: Record<string, unknown>[] = [];
206
206
 
207
207
  for (let i = 0; i < rawResults.length; i++) {
208
- // Yield to event loop
208
+ // 이벤트 루프에 양보
209
209
  if (i > 0 && i % YIELD_INTERVAL === 0) {
210
210
  await yieldToEventLoop();
211
211
  }
212
212
 
213
213
  const parsed = flatToNested(rawResults[i], columnInfos);
214
214
 
215
- // Exclude empty objects
215
+ // 객체 제외
216
216
  if (!isEmptyObject(parsed)) {
217
217
  results.push(parsed);
218
218
  }
219
219
  }
220
220
 
221
- // Return undefined for empty arrays
221
+ // 배열은 undefined 반환
222
222
  return results.length > 0 ? (results as TRecord[]) : undefined;
223
223
  }
224
224
 
225
225
  /**
226
- * Sort JOIN keys by depth (shallower ones first)
226
+ * JOIN key를 깊이순으로 정렬 (얕은 우선)
227
227
  * "posts" (1) < "posts.comments" (2)
228
228
  */
229
229
  function sortJoinKeysByDepth(joinKeys: string[]): string[] {
230
230
  return [...joinKeys].sort((a, b) => {
231
231
  const depthA = a.split(".").length;
232
232
  const depthB = b.split(".").length;
233
- return depthA - depthB; // Shallower ones first
233
+ return depthA - depthB; // 얕은 우선
234
234
  });
235
235
  }
236
236
 
237
237
  /**
238
- * Parse records with JOINs (recursive grouping)
238
+ * JOIN이 있는 레코드 파싱 (재귀 그룹핑)
239
239
  */
240
240
  async function parseJoinedRecords<TRecord>(
241
241
  rawResults: Record<string, unknown>[],
242
242
  meta: ResultMeta,
243
243
  ): Promise<TRecord[] | undefined> {
244
- // 1. Transform all records to nested structure
244
+ // 1. 모든 레코드를 중첩 구조로 변환
245
245
  const columnInfos = buildColumnInfos(meta.columns);
246
246
  const nestedRecords: Record<string, unknown>[] = [];
247
247
  for (let i = 0; i < rawResults.length; i++) {
@@ -251,22 +251,22 @@ async function parseJoinedRecords<TRecord>(
251
251
  nestedRecords.push(flatToNested(rawResults[i], columnInfos));
252
252
  }
253
253
 
254
- // 2. Sort JOIN keys by depth (shallower ones first)
254
+ // 2. JOIN key를 깊이순으로 정렬 (얕은 우선)
255
255
  const sortedJoinKeys = sortJoinKeysByDepth(Object.keys(meta.joins));
256
256
 
257
- // 3. Recursively group from root level
257
+ // 3. 루트 레벨부터 재귀적으로 그룹핑
258
258
  const results = groupRecordsRecursively(nestedRecords, sortedJoinKeys, meta.joins, "");
259
259
 
260
- // 4. Filter empty results
260
+ // 4. 결과 필터링
261
261
  const filteredResults = results.filter((r) => !isEmptyObject(r));
262
262
 
263
263
  return filteredResults.length > 0 ? (filteredResults as TRecord[]) : undefined;
264
264
  }
265
265
 
266
266
  /**
267
- * Serialize group key to string (used as Map key)
267
+ * 그룹 key 문자열로 직렬화 (Map key로 사용)
268
268
  *
269
- * Custom serialization faster than JSON.stringify
269
+ * JSON.stringify보다 빠른 커스텀 직렬화
270
270
  */
271
271
  function serializeGroupKey(groupKey: Record<string, unknown>, cachedKeyOrder?: string[]): string {
272
272
  const keys = cachedKeyOrder ?? Object.keys(groupKey).sort((a, b) => a.localeCompare(b));
@@ -282,14 +282,14 @@ function serializeGroupKey(groupKey: Record<string, unknown>, cachedKeyOrder?: s
282
282
  }
283
283
 
284
284
  /**
285
- * Recursively group records for current path
285
+ * 현재 경로에 대해 레코드를 재귀적으로 그룹핑
286
286
  *
287
- * Achieves O(n) complexity with Map-based grouping
287
+ * Map 기반 그룹핑으로 O(n) 복잡도 달성
288
288
  *
289
- * @param records - Record array to group
290
- * @param allJoinKeys - All JOIN keys (sorted by depth)
291
- * @param joinsConfig - JOIN configuration
292
- * @param currentPath - Current path (e.g., "", "posts", "posts.comments")
289
+ * @param records - 그룹핑할 레코드 배열
290
+ * @param allJoinKeys - 모든 JOIN key (깊이순 정렬)
291
+ * @param joinsConfig - JOIN 설정
292
+ * @param currentPath - 현재 경로 (예: "", "posts", "posts.comments")
293
293
  */
294
294
  function groupRecordsRecursively(
295
295
  records: Record<string, unknown>[],
@@ -297,15 +297,15 @@ function groupRecordsRecursively(
297
297
  joinsConfig: Record<string, { isSingle: boolean }>,
298
298
  currentPath: string,
299
299
  ): Record<string, unknown>[] {
300
- // Find JOIN keys directly corresponding to current path
301
- // e.g., currentPath="" → ["posts", "company"]
302
- // e.g., currentPath="posts" → ["posts.comments"]
300
+ // 현재 경로에 직접 대응하는 JOIN key 찾기
301
+ // 예: currentPath="" → ["posts", "company"]
302
+ // 예: currentPath="posts" → ["posts.comments"]
303
303
  const childJoinKeys = allJoinKeys.filter((key) => {
304
304
  if (currentPath === "") {
305
- // Root level: keys without dots
305
+ // 루트 레벨: 점이 없는 key
306
306
  return !key.includes(".");
307
307
  } else {
308
- // Sublevel: current path + "." + key
308
+ // 하위 레벨: 현재 경로 + "." + key
309
309
  return (
310
310
  key.startsWith(currentPath + ".") && key.slice(currentPath.length + 1).indexOf(".") === -1
311
311
  );
@@ -313,21 +313,21 @@ function groupRecordsRecursively(
313
313
  });
314
314
 
315
315
  if (childJoinKeys.length === 0) {
316
- // No more JOINs to group
316
+ // 이상 그룹핑할 JOIN 없음
317
317
  return records;
318
318
  }
319
319
 
320
- // Map-based grouping (O(n) complexity)
320
+ // Map 기반 그룹핑 (O(n) 복잡도)
321
321
  const groupMap = new Map<string, Record<string, unknown>>();
322
322
 
323
- // Precompute join key exclusion set for O(1) lookup
323
+ // O(1) 조회를 위한 JOIN key 제외 집합 사전 계산
324
324
  const joinKeyExclusions = buildJoinKeyExclusionSet(childJoinKeys);
325
325
 
326
- // Key order caching (determined from first record and reused)
326
+ // Key 순서 캐싱 ( 번째 레코드에서 결정 재사용)
327
327
  let groupKeyOrder: string[] | undefined;
328
328
 
329
329
  for (const record of records) {
330
- // Extract and serialize group key (excluding JOIN keys)
330
+ // 그룹 key 추출 직렬화 (JOIN key 제외)
331
331
  const groupKey = extractGroupKey(record, joinKeyExclusions);
332
332
  if (groupKeyOrder == null) {
333
333
  groupKeyOrder = Object.keys(groupKey).sort((a, b) => a.localeCompare(b));
@@ -337,27 +337,27 @@ function groupRecordsRecursively(
337
337
  const existingGroup = groupMap.get(keyStr);
338
338
 
339
339
  if (existingGroup != null) {
340
- // Merge JOIN data to existing group
340
+ // 기존 그룹에 JOIN 데이터 병합
341
341
  for (const joinKey of childJoinKeys) {
342
342
  const localKey = currentPath === "" ? joinKey : joinKey.slice(currentPath.length + 1);
343
343
  mergeJoinData(existingGroup, record, localKey, joinsConfig[joinKey].isSingle);
344
344
  }
345
345
  } else {
346
- // Generate new group
346
+ // 그룹 생성
347
347
  const newGroup = { ...record };
348
348
 
349
- // Initialize each JOIN key as array or single object
349
+ // JOIN key 배열 또는 단일 객체로 초기화
350
350
  for (const joinKey of childJoinKeys) {
351
351
  const localKey = currentPath === "" ? joinKey : joinKey.slice(currentPath.length + 1);
352
352
  const joinData = newGroup[localKey] as Record<string, unknown> | undefined;
353
353
 
354
354
  if (joinData != null && !isEmptyObject(joinData)) {
355
355
  if (!joinsConfig[joinKey].isSingle) {
356
- // Transform to array
356
+ // 배열로 변환
357
357
  newGroup[localKey] = [joinData];
358
358
  }
359
359
  } else {
360
- // Delete key if data is empty
360
+ // 데이터가 비어있으면 key 삭제
361
361
  delete newGroup[localKey];
362
362
  }
363
363
  }
@@ -366,17 +366,17 @@ function groupRecordsRecursively(
366
366
  }
367
367
  }
368
368
 
369
- // Transform Map to array
369
+ // Map 배열로 변환
370
370
  const grouped = Array.from(groupMap.values());
371
371
 
372
- // Recursively process sublevel of each JOIN
372
+ // JOIN의 하위 레벨을 재귀적으로 처리
373
373
  for (const group of grouped) {
374
374
  for (const joinKey of childJoinKeys) {
375
375
  const localKey = currentPath === "" ? joinKey : joinKey.slice(currentPath.length + 1);
376
376
  const joinData = group[localKey];
377
377
 
378
378
  if (Array.isArray(joinData) && joinData.length > 0) {
379
- // Array case: process sublevel recursively
379
+ // 배열인 경우: 하위 레벨을 재귀적으로 처리
380
380
  group[localKey] = groupRecordsRecursively(
381
381
  joinData as Record<string, unknown>[],
382
382
  allJoinKeys,
@@ -384,7 +384,7 @@ function groupRecordsRecursively(
384
384
  joinKey,
385
385
  );
386
386
  } else if (joinData != null && typeof joinData === "object" && !Array.isArray(joinData)) {
387
- // Single object case (isSingle: true)
387
+ // 단일 객체인 경우 (isSingle: true)
388
388
  const processed = groupRecordsRecursively(
389
389
  [joinData as Record<string, unknown>],
390
390
  allJoinKeys,
@@ -398,7 +398,7 @@ function groupRecordsRecursively(
398
398
  }
399
399
  }
400
400
 
401
- // Remove __hashSet__ internal property (temporary property for duplicate checking)
401
+ // __hashSet__ 내부 속성 제거 (중복 검사용 임시 속성)
402
402
  for (const group of grouped) {
403
403
  for (const key of Object.keys(group)) {
404
404
  if (key.startsWith("__hashSet__")) {
@@ -411,13 +411,13 @@ function groupRecordsRecursively(
411
411
  }
412
412
 
413
413
  /**
414
- * Build a Set of keys that should be excluded from group key (join keys and their prefixes)
414
+ * 그룹 key에서 제외할 key의 Set 구성 (join key와 접두사)
415
415
  */
416
416
  function buildJoinKeyExclusionSet(joinKeys: string[]): Set<string> {
417
417
  const exclusions = new Set<string>();
418
418
  for (const jk of joinKeys) {
419
419
  exclusions.add(jk);
420
- // Also exclude parent paths (e.g., "posts" for join key "posts.comments")
420
+ // 상위 경로도 제외 (예: join key "posts.comments"에 대해 "posts")
421
421
  const parts = jk.split(".");
422
422
  for (let i = 1; i < parts.length; i++) {
423
423
  exclusions.add(parts.slice(0, i).join("."));
@@ -427,7 +427,7 @@ function buildJoinKeyExclusionSet(joinKeys: string[]): Set<string> {
427
427
  }
428
428
 
429
429
  /**
430
- * Extract group key from record excluding JOIN keys
430
+ * JOIN key 제외하고 레코드에서 그룹 key 추출
431
431
  */
432
432
  function extractGroupKey(
433
433
  record: Record<string, unknown>,
@@ -435,9 +435,9 @@ function extractGroupKey(
435
435
  ): Record<string, unknown> {
436
436
  const result: Record<string, unknown> = {};
437
437
  for (const [key, value] of Object.entries(record)) {
438
- // Only include non-JOIN keys
438
+ // JOIN이 아닌 key만 포함
439
439
  if (!joinKeyExclusions.has(key)) {
440
- // Only use primitive values (not object/array) as group key
440
+ // 프리미티브 값만 그룹 key로 사용 (객체/배열 제외)
441
441
  if (value == null || typeof value !== "object") {
442
442
  result[key] = value;
443
443
  }
@@ -447,7 +447,7 @@ function extractGroupKey(
447
447
  }
448
448
 
449
449
  /**
450
- * Merge JOIN data to existing group
450
+ * 기존 그룹에 JOIN 데이터 병합
451
451
  */
452
452
  function mergeJoinData(
453
453
  existingGroup: Record<string, unknown>,
@@ -458,29 +458,29 @@ function mergeJoinData(
458
458
  const newJoinData = newRecord[localKey] as Record<string, unknown> | undefined;
459
459
 
460
460
  if (newJoinData == null || isEmptyObject(newJoinData)) {
461
- return; // No data to merge
461
+ return; // 병합할 데이터 없음
462
462
  }
463
463
 
464
464
  const existingJoinData = existingGroup[localKey];
465
465
 
466
466
  if (isSingle) {
467
- // isSingle: true - error if data exists and values differ
467
+ // isSingle: true - 데이터가 존재하고 값이 다르면 에러
468
468
  if (existingJoinData != null) {
469
469
  if (!obj.equal(existingJoinData as Record<string, unknown>, newJoinData)) {
470
- throw new Error(`isSingle relationship '${localKey}' has multiple different results.`);
470
+ throw new Error(`isSingle 관계 '${localKey}' 여러 개의 다른 결과가 있습니다.`);
471
471
  }
472
472
  } else {
473
473
  existingGroup[localKey] = newJoinData;
474
474
  }
475
475
  } else {
476
- // isSingle: false → Add to array
476
+ // isSingle: false → 배열에 추가
477
477
  const hashSetKey = `__hashSet__${localKey}`;
478
478
  if (!Array.isArray(existingJoinData)) {
479
479
  existingGroup[localKey] = [newJoinData];
480
- // Initialize internal property for Set-based duplicate checking
480
+ // Set 기반 중복 검사용 내부 속성 초기화
481
481
  existingGroup[hashSetKey] = new Set([serializeGroupKey(newJoinData)]);
482
482
  } else {
483
- // Set-based duplicate checking (O(1))
483
+ // Set 기반 중복 검사 (O(1))
484
484
  const hashSet = existingGroup[hashSetKey] as Set<string> | undefined;
485
485
  const newHash = serializeGroupKey(newJoinData);
486
486
  if (hashSet != null) {
@@ -489,7 +489,7 @@ function mergeJoinData(
489
489
  existingJoinData.push(newJoinData);
490
490
  }
491
491
  } else {
492
- // Fallback without hashSet (legacy approach)
492
+ // hashSet 없는 폴백 (레거시 방식)
493
493
  const isDuplicate = existingJoinData.some((item) =>
494
494
  obj.equal(item as Record<string, unknown>, newJoinData),
495
495
  );
package/docs/utilities.md DELETED
@@ -1,27 +0,0 @@
1
- # Utilities
2
-
3
- ## `parseQueryResult`
4
-
5
- Transform raw DB query results to typed TypeScript objects via `ResultMeta`. Handles type parsing, nested object construction from flat JOIN results, and deduplication.
6
-
7
- ```typescript
8
- async function parseQueryResult<TRecord>(
9
- rawResults: Record<string, unknown>[],
10
- meta: ResultMeta,
11
- ): Promise<TRecord[] | undefined>;
12
- ```
13
-
14
- | Parameter | Type | Description |
15
- |-----------|------|-------------|
16
- | `rawResults` | `Record<string, unknown>[]` | Raw result array from database |
17
- | `meta` | `ResultMeta` | Type transformation and JOIN structure information |
18
-
19
- **Returns:** Type-transformed and nested result array. Returns `undefined` if input is empty or no valid results.
20
-
21
- ### Behavior
22
-
23
- - **Type parsing**: Converts raw values to TypeScript types based on `meta.columns` mapping (e.g., string to `DateTime`, number to boolean).
24
- - **JOIN nesting**: Converts flat `"posts.id"` keys into nested `{ posts: { id: ... } }` structures.
25
- - **Deduplication**: Groups records by non-JOIN columns and collects JOIN data into arrays (or single objects for `isSingle: true`).
26
- - **Async**: Yields to the event loop every 100 records to prevent blocking.
27
- - **Empty handling**: Returns `undefined` for empty input or all-empty parsed records.