@simplysm/orm-common 14.0.1 → 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 (59) hide show
  1. package/README.md +135 -0
  2. package/dist/exec/queryable.js +18 -18
  3. package/dist/exec/queryable.js.map +1 -1
  4. package/dist/expr/expr.d.ts +102 -102
  5. package/dist/expr/expr.js +105 -105
  6. package/dist/expr/expr.js.map +1 -1
  7. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +1 -1
  8. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
  9. package/dist/query-builder/mssql/mssql-expr-renderer.js +17 -17
  10. package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
  11. package/dist/query-builder/mssql/mssql-query-builder.d.ts +2 -2
  12. package/dist/query-builder/mssql/mssql-query-builder.d.ts.map +1 -1
  13. package/dist/query-builder/mssql/mssql-query-builder.js +26 -26
  14. package/dist/query-builder/mssql/mssql-query-builder.js.map +1 -1
  15. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +1 -1
  16. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
  17. package/dist/query-builder/mysql/mysql-expr-renderer.js +14 -14
  18. package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
  19. package/dist/query-builder/mysql/mysql-query-builder.d.ts +10 -10
  20. package/dist/query-builder/mysql/mysql-query-builder.d.ts.map +1 -1
  21. package/dist/query-builder/mysql/mysql-query-builder.js +67 -67
  22. package/dist/query-builder/mysql/mysql-query-builder.js.map +1 -1
  23. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +1 -1
  24. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
  25. package/dist/query-builder/postgresql/postgresql-expr-renderer.js +13 -13
  26. package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
  27. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts +8 -8
  28. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
  29. package/dist/query-builder/postgresql/postgresql-query-builder.js +47 -47
  30. package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
  31. package/dist/schema/factory/relation-builder.d.ts +8 -8
  32. package/dist/schema/factory/relation-builder.js +9 -9
  33. package/dist/schema/factory/relation-builder.js.map +1 -1
  34. package/dist/schema/view-builder.js +2 -2
  35. package/dist/schema/view-builder.js.map +1 -1
  36. package/dist/types/column.d.ts +21 -21
  37. package/dist/types/column.js +5 -5
  38. package/dist/utils/result-parser.d.ts +11 -11
  39. package/dist/utils/result-parser.js +48 -48
  40. package/dist/utils/result-parser.js.map +1 -1
  41. package/docs/core.md +206 -0
  42. package/docs/expression.md +217 -0
  43. package/docs/query-builder.md +126 -0
  44. package/docs/queryable.md +236 -0
  45. package/docs/schema-builders.md +352 -0
  46. package/docs/types.md +501 -0
  47. package/package.json +7 -3
  48. package/src/exec/queryable.ts +18 -18
  49. package/src/expr/expr.ts +105 -105
  50. package/src/query-builder/mssql/mssql-expr-renderer.ts +17 -17
  51. package/src/query-builder/mssql/mssql-query-builder.ts +26 -26
  52. package/src/query-builder/mysql/mysql-expr-renderer.ts +14 -14
  53. package/src/query-builder/mysql/mysql-query-builder.ts +67 -67
  54. package/src/query-builder/postgresql/postgresql-expr-renderer.ts +13 -13
  55. package/src/query-builder/postgresql/postgresql-query-builder.ts +47 -47
  56. package/src/schema/factory/relation-builder.ts +9 -9
  57. package/src/schema/view-builder.ts +2 -2
  58. package/src/types/column.ts +23 -23
  59. package/src/utils/result-parser.ts +49 -49
@@ -5,7 +5,7 @@ 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
  /**
@@ -61,7 +61,7 @@ function parseValue(value: unknown, type: ColumnPrimitiveStr): unknown {
61
61
  }
62
62
 
63
63
  // ============================================
64
- // Grouping Utilities
64
+ // 그룹핑 유틸리티
65
65
  // ============================================
66
66
 
67
67
  /** flatToNested용 사전 계산된 column 메타데이터 */
@@ -127,7 +127,7 @@ function isEmptyObject(record: Record<string, unknown>): boolean {
127
127
  }
128
128
 
129
129
  // ============================================
130
- // Main Function
130
+ // 메인 함수
131
131
  // ============================================
132
132
 
133
133
  /** 양보 간격: N개 레코드마다 이벤트 루프에 양보 */
@@ -140,28 +140,28 @@ const yieldToEventLoop: () => Promise<void> =
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,19 +178,19 @@ 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
 
@@ -241,7 +241,7 @@ 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,13 +251,13 @@ 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;
@@ -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__")) {
@@ -464,7 +464,7 @@ function mergeJoinData(
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
470
  throw new Error(`isSingle 관계 '${localKey}'에 여러 개의 다른 결과가 있습니다.`);
@@ -473,7 +473,7 @@ function mergeJoinData(
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];