@simplysm/orm-common 14.1.2 → 14.1.6

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 (89) hide show
  1. package/dist/db-context.d.ts +11 -10
  2. package/dist/db-context.d.ts.map +1 -1
  3. package/dist/db-context.js +14 -11
  4. package/dist/db-context.js.map +1 -1
  5. package/dist/ddl/initialize.js.map +1 -1
  6. package/dist/ddl/relation-ddl.d.ts +1 -1
  7. package/dist/ddl/relation-ddl.d.ts.map +1 -1
  8. package/dist/ddl/relation-ddl.js.map +1 -1
  9. package/dist/ddl/table-ddl.d.ts +3 -3
  10. package/dist/ddl/table-ddl.d.ts.map +1 -1
  11. package/dist/ddl/table-ddl.js.map +1 -1
  12. package/dist/errors/db-transaction-error.d.ts +4 -5
  13. package/dist/errors/db-transaction-error.d.ts.map +1 -1
  14. package/dist/errors/db-transaction-error.js +5 -6
  15. package/dist/errors/db-transaction-error.js.map +1 -1
  16. package/dist/exec/executable.d.ts +4 -13
  17. package/dist/exec/executable.d.ts.map +1 -1
  18. package/dist/exec/executable.js.map +1 -1
  19. package/dist/exec/queryable.d.ts +11 -9
  20. package/dist/exec/queryable.d.ts.map +1 -1
  21. package/dist/exec/queryable.js +9 -5
  22. package/dist/exec/queryable.js.map +1 -1
  23. package/dist/expr/expr.d.ts +7 -4
  24. package/dist/expr/expr.d.ts.map +1 -1
  25. package/dist/expr/expr.js +5 -2
  26. package/dist/expr/expr.js.map +1 -1
  27. package/dist/models/system-migration.d.ts +1 -1
  28. package/dist/models/system-migration.d.ts.map +1 -1
  29. package/dist/query-builder/base/expr-renderer-base.d.ts +2 -1
  30. package/dist/query-builder/base/expr-renderer-base.d.ts.map +1 -1
  31. package/dist/query-builder/base/expr-renderer-base.js.map +1 -1
  32. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts +2 -2
  33. package/dist/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -1
  34. package/dist/query-builder/mssql/mssql-expr-renderer.js.map +1 -1
  35. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts +2 -2
  36. package/dist/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -1
  37. package/dist/query-builder/mysql/mysql-expr-renderer.js.map +1 -1
  38. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts +2 -2
  39. package/dist/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -1
  40. package/dist/query-builder/postgresql/postgresql-expr-renderer.js.map +1 -1
  41. package/dist/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -1
  42. package/dist/query-builder/postgresql/postgresql-query-builder.js.map +1 -1
  43. package/dist/schema/factory/column-builder.d.ts +1 -1
  44. package/dist/schema/factory/column-builder.d.ts.map +1 -1
  45. package/dist/schema/factory/relation-builder.d.ts +55 -49
  46. package/dist/schema/factory/relation-builder.d.ts.map +1 -1
  47. package/dist/schema/factory/relation-builder.js +12 -15
  48. package/dist/schema/factory/relation-builder.js.map +1 -1
  49. package/dist/schema/table-builder.d.ts +35 -18
  50. package/dist/schema/table-builder.d.ts.map +1 -1
  51. package/dist/schema/table-builder.js +32 -15
  52. package/dist/schema/table-builder.js.map +1 -1
  53. package/dist/schema/view-builder.d.ts +28 -24
  54. package/dist/schema/view-builder.d.ts.map +1 -1
  55. package/dist/schema/view-builder.js +18 -14
  56. package/dist/schema/view-builder.js.map +1 -1
  57. package/dist/types/column.d.ts +6 -17
  58. package/dist/types/column.d.ts.map +1 -1
  59. package/dist/types/column.js +3 -19
  60. package/dist/types/column.js.map +1 -1
  61. package/dist/types/db-context-def.d.ts +7 -7
  62. package/dist/types/db-context-def.d.ts.map +1 -1
  63. package/dist/types/query-def.d.ts +1 -1
  64. package/dist/types/query-def.d.ts.map +1 -1
  65. package/dist/utils/result-parser.d.ts.map +1 -1
  66. package/dist/utils/result-parser.js +3 -7
  67. package/dist/utils/result-parser.js.map +1 -1
  68. package/package.json +3 -3
  69. package/src/db-context.ts +23 -20
  70. package/src/ddl/initialize.ts +4 -4
  71. package/src/ddl/relation-ddl.ts +1 -1
  72. package/src/ddl/table-ddl.ts +3 -3
  73. package/src/errors/db-transaction-error.ts +6 -4
  74. package/src/exec/executable.ts +5 -4
  75. package/src/exec/queryable.ts +31 -24
  76. package/src/expr/expr.ts +10 -4
  77. package/src/query-builder/base/expr-renderer-base.ts +2 -1
  78. package/src/query-builder/mssql/mssql-expr-renderer.ts +5 -5
  79. package/src/query-builder/mysql/mysql-expr-renderer.ts +2 -2
  80. package/src/query-builder/postgresql/postgresql-expr-renderer.ts +5 -5
  81. package/src/query-builder/postgresql/postgresql-query-builder.ts +4 -3
  82. package/src/schema/factory/column-builder.ts +3 -3
  83. package/src/schema/factory/relation-builder.ts +350 -346
  84. package/src/schema/table-builder.ts +209 -193
  85. package/src/schema/view-builder.ts +148 -143
  86. package/src/types/column.ts +7 -26
  87. package/src/types/db-context-def.ts +7 -7
  88. package/src/types/query-def.ts +1 -1
  89. package/src/utils/result-parser.ts +11 -19
@@ -1,346 +1,350 @@
1
- import { type InferColumns } from "./column-builder";
2
- import type { TableBuilder } from "../table-builder";
3
- import type { ViewBuilder } from "../view-builder";
4
-
5
- // ============================================
6
- // ForeignKeyBuilder
7
- // ============================================
8
-
9
- /**
10
- * Foreign Key 관계 builder (N:1)
11
- *
12
- * 현재 Table에서 대상 Table로의 FK 관계를 정의
13
- * DB에 실제 FK 제약조건을 생성
14
- *
15
- * description 설정은 factory 함수의 opts 파라미터로 전달한다.
16
- * 메서드 체이닝(.description())은 TypeScript 순환 참조 시 TS7022를 유발하므로 제거됨.
17
- *
18
- * @template TOwner - 소유 Table builder 타입
19
- * @template TTargetFn - 대상 Table builder factory 타입
20
- *
21
- * @see {@link ForeignKeyTargetBuilder} 역참조 builder
22
- * @see {@link RelationKeyBuilder} DB FK 없는 관계
23
- */
24
- export class ForeignKeyBuilder<
25
- TOwner extends TableBuilder<any, any, any>,
26
- TTargetFn extends () => TableBuilder<any, any, any>,
27
- > {
28
- /**
29
- * @param meta - FK 메타데이터
30
- * @param meta.ownerFn - 소유 Table factory
31
- * @param meta.columns - FK column 이름 배열
32
- * @param meta.targetFn - 대상 Table factory
33
- * @param meta.description - 관계 설명
34
- */
35
- constructor(
36
- readonly meta: {
37
- ownerFn: () => TOwner;
38
- columns: string[];
39
- targetFn: TTargetFn;
40
- description?: string;
41
- },
42
- ) {}
43
- }
44
-
45
- /**
46
- * Foreign Key 역참조 builder (1:N)
47
- *
48
- * 다른 Table이 현재 Table을 참조하는 FK의 역참조를 정의
49
- * include() 시 배열로 로드됨 (opts.single: true 시 단일 객체)
50
- *
51
- * description, single 설정은 factory 함수의 opts 파라미터로 전달한다.
52
- * 메서드 체이닝(.description(), .single())은 TypeScript 순환 참조 시 TS7022를 유발하므로 제거됨.
53
- *
54
- * @template TTargetTableFn - 참조하는 Table builder factory 타입
55
- * @template TIsSingle - 단일 객체 여부
56
- *
57
- * @see {@link ForeignKeyBuilder} FK builder
58
- */
59
- export class ForeignKeyTargetBuilder<
60
- TTargetTableFn extends () => TableBuilder<any, any, any>,
61
- TIsSingle extends boolean,
62
- > {
63
- /**
64
- * @param meta - FK 역참조 메타데이터
65
- * @param meta.targetTableFn - 참조하는 Table factory
66
- * @param meta.relationName - 참조하는 Table의 FK 관계 이름
67
- * @param meta.description - 관계 설명
68
- * @param meta.isSingle - 단일 객체 여부
69
- */
70
- constructor(
71
- readonly meta: {
72
- targetTableFn: TTargetTableFn;
73
- relationName: string;
74
- description?: string;
75
- isSingle?: TIsSingle;
76
- },
77
- ) {}
78
- }
79
-
80
- // ============================================
81
- // RelationKeyBuilder (FK와 동일하지만 DB FK 등록하지 않음)
82
- // ============================================
83
-
84
- /**
85
- * 논리적 관계 builder (N:1) - DB FK 미생성
86
- *
87
- * ForeignKeyBuilder와 동일하지만 DB에 FK 제약조건을 생성하지 않음
88
- * View에서도 사용 가능
89
- *
90
- * description 설정은 factory 함수의 opts 파라미터로 전달한다.
91
- *
92
- * @template TOwner - 소유 Table/View builder 타입
93
- * @template TTargetFn - 대상 Table/View builder factory 타입
94
- *
95
- * @see {@link ForeignKeyBuilder} DB FK 생성 버전
96
- */
97
- export class RelationKeyBuilder<
98
- TOwner extends TableBuilder<any, any, any> | ViewBuilder<any, any, any>,
99
- TTargetFn extends () => TableBuilder<any, any, any> | ViewBuilder<any, any, any>,
100
- > {
101
- /**
102
- * @param meta - 관계 메타데이터
103
- * @param meta.ownerFn - 소유 Table/View factory
104
- * @param meta.columns - 관계 column 이름 배열
105
- * @param meta.targetFn - 대상 Table/View factory
106
- * @param meta.description - 관계 설명
107
- */
108
- constructor(
109
- readonly meta: {
110
- ownerFn: () => TOwner;
111
- columns: string[];
112
- targetFn: TTargetFn;
113
- description?: string;
114
- },
115
- ) {}
116
- }
117
-
118
- /**
119
- * 논리적 관계 역참조 builder (1:N) - DB FK 미생성
120
- *
121
- * ForeignKeyTargetBuilder와 동일하지만 DB에 FK 제약조건을 생성하지 않음
122
- * View에서도 사용 가능
123
- *
124
- * description, single 설정은 factory 함수의 opts 파라미터로 전달한다.
125
- *
126
- * @template TTargetTableFn - 참조하는 Table/View builder factory 타입
127
- * @template TIsSingle - 단일 객체 여부
128
- *
129
- * @see {@link ForeignKeyTargetBuilder} DB FK 생성 버전
130
- */
131
- export class RelationKeyTargetBuilder<
132
- TTargetTableFn extends () => TableBuilder<any, any, any> | ViewBuilder<any, any, any>,
133
- TIsSingle extends boolean,
134
- > {
135
- /**
136
- * @param meta - 관계 역참조 메타데이터
137
- * @param meta.targetTableFn - 참조하는 Table/View factory
138
- * @param meta.relationName - 참조하는 Table/View의 관계 이름
139
- * @param meta.description - 관계 설명
140
- * @param meta.isSingle - 단일 객체 여부
141
- */
142
- constructor(
143
- readonly meta: {
144
- targetTableFn: TTargetTableFn;
145
- relationName: string;
146
- description?: string;
147
- isSingle?: TIsSingle;
148
- },
149
- ) {}
150
- }
151
-
152
- /**
153
- * FK 관계 factory 타입 (table 전용)
154
- *
155
- * @template TOwner - 소유 Table builder 타입
156
- * @template TColumnKey - Column key 타입
157
- */
158
- type RelationFkFactory<TOwner extends TableBuilder<any, any, any>, TColumnKey extends string> = {
159
- /** N:1 FK 관계 정의 (DB FK 생성) */
160
- foreignKey<TTargetFn extends () => TableBuilder<any, any, any>>(
161
- columns: TColumnKey[],
162
- targetFn: TTargetFn,
163
- opts?: { description?: string },
164
- ): ForeignKeyBuilder<TOwner, TTargetFn>;
165
- /** 1:N FK 역참조 정의 (single: true → 단일 객체) */
166
- foreignKeyTarget<TTargetTableFn extends () => TableBuilder<any, any, any>>(
167
- targetTableFn: TTargetTableFn,
168
- relationName: string,
169
- opts: { single: true; description?: string },
170
- ): ForeignKeyTargetBuilder<TTargetTableFn, true>;
171
- foreignKeyTarget<TTargetTableFn extends () => TableBuilder<any, any, any>>(
172
- targetTableFn: TTargetTableFn,
173
- relationName: string,
174
- opts?: { single?: false; description?: string },
175
- ): ForeignKeyTargetBuilder<TTargetTableFn, false>;
176
- };
177
-
178
- /**
179
- * 논리적 관계 factory 타입 (table/View 공용)
180
- *
181
- * @template TOwner - 소유 Table/View builder 타입
182
- * @template TColumnKey - Column key 타입
183
- */
184
- type RelationRkFactory<
185
- TOwner extends TableBuilder<any, any, any> | ViewBuilder<any, any, any>,
186
- TColumnKey extends string,
187
- > = {
188
- /** N:1 논리적 관계 정의 (DB FK 미생성) */
189
- relationKey<TTargetFn extends () => TableBuilder<any, any, any> | ViewBuilder<any, any, any>>(
190
- columns: TColumnKey[],
191
- targetFn: TTargetFn,
192
- opts?: { description?: string },
193
- ): RelationKeyBuilder<TOwner, TTargetFn>;
194
- /** 1:N 논리적 역참조 정의 (single: true → 단일 객체) */
195
- relationKeyTarget<
196
- TTargetTableFn extends () => TableBuilder<any, any, any> | ViewBuilder<any, any, any>,
197
- >(
198
- targetTableFn: TTargetTableFn,
199
- relationName: string,
200
- opts: { single: true; description?: string },
201
- ): RelationKeyTargetBuilder<TTargetTableFn, true>;
202
- relationKeyTarget<
203
- TTargetTableFn extends () => TableBuilder<any, any, any> | ViewBuilder<any, any, any>,
204
- >(
205
- targetTableFn: TTargetTableFn,
206
- relationName: string,
207
- opts?: { single?: false; description?: string },
208
- ): RelationKeyTargetBuilder<TTargetTableFn, false>;
209
- };
210
-
211
- /**
212
- * 관계 builder factory 생성
213
- *
214
- * TableBuilder.relations()와 ViewBuilder.relations()에서 사용
215
- * Table은 FK + RelationKey 모두 사용 가능, View는 RelationKey만 사용 가능
216
- *
217
- * @template TOwner - 소유 Table/View builder 타입
218
- * @template TColumnKey - Column key 타입
219
- * @param ownerFn - 소유 Table/View factory 함수
220
- * @returns 관계 builder factory
221
- */
222
- export function createRelationFactory<
223
- TOwner extends TableBuilder<any, any, any> | ViewBuilder<any, any, any>,
224
- TColumnKey extends string,
225
- >(
226
- ownerFn: () => TOwner,
227
- ): TOwner extends TableBuilder<any, any, any>
228
- ? RelationFkFactory<TOwner, TColumnKey> & RelationRkFactory<TOwner, TColumnKey>
229
- : RelationRkFactory<TOwner, TColumnKey> {
230
- return {
231
- foreignKey(columns, targetFn, opts?) {
232
- return new ForeignKeyBuilder({
233
- ownerFn: ownerFn as () => TableBuilder<any, any, any>,
234
- columns,
235
- targetFn,
236
- description: opts?.description,
237
- });
238
- },
239
- foreignKeyTarget(targetTableFn, relationName, opts?) {
240
- return new ForeignKeyTargetBuilder({
241
- targetTableFn,
242
- relationName,
243
- description: opts?.description,
244
- isSingle: opts?.single,
245
- });
246
- },
247
- relationKey(columns, targetFn, opts?) {
248
- return new RelationKeyBuilder({
249
- ownerFn: ownerFn,
250
- columns,
251
- targetFn,
252
- description: opts?.description,
253
- });
254
- },
255
- relationKeyTarget(targetTableFn, relationName, opts?) {
256
- return new RelationKeyTargetBuilder({
257
- targetTableFn,
258
- relationName,
259
- description: opts?.description,
260
- isSingle: opts?.single,
261
- });
262
- },
263
- } as TOwner extends TableBuilder<any, any, any>
264
- ? RelationFkFactory<TOwner, TColumnKey> & RelationRkFactory<TOwner, TColumnKey>
265
- : RelationRkFactory<TOwner, TColumnKey>;
266
- }
267
-
268
- // ============================================
269
- // builder 레코드
270
- // ============================================
271
-
272
- /**
273
- * 관계 builder 레코드 타입
274
- *
275
- * TableBuilder.relations()와 ViewBuilder.relations()의 반환 타입
276
- */
277
- export type RelationBuilderRecord = Record<
278
- string,
279
- | ForeignKeyBuilder<any, any>
280
- | ForeignKeyTargetBuilder<any, any>
281
- | RelationKeyBuilder<any, any>
282
- | RelationKeyTargetBuilder<any, any>
283
- >;
284
-
285
- // ============================================
286
- // Infer - 관계 타입 추론
287
- // ============================================
288
-
289
- /**
290
- * FK/RelationKey에서 대상 타입 추출 (단일 객체)
291
- *
292
- * N:1 관계의 대상 타입
293
- *
294
- * @template T - FK 또는 RelationKey builder 타입
295
- */
296
- export type ExtractRelationTarget<TRelation, TVisited extends string = never> = TRelation extends
297
- | ForeignKeyBuilder<any, infer TTargetFn>
298
- | RelationKeyBuilder<any, infer TTargetFn>
299
- ? ReturnType<TTargetFn> extends TableBuilder<infer TName, infer TCols, infer TRels>
300
- ? TName extends TVisited
301
- ? InferColumns<TCols>
302
- : InferColumns<TCols> & InferDeepRelations<TRels, TVisited | TName>
303
- : ReturnType<TTargetFn> extends ViewBuilder<any, infer TData, infer TRels>
304
- ? TData & InferDeepRelations<TRels, TVisited>
305
- : never
306
- : never;
307
-
308
- /**
309
- * FKTarget/RelationKeyTarget에서 대상 타입 추출 (배열 또는 단일 객체)
310
- *
311
- * 1:N 관계의 대상 타입 (opts.single: true 시 단일 객체)
312
- * TTargetTableFn: 순환 참조 방지를 위한 지연 평가용 () => Post 형태
313
- *
314
- * @template T - FKTarget 또는 RelationKeyTarget builder 타입
315
- */
316
- export type ExtractRelationTargetResult<TRelation, TVisited extends string = never> =
317
- TRelation extends
318
- | ForeignKeyTargetBuilder<infer TTargetTableFn, infer TIsSingle>
319
- | RelationKeyTargetBuilder<infer TTargetTableFn, infer TIsSingle>
320
- ? ReturnType<TTargetTableFn> extends TableBuilder<infer TName, infer TCols, infer TRels>
321
- ? TName extends TVisited
322
- ? TIsSingle extends true
323
- ? InferColumns<TCols>
324
- : InferColumns<TCols>[]
325
- : TIsSingle extends true
326
- ? InferColumns<TCols> & InferDeepRelations<TRels, TVisited | TName>
327
- : (InferColumns<TCols> & InferDeepRelations<TRels, TVisited | TName>)[]
328
- : ReturnType<TTargetTableFn> extends ViewBuilder<any, infer TData, infer TRels>
329
- ? TIsSingle extends true
330
- ? TData & InferDeepRelations<TRels, TVisited>
331
- : (TData & InferDeepRelations<TRels, TVisited>)[]
332
- : never
333
- : never;
334
-
335
- /**
336
- * 관계 정의에서 심층 관계 타입 추론
337
- *
338
- * include() 없이 접근 undefined가 되도록 모든 관계를 optional로 설정
339
- *
340
- * @template TRelations - 관계 builder 레코드 타입
341
- */
342
- export type InferDeepRelations<TRelations extends RelationBuilderRecord, TVisited extends string = never> = {
343
- [K in keyof TRelations]?:
344
- | ExtractRelationTarget<TRelations[K], TVisited>
345
- | ExtractRelationTargetResult<TRelations[K], TVisited>;
346
- };
1
+ import { type InferColumns } from "./column-builder";
2
+ import type { TableBuilder } from "../table-builder";
3
+ import type { ViewBuilder } from "../view-builder";
4
+
5
+ // ============================================
6
+ // ForeignKeyBuilder
7
+ // ============================================
8
+
9
+ /**
10
+ * Foreign Key 관계 builder (N:1)
11
+ *
12
+ * 현재 Table에서 대상 Table로의 FK 관계를 정의
13
+ * DB에 실제 FK 제약조건을 생성
14
+ *
15
+ * description 설정은 factory 함수의 opts 파라미터로 전달한다.
16
+ * 메서드 체이닝(.description())은 TypeScript 순환 참조 시 TS7022를 유발하므로 제거됨.
17
+ *
18
+ * @template TTargetFn - 대상 Table builder factory 타입 (무제약)
19
+ *
20
+ * 대상 타겟을 잡는 제네릭은 **무제약**(`extends () => TableBuilder<...>` 제약 없음)이다.
21
+ * 제약이 있으면 모델 const 형성 중 `() => typeof X` 타겟 화살표를 eager 평가하여
22
+ * TS6 에서 순환 const(TS7022/7024)를 유발하기 때문이다. 대상 해소는 `$inferSelect`
23
+ * 접근 시점에 lazy 하게(`ExtractRelationTarget`) 이루어진다.
24
+ *
25
+ * @see {@link ForeignKeyTargetBuilder} 역참조 builder
26
+ * @see {@link RelationKeyBuilder} DB FK 없는 관계
27
+ */
28
+ export class ForeignKeyBuilder<TTargetFn> {
29
+ /**
30
+ * @param meta - FK 메타데이터
31
+ * @param meta.columns - FK column 이름 배열
32
+ * @param meta.targetFn - 대상 Table factory
33
+ * @param meta.description - 관계 설명
34
+ */
35
+ constructor(
36
+ readonly meta: {
37
+ columns: string[];
38
+ targetFn: TTargetFn;
39
+ description?: string;
40
+ },
41
+ ) {}
42
+ }
43
+
44
+ /**
45
+ * Foreign Key 역참조 builder (1:N)
46
+ *
47
+ * 다른 Table이 현재 Table을 참조하는 FK의 역참조를 정의
48
+ * include() 배열로 로드됨 (opts.single: true 단일 객체)
49
+ *
50
+ * description, single 설정은 factory 함수의 opts 파라미터로 전달한다.
51
+ * 메서드 체이닝(.description(), .single())은 TypeScript 순환 참조 TS7022를 유발하므로 제거됨.
52
+ *
53
+ * @template TTargetTableFn - 참조하는 Table builder factory 타입 (무제약)
54
+ * @template TIsSingle - 단일 객체 여부
55
+ *
56
+ * @see {@link ForeignKeyBuilder} FK builder
57
+ */
58
+ export class ForeignKeyTargetBuilder<TTargetTableFn, TIsSingle> {
59
+ /**
60
+ * @param meta - FK 역참조 메타데이터
61
+ * @param meta.targetTableFn - 참조하는 Table factory
62
+ * @param meta.relationName - 참조하는 Table의 FK 관계 이름
63
+ * @param meta.description - 관계 설명
64
+ * @param meta.isSingle - 단일 객체 여부
65
+ */
66
+ constructor(
67
+ readonly meta: {
68
+ targetTableFn: TTargetTableFn;
69
+ relationName: string;
70
+ description?: string;
71
+ isSingle?: TIsSingle;
72
+ },
73
+ ) {}
74
+ }
75
+
76
+ // ============================================
77
+ // RelationKeyBuilder (FK와 동일하지만 DB에 FK를 등록하지 않음)
78
+ // ============================================
79
+
80
+ /**
81
+ * 논리적 관계 builder (N:1) - DB FK 미생성
82
+ *
83
+ * ForeignKeyBuilder와 동일하지만 DB에 FK 제약조건을 생성하지 않음
84
+ * View에서도 사용 가능
85
+ *
86
+ * description 설정은 factory 함수의 opts 파라미터로 전달한다.
87
+ *
88
+ * @template TTargetFn - 대상 Table/View builder factory 타입 (무제약)
89
+ *
90
+ * @see {@link ForeignKeyBuilder} DB FK 생성 버전
91
+ */
92
+ export class RelationKeyBuilder<TTargetFn> {
93
+ /**
94
+ * @param meta - 관계 메타데이터
95
+ * @param meta.columns - 관계 column 이름 배열
96
+ * @param meta.targetFn - 대상 Table/View factory
97
+ * @param meta.description - 관계 설명
98
+ */
99
+ constructor(
100
+ readonly meta: {
101
+ columns: string[];
102
+ targetFn: TTargetFn;
103
+ description?: string;
104
+ },
105
+ ) {}
106
+ }
107
+
108
+ /**
109
+ * 논리적 관계 역참조 builder (1:N) - DB FK 미생성
110
+ *
111
+ * ForeignKeyTargetBuilder와 동일하지만 DB에 FK 제약조건을 생성하지 않음
112
+ * View에서도 사용 가능
113
+ *
114
+ * description, single 설정은 factory 함수의 opts 파라미터로 전달한다.
115
+ *
116
+ * @template TTargetTableFn - 참조하는 Table/View builder factory 타입 (무제약)
117
+ * @template TIsSingle - 단일 객체 여부
118
+ *
119
+ * @see {@link ForeignKeyTargetBuilder} DB FK 생성 버전
120
+ */
121
+ export class RelationKeyTargetBuilder<TTargetTableFn, TIsSingle> {
122
+ /**
123
+ * @param meta - 관계 역참조 메타데이터
124
+ * @param meta.targetTableFn - 참조하는 Table/View factory
125
+ * @param meta.relationName - 참조하는 Table/View의 관계 이름
126
+ * @param meta.description - 관계 설명
127
+ * @param meta.isSingle - 단일 객체 여부
128
+ */
129
+ constructor(
130
+ readonly meta: {
131
+ targetTableFn: TTargetTableFn;
132
+ relationName: string;
133
+ description?: string;
134
+ isSingle?: TIsSingle;
135
+ },
136
+ ) {}
137
+ }
138
+
139
+ // ============================================
140
+ // 관계 factory
141
+ // ============================================
142
+
143
+ /**
144
+ * FK 관계 factory 타입 (table 전용)
145
+ *
146
+ * 컬럼 키 제약(`TColumnKey extends string`)은 self-contained 하므로 **유지**한다.
147
+ * 대상 타겟을 잡는 제네릭(`TTargetFn`/`TTargetTableFn`)은 **무제약**이다.
148
+ *
149
+ * @template TColumnKey - Column key 타입
150
+ */
151
+ export type RelationFkFactory<TColumnKey extends string> = {
152
+ /** N:1 FK 관계 정의 (DB FK 생성) */
153
+ foreignKey<TTargetFn>(
154
+ columns: TColumnKey[],
155
+ targetFn: TTargetFn,
156
+ opts?: { description?: string },
157
+ ): ForeignKeyBuilder<TTargetFn>;
158
+ /** 1:N FK 역참조 정의 (single: true 단일 객체) */
159
+ foreignKeyTarget<TTargetTableFn>(
160
+ targetTableFn: TTargetTableFn,
161
+ relationName: string,
162
+ opts: { single: true; description?: string },
163
+ ): ForeignKeyTargetBuilder<TTargetTableFn, true>;
164
+ foreignKeyTarget<TTargetTableFn>(
165
+ targetTableFn: TTargetTableFn,
166
+ relationName: string,
167
+ opts?: { single?: false; description?: string },
168
+ ): ForeignKeyTargetBuilder<TTargetTableFn, false>;
169
+ };
170
+
171
+ /**
172
+ * 논리적 관계 factory 타입 (table/View 공용)
173
+ *
174
+ * @template TColumnKey - Column key 타입
175
+ */
176
+ export type RelationRkFactory<TColumnKey extends string> = {
177
+ /** N:1 논리적 관계 정의 (DB FK 미생성) */
178
+ relationKey<TTargetFn>(
179
+ columns: TColumnKey[],
180
+ targetFn: TTargetFn,
181
+ opts?: { description?: string },
182
+ ): RelationKeyBuilder<TTargetFn>;
183
+ /** 1:N 논리적 역참조 정의 (single: true → 단일 객체) */
184
+ relationKeyTarget<TTargetTableFn>(
185
+ targetTableFn: TTargetTableFn,
186
+ relationName: string,
187
+ opts: { single: true; description?: string },
188
+ ): RelationKeyTargetBuilder<TTargetTableFn, true>;
189
+ relationKeyTarget<TTargetTableFn>(
190
+ targetTableFn: TTargetTableFn,
191
+ relationName: string,
192
+ opts?: { single?: false; description?: string },
193
+ ): RelationKeyTargetBuilder<TTargetTableFn, false>;
194
+ };
195
+
196
+ /**
197
+ * Table용 관계 factory (FK + RelationKey 모두 사용 가능)
198
+ */
199
+ export type TableRelationFactory<TColumnKey extends string> = RelationFkFactory<TColumnKey> &
200
+ RelationRkFactory<TColumnKey>;
201
+
202
+ /**
203
+ * View용 관계 factory (RelationKey만 사용 가능)
204
+ */
205
+ export type ViewRelationFactory<TColumnKey extends string> = RelationRkFactory<TColumnKey>;
206
+
207
+ /**
208
+ * 관계 builder factory 생성
209
+ *
210
+ * `TableBuilder.relations(fn)` / `ViewBuilder.relations(fn)` 의 콜백 인자로 전달된다.
211
+ * Table은 FK + RelationKey 모두 사용 가능, View는 RelationKey만 사용 가능.
212
+ *
213
+ * @template TColumnKey - Column key 타입
214
+ * @returns 관계 builder factory
215
+ */
216
+ export function createRelationFactory<TColumnKey extends string = string>(): TableRelationFactory<TColumnKey> {
217
+ return {
218
+ foreignKey(columns, targetFn, opts?) {
219
+ return new ForeignKeyBuilder({
220
+ columns,
221
+ targetFn,
222
+ description: opts?.description,
223
+ });
224
+ },
225
+ foreignKeyTarget(targetTableFn, relationName, opts?) {
226
+ return new ForeignKeyTargetBuilder({
227
+ targetTableFn,
228
+ relationName,
229
+ description: opts?.description,
230
+ isSingle: opts?.single,
231
+ });
232
+ },
233
+ relationKey(columns, targetFn, opts?) {
234
+ return new RelationKeyBuilder({
235
+ columns,
236
+ targetFn,
237
+ description: opts?.description,
238
+ });
239
+ },
240
+ relationKeyTarget(targetTableFn, relationName, opts?) {
241
+ return new RelationKeyTargetBuilder({
242
+ targetTableFn,
243
+ relationName,
244
+ description: opts?.description,
245
+ isSingle: opts?.single,
246
+ });
247
+ },
248
+ } as TableRelationFactory<TColumnKey>;
249
+ }
250
+
251
+ // ============================================
252
+ // builder 레코드
253
+ // ============================================
254
+
255
+ /**
256
+ * 관계 builder 레코드 타입
257
+ *
258
+ * `TableBuilder.relations(fn)` / `ViewBuilder.relations(fn)` 콜백의 반환 타입.
259
+ */
260
+ export type RelationBuilderRecord = Record<
261
+ string,
262
+ | ForeignKeyBuilder<any>
263
+ | ForeignKeyTargetBuilder<any, any>
264
+ | RelationKeyBuilder<any>
265
+ | RelationKeyTargetBuilder<any, any>
266
+ >;
267
+
268
+ // ============================================
269
+ // Infer - 관계 타입 추론 (lazy 구조적 walk + 순환 방지 visited)
270
+ // ============================================
271
+ //
272
+ // 핵심: 관계 대상을 `ReturnType<TFn>` 으로 즉시 풀지 않고
273
+ // `TFn extends () => infer TTarget` 형태로 **lazy** 하게 푼다.
274
+ // 이로써 `$inferSelect` phantom 필드에 접근하기 전까지 `() => typeof X` 타겟이
275
+ // 평가되지 않아, 모델 const 형성 중 순환이 발생하지 않는다.
276
+ // 같은 테이블/뷰 이름 재방문(`TVisited`) 시 컬럼만 반환하여 무한 재귀를 끊는다.
277
+
278
+ /**
279
+ * FK/RelationKey에서 대상 타입 추출 (단일 객체, N:1)
280
+ *
281
+ * 대상이 Table이면 컬럼 + 심층 관계, View면 데이터 + 심층 관계를 반환한다.
282
+ *
283
+ * @template TRelation - FK 또는 RelationKey builder 타입
284
+ * @template TVisited - 순환 방지를 위한 방문 테이블/뷰명 집합
285
+ */
286
+ export type ExtractRelationTarget<TRelation, TVisited extends string = never> = TRelation extends
287
+ | ForeignKeyBuilder<infer TTargetFn>
288
+ | RelationKeyBuilder<infer TTargetFn>
289
+ ? TTargetFn extends () => infer TTarget
290
+ ? TTarget extends TableBuilder<infer TName, infer TCols, infer TRels>
291
+ ? TName extends TVisited
292
+ ? InferColumns<TCols>
293
+ : InferColumns<TCols> & InferDeepRelations<TRels, TVisited | TName>
294
+ : TTarget extends ViewBuilder<any, infer TVName, infer TData, infer TVRels>
295
+ ? TVName extends TVisited
296
+ ? TData
297
+ : TData & InferDeepRelations<TVRels, TVisited | TVName>
298
+ : never
299
+ : never
300
+ : never;
301
+
302
+ /**
303
+ * FKTarget/RelationKeyTarget에서 대상 타입 추출 (배열 또는 단일 객체, 1:N)
304
+ *
305
+ * opts.single: true 시 단일 객체, 아니면 배열.
306
+ *
307
+ * @template TRelation - FKTarget 또는 RelationKeyTarget builder 타입
308
+ * @template TVisited - 순환 방지를 위한 방문 테이블/뷰명 집합
309
+ */
310
+ export type ExtractRelationTargetResult<
311
+ TRelation,
312
+ TVisited extends string = never,
313
+ > = TRelation extends
314
+ | ForeignKeyTargetBuilder<infer TTargetTableFn, infer TIsSingle>
315
+ | RelationKeyTargetBuilder<infer TTargetTableFn, infer TIsSingle>
316
+ ? TTargetTableFn extends () => infer TTarget
317
+ ? TTarget extends TableBuilder<infer TName, infer TCols, infer TRels>
318
+ ? TName extends TVisited
319
+ ? TIsSingle extends true
320
+ ? InferColumns<TCols>
321
+ : InferColumns<TCols>[]
322
+ : TIsSingle extends true
323
+ ? InferColumns<TCols> & InferDeepRelations<TRels, TVisited | TName>
324
+ : (InferColumns<TCols> & InferDeepRelations<TRels, TVisited | TName>)[]
325
+ : TTarget extends ViewBuilder<any, infer TVName, infer TData, infer TVRels>
326
+ ? TVName extends TVisited
327
+ ? TIsSingle extends true
328
+ ? TData
329
+ : TData[]
330
+ : TIsSingle extends true
331
+ ? TData & InferDeepRelations<TVRels, TVisited | TVName>
332
+ : (TData & InferDeepRelations<TVRels, TVisited | TVName>)[]
333
+ : never
334
+ : never
335
+ : never;
336
+
337
+ /**
338
+ * 관계 레코드에서 심층 관계 타입 추론
339
+ *
340
+ * include() 없이 접근 undefined가 되도록 모든 관계를 optional로 설정.
341
+ * 입력 제약 없음(무제약) — 관계가 없는(`{}`) 테이블도 안전하게 빈 객체로 해소된다.
342
+ *
343
+ * @template TRelations - 관계 builder 레코드 타입 (무제약)
344
+ * @template TVisited - 순환 방지를 위한 방문 테이블/뷰명 집합
345
+ */
346
+ export type InferDeepRelations<TRelations, TVisited extends string = never> = {
347
+ [K in keyof TRelations]?:
348
+ | ExtractRelationTarget<TRelations[K], TVisited>
349
+ | ExtractRelationTargetResult<TRelations[K], TVisited>;
350
+ };