sonamu 0.5.5 → 0.5.7

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 (52) hide show
  1. package/dist/api/decorators.d.ts +1 -0
  2. package/dist/api/decorators.d.ts.map +1 -1
  3. package/dist/api/decorators.js +1 -1
  4. package/dist/api/decorators.js.map +1 -1
  5. package/dist/api/sonamu.d.ts.map +1 -1
  6. package/dist/api/sonamu.js +1 -1
  7. package/dist/api/sonamu.js.map +1 -1
  8. package/dist/bin/build-config.d.ts +4 -0
  9. package/dist/bin/build-config.d.ts.map +1 -1
  10. package/dist/bin/build-config.js +1 -1
  11. package/dist/bin/build-config.js.map +1 -1
  12. package/dist/bin/cli-wrapper.js +1 -1
  13. package/dist/bin/cli-wrapper.js.map +1 -1
  14. package/dist/database/db.d.ts +3 -0
  15. package/dist/database/db.d.ts.map +1 -1
  16. package/dist/database/db.js +1 -1
  17. package/dist/database/db.js.map +1 -1
  18. package/dist/database/puri-wrapper.d.ts +22 -10
  19. package/dist/database/puri-wrapper.d.ts.map +1 -1
  20. package/dist/database/puri-wrapper.js +1 -1
  21. package/dist/database/puri-wrapper.js.map +1 -1
  22. package/dist/database/puri.d.ts +91 -65
  23. package/dist/database/puri.d.ts.map +1 -1
  24. package/dist/database/puri.js +1 -1
  25. package/dist/database/puri.js.map +1 -1
  26. package/dist/database/puri.types.d.ts +28 -42
  27. package/dist/database/puri.types.d.ts.map +1 -1
  28. package/dist/database/transaction-context.d.ts +3 -3
  29. package/dist/database/transaction-context.d.ts.map +1 -1
  30. package/dist/database/transaction-context.js.map +1 -1
  31. package/dist/templates/service.template.d.ts.map +1 -1
  32. package/dist/templates/service.template.js +1 -1
  33. package/dist/templates/service.template.js.map +1 -1
  34. package/dist/types/types.d.ts +1 -1
  35. package/dist/types/types.d.ts.map +1 -1
  36. package/dist/types/types.js.map +1 -1
  37. package/package.json +1 -2
  38. package/src/api/decorators.ts +14 -5
  39. package/src/api/sonamu.ts +21 -20
  40. package/src/bin/build-config.ts +7 -3
  41. package/src/bin/cli-wrapper.ts +31 -12
  42. package/src/database/db.ts +44 -9
  43. package/src/database/puri-wrapper.ts +104 -26
  44. package/src/database/puri.ts +434 -543
  45. package/src/database/puri.types.ts +99 -200
  46. package/src/database/transaction-context.ts +4 -4
  47. package/src/templates/service.template.ts +10 -1
  48. package/src/types/types.ts +1 -1
  49. package/dist/entity/migrator.d.ts +0 -135
  50. package/dist/entity/migrator.d.ts.map +0 -1
  51. package/dist/entity/migrator.js +0 -2
  52. package/dist/entity/migrator.js.map +0 -1
@@ -1,137 +1,144 @@
1
1
  import type { Knex } from "knex";
2
2
  import type {
3
3
  AvailableColumns,
4
+ SelectObject,
5
+ ParseSelectObject,
6
+ WhereCondition,
4
7
  ComparisonOperator,
5
- EmptyRecord,
6
- Expand,
7
8
  ExtractColumnType,
9
+ SqlExpression,
10
+ Expand,
8
11
  FulltextColumns,
9
- InsertData,
10
- MergeJoined,
11
- ParseSelectObject,
12
12
  ResultAvailableColumns,
13
- SelectObject,
14
- SqlFunction,
15
- WhereCondition,
13
+ InsertData,
14
+ SingleTableValue,
16
15
  } from "./puri.types";
17
16
  import chalk from "chalk";
17
+ import assert from "assert";
18
18
 
19
- // 메인 Puri 클래스
20
19
  export class Puri<
21
20
  TSchema,
22
- TTable extends keyof TSchema | string,
23
- TOriginal = TTable extends keyof TSchema ? TSchema[TTable] : unknown,
24
- TResult = TTable extends keyof TSchema ? TSchema[TTable] : unknown,
25
- TJoined = EmptyRecord,
21
+ TTables extends Record<string, any>,
22
+ TResult,
23
+ TResolved = Expand<TResult>[],
26
24
  > {
27
25
  private knexQuery: Knex.QueryBuilder;
28
26
 
29
27
  // 생성자 시그니처들
28
+ constructor(knex: Knex, tableName: string);
30
29
  constructor(
31
30
  knex: Knex,
32
- tableName: TTable extends keyof TSchema ? TTable : unknown
33
- );
34
- constructor(
35
- knex: Knex,
36
- subquery: Puri<TSchema, any, any, TOriginal, any>,
37
- alias: TTable extends string ? TTable : never
31
+ tableSpec: Record<string, string | Puri<TSchema, any, any>>
38
32
  );
39
33
  constructor(
40
34
  private knex: Knex,
41
- tableNameOrSubquery: any,
42
- alias?: TTable extends string ? TTable : never
35
+ tableNameOrSpec: any
43
36
  ) {
44
- if (typeof tableNameOrSubquery === "string") {
45
- // 일반 테이블로 시작
46
- this.knexQuery = knex(tableNameOrSubquery).from(tableNameOrSubquery);
37
+ if (typeof tableNameOrSpec === "string") {
38
+ // Case: new Puri(knex, "users")
39
+ this.knexQuery = this.knex(tableNameOrSpec).from(tableNameOrSpec);
40
+ } else if (typeof tableNameOrSpec === "object") {
41
+ const entries = Object.entries(tableNameOrSpec);
42
+ if (entries.length !== 1) {
43
+ throw new Error("Table spec must have exactly one entry");
44
+ }
45
+ assert(entries[0]);
46
+ const [alias, spec] = entries[0];
47
+ if (typeof spec === "string") {
48
+ this.knexQuery = this.knex(spec).from({ [alias]: spec });
49
+ } else if (spec instanceof Puri) {
50
+ const subqueryBuilder = spec.raw();
51
+ this.knexQuery = this.knex.from(subqueryBuilder.as(alias));
52
+ } else {
53
+ throw new Error("Invalid table specification");
54
+ }
47
55
  } else {
48
- // 서브쿼리로 시작
49
- this.knexQuery = knex.from(tableNameOrSubquery.raw().as(alias));
56
+ throw new Error("Invalid table specification");
50
57
  }
51
58
  }
52
59
 
53
- // Static SQL helper functions
54
- static count(column: string = "*"): SqlFunction<"number"> {
60
+ // Static SQL helper functions for SELECT
61
+ static count(column: string = "*"): SqlExpression<"number"> {
55
62
  return {
56
- _type: "sql_function",
63
+ _type: "sql_expression",
57
64
  _return: "number",
58
65
  _sql: `COUNT(${column})`,
59
66
  };
60
67
  }
61
-
62
- static sum(column: string): SqlFunction<"number"> {
63
- return { _type: "sql_function", _return: "number", _sql: `SUM(${column})` };
68
+ static sum(column: string): SqlExpression<"number"> {
69
+ return {
70
+ _type: "sql_expression",
71
+ _return: "number",
72
+ _sql: `SUM(${column})`,
73
+ };
64
74
  }
65
-
66
- static avg(column: string): SqlFunction<"number"> {
67
- return { _type: "sql_function", _return: "number", _sql: `AVG(${column})` };
75
+ static avg(column: string): SqlExpression<"number"> {
76
+ return {
77
+ _type: "sql_expression",
78
+ _return: "number",
79
+ _sql: `AVG(${column})`,
80
+ };
68
81
  }
69
-
70
- static max(column: string): SqlFunction<"number"> {
71
- return { _type: "sql_function", _return: "number", _sql: `MAX(${column})` };
82
+ static max(column: string): SqlExpression<"number"> {
83
+ return {
84
+ _type: "sql_expression",
85
+ _return: "number",
86
+ _sql: `MAX(${column})`,
87
+ };
72
88
  }
73
-
74
- static min(column: string): SqlFunction<"number"> {
75
- return { _type: "sql_function", _return: "number", _sql: `MIN(${column})` };
89
+ static min(column: string): SqlExpression<"number"> {
90
+ return {
91
+ _type: "sql_expression",
92
+ _return: "number",
93
+ _sql: `MIN(${column})`,
94
+ };
76
95
  }
77
-
78
- static concat(...args: string[]): SqlFunction<"string"> {
96
+ static concat(...args: string[]): SqlExpression<"string"> {
79
97
  return {
80
- _type: "sql_function",
98
+ _type: "sql_expression",
81
99
  _return: "string",
82
100
  _sql: `CONCAT(${args.join(", ")})`,
83
101
  };
84
102
  }
85
-
86
- static upper(column: string): SqlFunction<"string"> {
103
+ static upper(column: string): SqlExpression<"string"> {
87
104
  return {
88
- _type: "sql_function",
105
+ _type: "sql_expression",
89
106
  _return: "string",
90
107
  _sql: `UPPER(${column})`,
91
108
  };
92
109
  }
93
-
94
- static lower(column: string): SqlFunction<"string"> {
110
+ static lower(column: string): SqlExpression<"string"> {
95
111
  return {
96
- _type: "sql_function",
112
+ _type: "sql_expression",
97
113
  _return: "string",
98
114
  _sql: `LOWER(${column})`,
99
115
  };
100
116
  }
101
117
 
102
- // Raw functions
103
- static rawString(sql: string): SqlFunction<"string"> {
104
- return { _type: "sql_function", _return: "string", _sql: sql };
118
+ // Raw functions for SELECT
119
+ static rawString(sql: string): SqlExpression<"string"> {
120
+ return { _type: "sql_expression", _return: "string", _sql: sql };
105
121
  }
106
-
107
- static rawNumber(sql: string): SqlFunction<"number"> {
108
- return { _type: "sql_function", _return: "number", _sql: sql };
122
+ static rawNumber(sql: string): SqlExpression<"number"> {
123
+ return { _type: "sql_expression", _return: "number", _sql: sql };
109
124
  }
110
-
111
- static rawBoolean(sql: string): SqlFunction<"boolean"> {
112
- return { _type: "sql_function", _return: "boolean", _sql: sql };
125
+ static rawBoolean(sql: string): SqlExpression<"boolean"> {
126
+ return { _type: "sql_expression", _return: "boolean", _sql: sql };
113
127
  }
114
-
115
- static rawDate(sql: string): SqlFunction<"date"> {
116
- return { _type: "sql_function", _return: "date", _sql: sql };
128
+ static rawDate(sql: string): SqlExpression<"date"> {
129
+ return { _type: "sql_expression", _return: "date", _sql: sql };
117
130
  }
118
131
 
119
- // Alias 기반 Select
120
- select<TSelect extends SelectObject<TSchema, TTable, TOriginal, TJoined>>(
132
+ // SELECT
133
+ select<TSelect extends SelectObject<TTables>>(
121
134
  selectObj: TSelect
122
- ): Puri<
123
- TSchema,
124
- TTable,
125
- TOriginal,
126
- ParseSelectObject<TSchema, TTable, TSelect, TOriginal, TJoined>,
127
- TJoined
128
- > {
135
+ ): Puri<TSchema, TTables, ParseSelectObject<TTables, TSelect>> {
129
136
  const selectClauses: (string | Knex.Raw)[] = [];
130
137
 
131
138
  for (const [alias, columnOrFunction] of Object.entries(selectObj)) {
132
139
  if (
133
140
  typeof columnOrFunction === "object" &&
134
- columnOrFunction._type === "sql_function"
141
+ columnOrFunction._type === "sql_expression"
135
142
  ) {
136
143
  // SQL 함수인 경우
137
144
  selectClauses.push(
@@ -154,50 +161,204 @@ export class Puri<
154
161
  return this as any;
155
162
  }
156
163
 
157
- // 전체 선택 (편의 메서드)
158
- selectAll(): Puri<
159
- TSchema,
160
- TTable,
161
- TOriginal,
162
- TTable extends keyof TSchema
163
- ? TSchema[TTable] & TJoined
164
- : TResult & TJoined,
165
- TJoined
166
- > {
164
+ // SELECT *
165
+ selectAll(): this {
167
166
  this.knexQuery.select("*");
168
167
  return this as any;
169
168
  }
170
169
 
171
- // Where 조건 (조인된 테이블 컬럼도 지원)
172
- where(
173
- conditions: WhereCondition<TSchema, TTable, TOriginal, TJoined>
174
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
175
- where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
170
+ // JOIN: 서브쿼리 + Alias
171
+ join<TJoinAlias extends string, TSubResult>(
172
+ tableSpec: { [K in TJoinAlias]: Puri<TSchema, any, TSubResult> },
173
+ left: AvailableColumns<TTables>,
174
+ right: `${TJoinAlias}.${keyof TSubResult & string}`
175
+ ): Puri<
176
+ TSchema,
177
+ TTables & Record<TJoinAlias, TSubResult>, // 서브쿼리의 TResult
178
+ TResult
179
+ >;
180
+ // JOIN: 테이블 + Alias
181
+ join<TJoinTable extends keyof TSchema, TJoinAlias extends string>(
182
+ tableSpec: { [K in TJoinAlias]: TJoinTable },
183
+ left: AvailableColumns<TTables>,
184
+ right: `${TJoinAlias}.${keyof TSchema[TJoinTable] & string}`
185
+ ): Puri<
186
+ TSchema,
187
+ TTables & Record<TJoinAlias, TSchema[TJoinTable]>, // TTables 확장!
188
+ TResult
189
+ >;
190
+ // JOIN: 테이블명
191
+ join<TJoinTable extends keyof TSchema>(
192
+ tableName: TJoinTable,
193
+ left: AvailableColumns<TTables>,
194
+ right: `${TJoinTable & string}.${keyof TSchema[TJoinTable] & string}`
195
+ ): Puri<
196
+ TSchema,
197
+ TTables & Record<TJoinTable, TSchema[TJoinTable]>, // 테이블명이 키
198
+ TResult
199
+ >;
200
+ // JOIN: 서브쿼리 + Alias + 콜백
201
+ join<TJoinAlias extends string, TSubResult>(
202
+ tableSpec: { [K in TJoinAlias]: Puri<TSchema, any, TSubResult> },
203
+ callback: (
204
+ j: JoinClauseGroup<TTables, Record<TJoinAlias, TSubResult>>
205
+ ) => void
206
+ ): Puri<TSchema, TTables & Record<TJoinAlias, TSubResult>, TResult>;
207
+ // JOIN: 테이블 + Alias + 콜백
208
+ join<TJoinTable extends keyof TSchema, TJoinAlias extends string>(
209
+ tableSpec: { [K in TJoinAlias]: TJoinTable },
210
+ callback: (
211
+ j: JoinClauseGroup<TTables, Record<TJoinAlias, TSchema[TJoinTable]>>
212
+ ) => void
213
+ ): Puri<TSchema, TTables & Record<TJoinAlias, TSchema[TJoinTable]>, TResult>;
214
+ // JOIN: 테이블명 + 콜백
215
+ join<TJoinTable extends keyof TSchema>(
216
+ tableName: TJoinTable,
217
+ callback: (
218
+ j: JoinClauseGroup<TTables, Record<TJoinTable, TSchema[TJoinTable]>>
219
+ ) => void
220
+ ): Puri<TSchema, TTables & Record<TJoinTable, TSchema[TJoinTable]>, TResult>;
221
+ // JOIN 실제 구현
222
+ join(tableNameOrSpec: any, ...args: any[]): any {
223
+ return this.__commonJoin("join", tableNameOrSpec, ...args);
224
+ }
225
+
226
+ // LEFT JOIN: 서브쿼리 + Alias
227
+ leftJoin<TJoinAlias extends string, TSubResult>(
228
+ tableSpec: { [K in TJoinAlias]: Puri<TSchema, any, TSubResult> },
229
+ left: AvailableColumns<TTables>,
230
+ right: `${TJoinAlias}.${keyof TSubResult & string}`
231
+ ): Puri<
232
+ TSchema,
233
+ TTables & Record<TJoinAlias, TSubResult>, // 서브쿼리의 TResult
234
+ TResult
235
+ >;
236
+ // LEFT JOIN: 테이블 + Alias
237
+ leftJoin<TJoinTable extends keyof TSchema, TJoinAlias extends string>(
238
+ tableSpec: { [K in TJoinAlias]: TJoinTable },
239
+ left: AvailableColumns<TTables>,
240
+ right: `${TJoinAlias}.${keyof TSchema[TJoinTable] & string}`
241
+ ): Puri<
242
+ TSchema,
243
+ TTables & Record<TJoinAlias, TSchema[TJoinTable]>, // TTables 확장!
244
+ TResult
245
+ >;
246
+ // LEFT JOIN: 테이블명
247
+ leftJoin<TJoinTable extends keyof TSchema>(
248
+ tableName: TJoinTable,
249
+ left: AvailableColumns<TTables>,
250
+ right: `${TJoinTable & string}.${keyof TSchema[TJoinTable] & string}`
251
+ ): Puri<
252
+ TSchema,
253
+ TTables & Record<TJoinTable, TSchema[TJoinTable]>, // 테이블명이 키
254
+ TResult
255
+ >;
256
+ // LEFT JOIN: 서브쿼리 + Alias + 콜백
257
+ leftJoin<TJoinAlias extends string, TSubResult>(
258
+ tableSpec: { [K in TJoinAlias]: Puri<TSchema, any, TSubResult> },
259
+ callback: (
260
+ j: JoinClauseGroup<TTables, Record<TJoinAlias, TSubResult>>
261
+ ) => void
262
+ ): Puri<TSchema, TTables & Record<TJoinAlias, TSubResult>, TResult>;
263
+ // LEFT JOIN: 테이블 + Alias + 콜백
264
+ leftJoin<TJoinTable extends keyof TSchema, TJoinAlias extends string>(
265
+ tableSpec: { [K in TJoinAlias]: TJoinTable },
266
+ callback: (
267
+ j: JoinClauseGroup<TTables, Record<TJoinAlias, TSchema[TJoinTable]>>
268
+ ) => void
269
+ ): Puri<TSchema, TTables & Record<TJoinAlias, TSchema[TJoinTable]>, TResult>;
270
+ // LEFT JOIN: 테이블명 + 콜백
271
+ leftJoin<TJoinTable extends keyof TSchema>(
272
+ tableName: TJoinTable,
273
+ callback: (
274
+ j: JoinClauseGroup<TTables, Record<TJoinTable, TSchema[TJoinTable]>>
275
+ ) => void
276
+ ): Puri<TSchema, TTables & Record<TJoinTable, TSchema[TJoinTable]>, TResult>;
277
+ // LEFT JOIN 실제 구현
278
+ leftJoin(tableNameOrSpec: any, ...args: any[]): any {
279
+ return this.__commonJoin("leftJoin", tableNameOrSpec, ...args);
280
+ }
281
+
282
+ __commonJoin(
283
+ joinType: "join" | "leftJoin",
284
+ tableNameOrSpec: any,
285
+ ...args: any[]
286
+ ): this {
287
+ if (typeof tableNameOrSpec === "string") {
288
+ // Case 1: join("posts", ...)
289
+ const tableName = tableNameOrSpec;
290
+
291
+ if (args.length === 1 && typeof args[0] === "function") {
292
+ // join("posts", callback)
293
+ const callback = args[0];
294
+ this.knexQuery[joinType](tableName, (joinClause) => {
295
+ callback(new JoinClauseGroup(joinClause));
296
+ });
297
+ } else {
298
+ // join("posts", left, right)
299
+ const [left, right] = args;
300
+ this.knexQuery[joinType](tableName, left, right);
301
+ }
302
+ } else if (typeof tableNameOrSpec === "object") {
303
+ // Case 2: join({ alias: "table" }, ...) or join({ alias: subquery }, ...)
304
+ const entries = Object.entries(tableNameOrSpec);
305
+ if (entries.length !== 1) {
306
+ throw new Error("Table spec must have exactly one entry");
307
+ }
308
+ assert(entries[0]);
309
+ const [[alias, spec]] = entries;
310
+
311
+ if (typeof spec === "string") {
312
+ // 테이블: join({ p: "posts" }, ...)
313
+ if (args.length === 1 && typeof args[0] === "function") {
314
+ // Callback
315
+ const callback = args[0];
316
+ this.knexQuery[joinType]({ [alias]: spec }, (joinClause) => {
317
+ callback(new JoinClauseGroup(joinClause));
318
+ });
319
+ } else {
320
+ // Simple
321
+ const [left, right] = args;
322
+ this.knexQuery[joinType]({ [alias]: spec }, left, right);
323
+ }
324
+ } else if (spec instanceof Puri) {
325
+ // 서브쿼리: join({ sq: subquery }, ...)
326
+ if (args.length === 1 && typeof args[0] === "function") {
327
+ // Callback
328
+ const callback = args[0];
329
+ this.knexQuery[joinType](spec.raw().as(alias), (joinClause) => {
330
+ callback(new JoinClauseGroup(joinClause));
331
+ });
332
+ } else {
333
+ // Simple
334
+ const [left, right] = args;
335
+ this.knexQuery[joinType](spec.raw().as(alias), left, right);
336
+ }
337
+ } else {
338
+ throw new Error("Invalid table specification");
339
+ }
340
+ } else {
341
+ throw new Error("Invalid arguments");
342
+ }
343
+
344
+ return this;
345
+ }
346
+
347
+ // WHERE: 객체 - 사용: .where({ "u.id": 1, "u.status": "active" })
348
+ where(conditions: WhereCondition<TTables>): this;
349
+ // WHERE: 컬럼 - 사용: .where("u.id", 1)
350
+ where<TColumn extends AvailableColumns<TTables>>(
176
351
  column: TColumn,
177
- value: ExtractColumnType<
178
- TSchema,
179
- TTable,
180
- TColumn & string,
181
- TOriginal,
182
- TJoined
183
- >
184
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
185
- where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
352
+ value: ExtractColumnType<TTables, TColumn & string>
353
+ ): this;
354
+ // WHERE: 컬럼 - 사용: .where("u.id", ">", 10)
355
+ where<TColumn extends AvailableColumns<TTables>>(
186
356
  column: TColumn,
187
- operator: ComparisonOperator | "like",
188
- value: ExtractColumnType<
189
- TSchema,
190
- TTable,
191
- TColumn & string,
192
- TOriginal,
193
- TJoined
194
- >
195
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
196
- where(
197
- columnOrConditions: any,
198
- operatorOrValue?: any,
199
- value?: any
200
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
357
+ operator: ComparisonOperator,
358
+ value: ExtractColumnType<TTables, TColumn & string>
359
+ ): this;
360
+ // WHERE: 컬럼 - 사용: .where("u.id", "like", "%test%")
361
+ where(columnOrConditions: any, operatorOrValue?: any, value?: any): this {
201
362
  if (typeof columnOrConditions === "object") {
202
363
  this.knexQuery.where(columnOrConditions);
203
364
  } else if (arguments.length === 2) {
@@ -223,335 +384,139 @@ export class Puri<
223
384
  return this;
224
385
  }
225
386
 
226
- // WhereIn (조인된 테이블 컬럼도 지원)
227
- whereIn<
228
- TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
229
- >(
387
+ // WHERE IN
388
+ whereIn<TColumn extends AvailableColumns<TTables>>(
230
389
  column: TColumn,
231
- values: ExtractColumnType<
232
- TSchema,
233
- TTable,
234
- TColumn & string,
235
- TOriginal,
236
- TJoined
237
- >[]
238
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
239
- whereIn(
240
- column: string,
241
- values: any[]
242
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
390
+ values: ExtractColumnType<TTables, TColumn & string>[]
391
+ ): Puri<TSchema, TTables, TResult> {
243
392
  this.knexQuery.whereIn(column, values);
244
- return this;
393
+ return this as any;
245
394
  }
246
395
 
247
- whereNotIn<
248
- TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
249
- >(
396
+ // WHERE NOT IN
397
+ whereNotIn<TColumn extends AvailableColumns<TTables>>(
250
398
  column: TColumn,
251
- values: ExtractColumnType<
252
- TSchema,
253
- TTable,
254
- TColumn & string,
255
- TOriginal,
256
- TJoined
257
- >[]
258
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
259
- whereNotIn(
260
- column: string,
261
- values: any[]
262
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
263
- this.knexQuery.whereNotIn(column, values);
264
- return this;
399
+ values: ExtractColumnType<TTables, TColumn & string>[]
400
+ ): Puri<TSchema, TTables, TResult> {
401
+ this.knexQuery.whereIn(column, values);
402
+ return this as any;
265
403
  }
266
404
 
267
- whereMatch<
268
- TColumn extends FulltextColumns<TSchema, TTable, TOriginal, TJoined>,
269
- >(
405
+ // WHERE MATCH
406
+ whereMatch<TColumn extends FulltextColumns<TTables>>(
270
407
  column: TColumn,
271
408
  value: string
272
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
409
+ ): this {
273
410
  this.knexQuery.whereRaw(`MATCH (${String(column)}) AGAINST (?)`, [value]);
274
411
  return this;
275
412
  }
276
413
 
277
- // WhereGroup (괄호 그룹핑 지원)
278
- whereGroup(
279
- callback: (
280
- group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
281
- ) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
282
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
414
+ // WHERE 괄호 그룹핑
415
+ whereGroup(callback: (g: WhereGroup<TTables>) => void): this {
283
416
  this.knexQuery.where((builder) => {
284
- const group = new WhereGroup<TSchema, TTable, TOriginal, TJoined>(
285
- builder
286
- );
417
+ const group = new WhereGroup<TTables>(builder);
287
418
  callback(group);
288
419
  });
289
420
  return this;
290
421
  }
291
-
292
- orWhereGroup(
293
- callback: (
294
- group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
295
- ) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
296
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
422
+ orWhereGroup(callback: (g: WhereGroup<TTables>) => void): this {
297
423
  this.knexQuery.orWhere((builder) => {
298
- const group = new WhereGroup<TSchema, TTable, TOriginal, TJoined>(
299
- builder
300
- );
424
+ const group = new WhereGroup<TTables>(builder);
301
425
  callback(group);
302
426
  });
303
427
  return this;
304
428
  }
305
429
 
306
- // Join
307
- join<
308
- TJoinTable extends keyof TSchema,
309
- TLColumn extends AvailableColumns<
310
- TSchema,
311
- TTable,
312
- TOriginal,
313
- TJoined & Record<TJoinTable, TSchema[TJoinTable]>
314
- >,
315
- TRColumn extends AvailableColumns<
316
- TSchema,
317
- TTable,
318
- TOriginal,
319
- TJoined & Record<TJoinTable, TSchema[TJoinTable]>
320
- >,
321
- >(
322
- table: TJoinTable,
323
- left: TLColumn,
324
- right: TRColumn
325
- ): Puri<
326
- TSchema,
327
- TTable,
328
- TOriginal,
329
- TResult,
330
- MergeJoined<TJoined, Record<TJoinTable, TSchema[TJoinTable]>>
331
- >;
332
- join<TJoinTable extends keyof TSchema>(
333
- table: TJoinTable,
334
- joinCallback: (
335
- joinClause: JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>
336
- ) => void
337
- ): Puri<
338
- TSchema,
339
- TTable,
340
- TOriginal,
341
- TResult,
342
- MergeJoined<TJoined, Record<TJoinTable, TSchema[TJoinTable]>>
343
- >;
344
- join<TSubResult, TAlias extends string>(
345
- subquery: Puri<TSchema, any, any, TSubResult, any>,
346
- alias: TAlias,
347
- left: string,
348
- right: string
349
- ): Puri<
350
- TSchema,
351
- TTable,
352
- TOriginal,
353
- TResult,
354
- TJoined & Record<TAlias, TSubResult>
355
- >;
356
- join(
357
- table: string,
358
- left: string,
359
- right: string
360
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
361
- join(
362
- tableOrSubquery: string | keyof TSchema | Puri<TSchema, any, any, any, any>,
363
- ...args: any[]
364
- ): Puri<TSchema, TTable, TOriginal, TResult, any> {
365
- if (tableOrSubquery instanceof Puri) {
366
- // 서브쿼리 조인: join(subquery, alias, left, right)
367
- const [alias, left, right] = args;
368
- this.knexQuery.join(tableOrSubquery.raw().as(alias), left, right);
369
- } else if (
370
- args.length === 2 &&
371
- typeof args[0] === "string" &&
372
- typeof args[1] === "string"
373
- ) {
374
- const [left, right] = args;
375
- this.knexQuery.join(tableOrSubquery as string, left, right);
376
- } else if (args.length === 1 && typeof args[0] === "function") {
377
- const joinCallback = args[0];
378
- this.knexQuery.join(tableOrSubquery as string, (joinClause) => {
379
- joinCallback(new JoinClauseGroup(joinClause));
380
- });
381
- } else {
382
- throw new Error("Invalid arguments");
383
- }
384
- return this as any;
385
- }
386
-
387
- leftJoin<
388
- TJoinTable extends keyof TSchema,
389
- TLColumn extends AvailableColumns<
390
- TSchema,
391
- TTable,
392
- TOriginal,
393
- TJoined & Record<TJoinTable, TSchema[TJoinTable]>
394
- >,
395
- TRColumn extends AvailableColumns<
396
- TSchema,
397
- TTable,
398
- TOriginal,
399
- TJoined & Record<TJoinTable, TSchema[TJoinTable]>
400
- >,
401
- >(
402
- table: TJoinTable,
403
- left: TLColumn,
404
- right: TRColumn
405
- ): Puri<
406
- TSchema,
407
- TTable,
408
- TOriginal,
409
- TResult,
410
- TJoined & Record<TJoinTable, Partial<TSchema[TJoinTable]>>
411
- >;
412
- leftJoin<TSubResult, TAlias extends string>(
413
- subquery: Puri<TSchema, any, any, TSubResult, any>,
414
- alias: TAlias,
415
- left: string,
416
- right: string
417
- ): Puri<
418
- TSchema,
419
- TTable,
420
- TOriginal,
421
- TResult,
422
- TJoined & Record<TAlias, Partial<TSubResult>>
423
- >;
424
- leftJoin(
425
- table: string,
426
- left: string,
427
- right: string
428
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
429
- leftJoin(
430
- tableOrSubquery: string | keyof TSchema | Puri<TSchema, any, any, any, any>,
431
- ...args: any[]
432
- ): Puri<TSchema, TTable, TOriginal, TResult, any> {
433
- if (tableOrSubquery instanceof Puri) {
434
- // 서브쿼리 조인: leftJoin(subquery, alias, left, right)
435
- const [alias, left, right] = args;
436
- this.knexQuery.leftJoin(tableOrSubquery.raw().as(alias), left, right);
437
- } else {
438
- const [left, right] = args;
439
- this.knexQuery.leftJoin(tableOrSubquery as string, left, right);
440
- }
441
- return this as any;
442
- }
443
-
444
- // OrderBy
445
- orderBy<
446
- TColumn extends ResultAvailableColumns<
447
- TSchema,
448
- TTable,
449
- TOriginal,
450
- TResult,
451
- TJoined
452
- >,
453
- >(
430
+ // ORDER BY
431
+ orderBy<TColumn extends ResultAvailableColumns<TTables, TResult>>(
454
432
  column: TColumn,
455
433
  direction: "asc" | "desc"
456
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
457
- orderBy(
458
- column: string,
459
- direction: "asc" | "desc" = "asc"
460
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
434
+ ): this;
435
+ orderBy(column: string, direction: "asc" | "desc" = "asc"): this {
461
436
  this.knexQuery.orderBy(column, direction);
462
437
  return this;
463
438
  }
464
439
 
465
440
  // 기본 쿼리 메서드들
466
- limit(count: number): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
441
+ limit(count: number): this {
467
442
  this.knexQuery.limit(count);
468
443
  return this;
469
444
  }
470
445
 
471
- offset(count: number): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
446
+ offset(count: number): this {
472
447
  this.knexQuery.offset(count);
473
448
  return this;
474
449
  }
475
450
 
476
- // Group by (조인된 테이블 컬럼도 지원)
477
- groupBy<
478
- TColumns extends ResultAvailableColumns<
479
- TSchema,
480
- TTable,
481
- TOriginal,
482
- TResult,
483
- TJoined
484
- >,
485
- >(...columns: TColumns[]): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
486
- groupBy(
487
- ...columns: string[]
488
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
451
+ // GROUP BY
452
+ groupBy<TColumns extends ResultAvailableColumns<TTables, TResult>>(
453
+ ...columns: TColumns[]
454
+ ): this;
455
+ groupBy(...columns: string[]): this {
489
456
  this.knexQuery.groupBy(...(columns as string[]));
490
457
  return this;
491
458
  }
492
459
 
493
- having(condition: string): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
494
- having<
495
- TColumn extends ResultAvailableColumns<
496
- TSchema,
497
- TTable,
498
- TOriginal,
499
- TResult,
500
- TJoined
501
- >,
502
- >(
503
- condition: TColumn,
460
+ // HAVING
461
+ having(condition: string): this;
462
+ having<TColumn extends ResultAvailableColumns<TTables, TResult>>(
463
+ column: TColumn,
504
464
  operator: ComparisonOperator,
505
465
  value: any
506
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined>;
507
- having(
508
- ...conditions: string[]
509
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
510
- this.knexQuery.having(...(conditions as [string, string, string]));
466
+ ): this;
467
+ // HAVING 구현
468
+ having(...conditions: any[]): this {
469
+ if (conditions.length === 1) {
470
+ // having("COUNT(*) > 10")
471
+ this.knexQuery.having(conditions[0]);
472
+ } else if (conditions.length === 3) {
473
+ // having("count", ">", 10)
474
+ this.knexQuery.having(conditions[0], conditions[1], conditions[2]);
475
+ } else {
476
+ throw new Error("Invalid having arguments");
477
+ }
511
478
  return this;
512
479
  }
480
+
513
481
  // 실행 메서드들 - thenable 구현
514
- then<TResult1, TResult2 = never>(
482
+ then<TResult1 = TResolved, TResult2 = never>(
515
483
  onfulfilled?:
516
- | ((
517
- value: Expand<TResult>[]
518
- ) => Expand<TResult1> | PromiseLike<Expand<TResult1>>)
484
+ | ((value: TResolved) => TResult1 | PromiseLike<TResult1>)
519
485
  | null,
520
486
  onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
521
- ): Promise<Expand<TResult1> | TResult2> {
487
+ ): Promise<TResult1 | TResult2> {
522
488
  return this.knexQuery.then(onfulfilled as any, onrejected);
523
489
  }
524
-
525
490
  catch<TResult2 = never>(
526
491
  onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
527
492
  ): Promise<Expand<TResult> | TResult2> {
528
493
  return this.knexQuery.catch(onrejected);
529
494
  }
530
-
531
495
  finally(onfinally?: (() => void) | null): Promise<Expand<TResult>> {
532
496
  return this.knexQuery.finally(onfinally);
533
497
  }
534
498
 
535
- // 안전한 실행 메서드들
499
+ // 하나만 쿼리
536
500
  async first(): Promise<Expand<TResult> | undefined> {
537
- return this.knexQuery.first() as Promise<Expand<TResult> | undefined>;
501
+ return this.knexQuery.first();
538
502
  }
539
-
503
+ // 하나만 쿼리 실패 시 에러
540
504
  async firstOrFail(): Promise<TResult> {
541
505
  const result = await this.knexQuery.first();
542
506
  if (!result) {
543
507
  throw new Error("No results found");
544
508
  }
545
- return result as TResult;
509
+ return result;
546
510
  }
547
511
 
512
+ // 쿼리 후 인덱스 리턴
548
513
  async at(index: number): Promise<Expand<TResult> | undefined> {
549
- const results = await this;
550
- return results[index] as Expand<TResult> | undefined;
514
+ const results = (await this) as any[];
515
+ return results[index];
551
516
  }
552
-
517
+ // 쿼리 후 인덱스 리턴 실패 시 에러
553
518
  async assertAt(index: number): Promise<Expand<TResult>> {
554
- const results = await this;
519
+ const results = (await this) as any[];
555
520
  const result = results[index];
556
521
  if (result === undefined) {
557
522
  throw new Error(`No result found at index ${index}`);
@@ -559,31 +524,67 @@ export class Puri<
559
524
  return result;
560
525
  }
561
526
 
562
- // Insert/Update/Delete
563
- // TODO(Haze, 251030): InsertData<T>에서 nullable type을 제대로 처리하지 못하는 것 같음.
564
- async insert(
565
- data: TTable extends keyof TSchema ? InsertData<TSchema[TTable]> : unknown
566
- ): Promise<number[]> {
567
- return this.knexQuery.insert(data);
527
+ // 쿼리한 레코드에서 특정 컬럼만 추출한 배열 리턴
528
+ async pluck<TColumn extends ResultAvailableColumns<TTables, TResult>>(
529
+ column: TColumn
530
+ ): Promise<ExtractColumnType<TTables, TColumn & string>[]> {
531
+ return this.knexQuery.pluck(column) as Promise<
532
+ ExtractColumnType<TTables, TColumn & string>[]
533
+ >;
534
+ }
535
+
536
+ // INSERT
537
+ insert(
538
+ data: InsertData<SingleTableValue<TTables>>
539
+ ): Puri<TSchema, {}, number, number[]> {
540
+ this.knexQuery.insert(data);
541
+ return this as any;
568
542
  }
569
543
 
570
- async update(
571
- data: Partial<TTable extends keyof TSchema ? TSchema[TTable] : unknown>
572
- ): Promise<number> {
573
- return this.knexQuery.update(data);
544
+ // UPDATE
545
+ update(data: WhereCondition<TTables>): Puri<TSchema, {}, number, number> {
546
+ this.knexQuery.update(data);
547
+ return this as any;
548
+ }
549
+
550
+ // Increment
551
+ increment<TColumn extends AvailableColumns<TTables>>(
552
+ column: TColumn,
553
+ value: number
554
+ ): this {
555
+ if (value <= 0) {
556
+ throw new Error("Increment value must be greater than 0");
557
+ }
558
+ this.knexQuery.increment(column, value);
559
+ return this;
560
+ }
561
+ // Decrement
562
+ decrement<TColumn extends AvailableColumns<TTables>>(
563
+ column: TColumn,
564
+ value: number
565
+ ): this {
566
+ if (value <= 0) {
567
+ throw new Error("Decrement value must be greater than 0");
568
+ }
569
+ this.knexQuery.decrement(column, value);
570
+ return this;
574
571
  }
575
572
 
576
- async delete(): Promise<number> {
577
- return this.knexQuery.delete();
573
+ // DELETE
574
+ delete(): Puri<TSchema, {}, number, number> {
575
+ this.knexQuery.delete();
576
+ return this as any;
578
577
  }
579
578
 
579
+ // 확인 쿼리 리턴
580
580
  toQuery(): string {
581
581
  return this.knexQuery.toQuery();
582
582
  }
583
583
 
584
- debug(): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
584
+ // 쿼리 디버깅 로그 출력
585
+ debug(): this {
585
586
  console.log(
586
- `${chalk.cyan("[Puri Debug]")} ${chalk.yellow(this.formatSQL(this.toQuery()))}`
587
+ `${chalk.cyan("[Puri Debug]")} ${chalk.yellow(this.toQuery())}`
587
588
  );
588
589
  return this;
589
590
  }
@@ -699,174 +700,62 @@ export class Puri<
699
700
  return indentedLines.join("\n").trim();
700
701
  }
701
702
 
703
+ // Knex 쿼리 빌더 직접 접근
702
704
  raw(): Knex.QueryBuilder {
703
705
  return this.knexQuery;
704
706
  }
705
-
706
- increment<
707
- TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
708
- >(
709
- column: TColumn,
710
- value: number
711
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
712
- if (value <= 0) {
713
- throw new Error("Increment value must be greater than 0");
714
- }
715
- this.knexQuery.increment(column, value);
716
- return this;
717
- }
718
-
719
- decrement<
720
- TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
721
- >(
722
- column: TColumn,
723
- value: number
724
- ): Puri<TSchema, TTable, TOriginal, TResult, TJoined> {
725
- if (value <= 0) {
726
- throw new Error("Decrement value must be greater than 0");
727
- }
728
- this.knexQuery.decrement(column, value);
729
- return this;
730
- }
731
707
  }
732
708
 
733
- // 11. Database 클래스
734
- class WhereGroup<
735
- TSchema,
736
- TTable extends keyof TSchema | string,
737
- TOriginal = any,
738
- TJoined = EmptyRecord,
739
- > {
709
+ export class WhereGroup<TTables extends Record<string, any>> {
740
710
  constructor(private builder: Knex.QueryBuilder) {}
741
711
 
742
- where(
743
- conditions: WhereCondition<TSchema, TTable, TOriginal, TJoined>
744
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
745
- where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
712
+ // where 메서드들
713
+ where(conditions: WhereCondition<TTables>): this;
714
+ where<TColumn extends AvailableColumns<TTables>>(
746
715
  column: TColumn,
747
- value: ExtractColumnType<
748
- TSchema,
749
- TTable,
750
- TColumn & string,
751
- TOriginal,
752
- TJoined
753
- >
754
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
755
- where<TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>>(
716
+ value: ExtractColumnType<TTables, TColumn & string>
717
+ ): this;
718
+ where<TColumn extends AvailableColumns<TTables>>(
756
719
  column: TColumn,
757
- operator: ComparisonOperator | "like",
758
- value: ExtractColumnType<
759
- TSchema,
760
- TTable,
761
- TColumn & string,
762
- TOriginal,
763
- TJoined
764
- >
765
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
766
- where(raw: string): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
767
- where(...args: any[]): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
720
+ operator: ComparisonOperator,
721
+ value: ExtractColumnType<TTables, TColumn & string>
722
+ ): this;
723
+ where(...args: any[]): WhereGroup<TTables> {
768
724
  this.builder.where(args[0], ...args.slice(1));
769
725
  return this;
770
726
  }
771
727
 
772
- orWhere(
773
- conditions: WhereCondition<TSchema, TTable, TOriginal, TJoined>
774
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
775
- orWhere<
776
- TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
777
- >(
728
+ // orWhere 메서드들
729
+ orWhere(conditions: WhereCondition<TTables>): this;
730
+ orWhere<TColumn extends AvailableColumns<TTables>>(
778
731
  column: TColumn,
779
- value: ExtractColumnType<
780
- TSchema,
781
- TTable,
782
- TColumn & string,
783
- TOriginal,
784
- TJoined
785
- >
786
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
787
- orWhere<
788
- TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
789
- >(
732
+ value: ExtractColumnType<TTables, TColumn & string>
733
+ ): this;
734
+ orWhere<TColumn extends AvailableColumns<TTables>>(
790
735
  column: TColumn,
791
- operator: ComparisonOperator | "like",
792
- value: ExtractColumnType<
793
- TSchema,
794
- TTable,
795
- TColumn & string,
796
- TOriginal,
797
- TJoined
798
- >
799
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
800
- orWhere(raw: string): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
801
- orWhere(...args: any[]): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
736
+ operator: ComparisonOperator,
737
+ value: ExtractColumnType<TTables, TColumn & string>
738
+ ): this;
739
+ orWhere(...args: any[]): WhereGroup<TTables> {
802
740
  this.builder.orWhere(args[0], ...args.slice(1));
803
741
  return this;
804
742
  }
805
743
 
806
- whereIn<
807
- TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
808
- >(
809
- column: TColumn,
810
- values: ExtractColumnType<
811
- TSchema,
812
- TTable,
813
- TColumn & string,
814
- TOriginal,
815
- TJoined
816
- >[]
817
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
818
- whereIn(
819
- column: string,
820
- values: any[]
821
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
822
- this.builder.whereIn(column, values);
823
- return this;
824
- }
825
-
826
- orWhereIn<
827
- TColumn extends AvailableColumns<TSchema, TTable, TOriginal, TJoined>,
828
- >(
829
- column: TColumn,
830
- values: ExtractColumnType<
831
- TSchema,
832
- TTable,
833
- TColumn & string,
834
- TOriginal,
835
- TJoined
836
- >[]
837
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined>;
838
- orWhereIn(
839
- column: string,
840
- values: any[]
841
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
842
- this.builder.orWhereIn(column, values);
843
- return this;
844
- }
845
-
846
- // 중첩 그룹 지원
847
- whereGroup(
848
- callback: (
849
- group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
850
- ) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
851
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
744
+ // 중첩 그룹
745
+ whereGroup(callback: (g: WhereGroup<TTables>) => void): this;
746
+ whereGroup(callback: (g: WhereGroup<TTables>) => void): WhereGroup<TTables> {
852
747
  this.builder.where((subBuilder) => {
853
- const subGroup = new WhereGroup<TSchema, TTable, TOriginal, TJoined>(
854
- subBuilder
855
- );
748
+ const subGroup = new WhereGroup<TTables>(subBuilder);
856
749
  callback(subGroup);
857
750
  });
858
751
  return this;
859
752
  }
860
-
753
+ orWhereGroup(callback: (g: WhereGroup<TTables>) => void): this;
861
754
  orWhereGroup(
862
- callback: (
863
- group: WhereGroup<TSchema, TTable, TOriginal, TJoined>
864
- ) => WhereGroup<TSchema, TTable, TOriginal, TJoined>
865
- ): WhereGroup<TSchema, TTable, TOriginal, TJoined> {
755
+ callback: (g: WhereGroup<TTables>) => void
756
+ ): WhereGroup<TTables> {
866
757
  this.builder.orWhere((subBuilder) => {
867
- const subGroup = new WhereGroup<TSchema, TTable, TOriginal, TJoined>(
868
- subBuilder
869
- );
758
+ const subGroup = new WhereGroup<TTables>(subBuilder);
870
759
  callback(subGroup);
871
760
  });
872
761
  return this;
@@ -874,37 +763,39 @@ class WhereGroup<
874
763
  }
875
764
 
876
765
  export class JoinClauseGroup<
877
- TSchema,
878
- TTable extends keyof TSchema | string,
879
- TOriginal = any,
880
- TJoined = EmptyRecord,
766
+ TLeft extends Record<string, any>,
767
+ TRight extends Record<string, any>,
881
768
  > {
882
769
  constructor(private callback: Knex.JoinClause) {}
883
770
 
771
+ // ON(AND): 컬럼 = 컬럼
772
+ on(left: AvailableColumns<TLeft>, right: AvailableColumns<TRight>): this;
773
+ // ON(AND): 컬럼 (연산자) 컬럼
884
774
  on(
885
- callback: (
886
- joinClause: JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>
887
- ) => void
888
- ): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>;
889
- on(
890
- column: string,
891
- value: any
892
- ): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>;
893
- on(...args: any[]): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined> {
775
+ left: AvailableColumns<TLeft>,
776
+ operator: ComparisonOperator,
777
+ right: AvailableColumns<TRight>
778
+ ): this;
779
+ // ON(AND): 콜백
780
+ on(callback: (nested: JoinClauseGroup<TLeft, TRight>) => void): this;
781
+ // ON(AND) 구현
782
+ on(...args: any[]): this {
894
783
  this.callback.on(...(args as [string, string]));
895
784
  return this;
896
785
  }
897
786
 
787
+ // ON(OR): 컬럼 = 컬럼
788
+ orOn(left: AvailableColumns<TLeft>, right: AvailableColumns<TRight>): this;
789
+ // ON(OR): 컬럼 (연산자) 컬럼
898
790
  orOn(
899
- callback: (
900
- joinClause: JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>
901
- ) => void
902
- ): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>;
903
- orOn(
904
- column: string,
905
- value: any
906
- ): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined>;
907
- orOn(...args: any[]): JoinClauseGroup<TSchema, TTable, TOriginal, TJoined> {
791
+ left: AvailableColumns<TLeft>,
792
+ operator: ComparisonOperator,
793
+ right: AvailableColumns<TRight>
794
+ ): this;
795
+ // ON(OR): 콜백
796
+ orOn(callback: (nested: JoinClauseGroup<TLeft, TRight>) => void): this;
797
+ // ON(OR) 구현
798
+ orOn(...args: any[]): this {
908
799
  this.callback.orOn(...(args as [string, string]));
909
800
  return this;
910
801
  }