sonamu 0.5.6 → 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 (40) 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/database/db.d.ts +3 -0
  9. package/dist/database/db.d.ts.map +1 -1
  10. package/dist/database/db.js +1 -1
  11. package/dist/database/db.js.map +1 -1
  12. package/dist/database/puri-wrapper.d.ts +22 -10
  13. package/dist/database/puri-wrapper.d.ts.map +1 -1
  14. package/dist/database/puri-wrapper.js +1 -1
  15. package/dist/database/puri-wrapper.js.map +1 -1
  16. package/dist/database/puri.d.ts +91 -66
  17. package/dist/database/puri.d.ts.map +1 -1
  18. package/dist/database/puri.js +1 -1
  19. package/dist/database/puri.js.map +1 -1
  20. package/dist/database/puri.types.d.ts +28 -42
  21. package/dist/database/puri.types.d.ts.map +1 -1
  22. package/dist/database/transaction-context.d.ts +3 -3
  23. package/dist/database/transaction-context.d.ts.map +1 -1
  24. package/dist/database/transaction-context.js.map +1 -1
  25. package/dist/templates/service.template.d.ts.map +1 -1
  26. package/dist/templates/service.template.js +1 -1
  27. package/dist/templates/service.template.js.map +1 -1
  28. package/dist/types/types.d.ts +1 -1
  29. package/dist/types/types.d.ts.map +1 -1
  30. package/dist/types/types.js.map +1 -1
  31. package/package.json +1 -2
  32. package/src/api/decorators.ts +14 -5
  33. package/src/api/sonamu.ts +21 -20
  34. package/src/database/db.ts +44 -9
  35. package/src/database/puri-wrapper.ts +104 -26
  36. package/src/database/puri.ts +429 -557
  37. package/src/database/puri.types.ts +99 -202
  38. package/src/database/transaction-context.ts +4 -4
  39. package/src/templates/service.template.ts +10 -1
  40. package/src/types/types.ts +1 -1
@@ -1,130 +1,41 @@
1
- export type ComparisonOperator = "=" | ">" | ">=" | "<" | "<=" | "<>" | "!=";
2
- export type Expand<T> = T extends any[]
3
- ? { [K in keyof T[0]]: T[0][K] }[] // 배열이면 첫 번째 요소를 Expand하고 배열로 감쌈
4
- : T extends object
5
- ? { [K in keyof T]: T[K] }
6
- : T;
7
-
8
- // EmptyRecord가 남아있으면 AvailableColumns 추론이 제대로 되지 않음 (EmptyRecord를 {}로 변경하면 정상 동작함)
9
- export type MergeJoined<TExisting, TNew> = TExisting extends EmptyRecord
10
- ? TNew // 첫 join: EmptyRecord 제거하고 대체
11
- : TExisting & TNew; // 이후 join: 누적
12
-
13
- type DeepEqual<T, U> = [T] extends [U]
14
- ? [U] extends [T]
15
- ? true
16
- : false
17
- : false;
18
- type Extends<T, U> =
19
- DeepEqual<T, Record<string, never>> extends true
20
- ? false
21
- : T extends U
22
- ? true
23
- : false;
24
- type NullableToOptional<T> = {
25
- [K in keyof T as T[K] extends null | undefined ? K : never]?: Exclude<
26
- T[K],
27
- null | undefined
28
- >;
29
- } & Partial<{
30
- [K in keyof T as T[K] extends null | undefined ? never : K]: T[K];
31
- }>;
32
-
33
- // Join 등이 Empty 상태일 떄 {}가 아니라 EmptyRecord를 써서
34
- export type EmptyRecord = Record<string, never>;
1
+ // 메타데이터 컬럼 제외
2
+ type ExcludeMetadataColumns<T> = T extends {
3
+ __fulltext__: readonly (infer _Col)[];
4
+ }
5
+ ? Omit<T, "__fulltext__">
6
+ : T;
35
7
 
8
+ // TTables의 모든 테이블에서 사용 가능한 컬럼 경로
9
+ export type AvailableColumns<TTables extends Record<string, any>> =
10
+ | {
11
+ [TAlias in keyof TTables]: `${TAlias & string}.${ExcludeMetadataColumns<keyof TTables[TAlias]> & string}`;
12
+ }[keyof TTables]
13
+ | (IsSingleKey<TTables> extends true
14
+ ? ExcludeMetadataColumns<keyof TTables[keyof TTables]> // 단일 테이블이면 컬럼명만도 허용
15
+ : never);
36
16
  // Group By, Order By, Having 등에서 선택 가능한 컬럼
37
17
  export type ResultAvailableColumns<
38
- TSchema,
39
- T extends keyof TSchema | string,
40
- TOriginal = any,
18
+ TTables extends Record<string, any>,
41
19
  TResult = any,
42
- TJoined = EmptyRecord,
43
- > = Exclude<
44
- | AvailableColumns<TSchema, T, TOriginal, TJoined>
45
- | `${keyof TResult & string}`,
46
- "__fulltext__" | `${T & string}.__fulltext__`
47
- >;
48
-
49
- // 사용 가능한 컬럼 경로 타입 (메인 테이블 + 조인된 테이블들)
50
- export type AvailableColumns<
51
- TSchema,
52
- T extends keyof TSchema | string,
53
- TOriginal = any,
54
- TJoined = EmptyRecord,
55
- > = T extends keyof TSchema
56
- ? // 기존 테이블 케이스
57
- | (Extends<TJoined, Record<string, any>> extends false
58
- ? // 이게 TSchema[T]에 존재하면
59
- keyof TSchema[T]
60
- : {
61
- [K in keyof TJoined]: TJoined[K] extends Record<string, any>
62
- ? `${string & K}.${keyof TJoined[K] & string}`
63
- : never;
64
- }[keyof TJoined])
65
- | `${T & string}.${keyof TSchema[T] & string}`
66
- : // 서브쿼리 케이스 (T는 alias string)
67
- | keyof TOriginal
68
- | `${T & string}.${keyof TOriginal & string}`
69
- | (Extends<TJoined, Record<string, any>> extends true
70
- ? {
71
- [K in keyof TJoined]: TJoined[K] extends Record<string, any>
72
- ? `${string & K}.${keyof TJoined[K] & string}`
73
- : never;
74
- }[keyof TJoined]
75
- : never);
20
+ > = AvailableColumns<TTables> | `${keyof TResult & string}`;
76
21
 
77
- // 컬럼 경로에서 타입 추출
78
- export type ExtractColumnType<
79
- TSchema,
80
- T extends keyof TSchema | string,
81
- Path extends string,
82
- TOriginal = any,
83
- TJoined = EmptyRecord,
84
- > = T extends keyof TSchema
85
- ? // 기존 테이블 케이스
86
- Path extends keyof TSchema[T]
87
- ? TSchema[T][Path] // 메인 테이블 컬럼
88
- : Path extends `${T & string}.${infer Column}`
89
- ? Column extends keyof TSchema[T]
90
- ? TSchema[T][Column]
91
- : never
92
- : Path extends `${infer Table}.${infer Column}`
93
- ? Table extends keyof TJoined
94
- ? TJoined[Table] extends Record<string, any>
95
- ? Column extends keyof TJoined[Table]
96
- ? TJoined[Table][Column]
97
- : never
98
- : never
99
- : never
100
- : never
101
- : // 서브쿼리 케이스 (T는 alias)
102
- Path extends `${T & string}.${infer Column}`
103
- ? Column extends keyof TOriginal
104
- ? TOriginal[Column] // 서브쿼리 alias.컬럼
105
- : never
106
- : Path extends `${infer Table}.${infer Column}`
107
- ? Table extends keyof TJoined
108
- ? TJoined[Table] extends Record<string, any>
109
- ? Column extends keyof TJoined[Table]
110
- ? TJoined[Table][Column]
111
- : never
112
- : never
113
- : never
114
- : Path extends keyof TOriginal
115
- ? TOriginal[Path] // 서브쿼리 컬럼 직접 접근 (가장 마지막에)
116
- : never;
22
+ // Select 타입 확장
23
+ export type SelectValue<TTables extends Record<string, any>> =
24
+ | AvailableColumns<TTables>
25
+ | SqlExpression<"string" | "number" | "boolean" | "date">;
117
26
 
118
- // SQL 함수 타입 정의
119
- export type SqlFunction<T extends "string" | "number" | "boolean" | "date"> = {
120
- _type: "sql_function";
121
- _return: T;
122
- _sql: string;
123
- };
27
+ // Select 객체 타입 (현재는 컬럼 경로만 지원)
28
+ export type SelectObject<TTables extends Record<string, any>> = Record<
29
+ string,
30
+ SelectValue<TTables> // AvailableColumns 대신
31
+ >;
124
32
 
125
- // SQL 함수 결과에서 타입 추출
126
- type ExtractSqlType<T> =
127
- T extends SqlFunction<infer R>
33
+ // Select 결과 타입 추론
34
+ export type ParseSelectObject<
35
+ TTables extends Record<string, any>,
36
+ TSelect extends SelectObject<TTables>,
37
+ > = {
38
+ [K in keyof TSelect]: TSelect[K] extends SqlExpression<infer R>
128
39
  ? R extends "string"
129
40
  ? string
130
41
  : R extends "number"
@@ -134,94 +45,80 @@ type ExtractSqlType<T> =
134
45
  : R extends "date"
135
46
  ? Date
136
47
  : never
48
+ : ExtractColumnType<TTables, TSelect[K] & string>;
49
+ };
50
+
51
+ // 컬럼 경로에서 타입 추출
52
+ export type ExtractColumnType<
53
+ TTables extends Record<string, any>,
54
+ Path extends string,
55
+ > = Path extends `${infer TAlias}.${infer TColumn}`
56
+ ? TAlias extends keyof TTables
57
+ ? TColumn extends keyof TTables[TAlias]
58
+ ? TTables[TAlias][TColumn]
59
+ : never
60
+ : never
61
+ : IsSingleKey<TTables> extends true // 추가
62
+ ? Path extends keyof TTables[keyof TTables]
63
+ ? TTables[keyof TTables][Path]
64
+ : never
137
65
  : never;
66
+ // Where 조건 객체 타입
67
+ // 예: { "u.id": 1, "u.status": "active" }
68
+ export type WhereCondition<TTables extends Record<string, any>> = {
69
+ [key in AvailableColumns<TTables>]?: ExtractColumnType<TTables, key & string>;
70
+ };
138
71
 
139
- // Select 타입 확장
140
- export type SelectValue<
141
- TSchema,
142
- T extends keyof TSchema | string,
143
- TOriginal = any,
144
- TJoined = EmptyRecord,
145
- > =
146
- | AvailableColumns<TSchema, T, TOriginal, TJoined> // 기존 컬럼
147
- | SqlFunction<"string" | "number" | "boolean" | "date">; // SQL 함수
72
+ // Fulltext index 컬럼 추출 타입
73
+ export type FulltextColumns<TTables extends Record<string, any>> = {
74
+ [TAlias in keyof TTables]: TTables[TAlias] extends {
75
+ __fulltext__: readonly (infer Col)[];
76
+ }
77
+ ? Col extends string
78
+ ? `${TAlias & string}.${Col}`
79
+ : never
80
+ : never;
81
+ }[keyof TTables];
148
82
 
149
- // Select 객체 타입 정의
150
- export type SelectObject<
151
- TSchema,
152
- T extends keyof TSchema | string,
153
- TOriginal = any,
154
- TJoined = EmptyRecord,
155
- > = Record<string, SelectValue<TSchema, T, TOriginal, TJoined>>;
83
+ // 비교 연산자
84
+ export type ComparisonOperator = "=" | ">" | ">=" | "<" | "<=" | "<>" | "!=";
156
85
 
157
- // Select 결과 타입 추론
158
- export type ParseSelectObject<
159
- TSchema,
160
- T extends keyof TSchema | string,
161
- S extends SelectObject<TSchema, T, TOriginal, TJoined>,
162
- TOriginal = any,
163
- TJoined = EmptyRecord,
164
- > = {
165
- [K in keyof S]: S[K] extends SqlFunction<any>
166
- ? ExtractSqlType<S[K]> // SQL 함수면 타입 추출
167
- : ExtractColumnType<TSchema, T, S[K] & string, TOriginal, TJoined>;
168
- };
86
+ // SQL Expression 타입 정의
87
+ export type SqlExpression<T extends "string" | "number" | "boolean" | "date"> =
88
+ {
89
+ _type: "sql_expression"; // 또는 "computed_value"
90
+ _return: T;
91
+ _sql: string;
92
+ };
169
93
 
170
- // Where 조건 타입 (조인된 테이블 컬럼도 포함)
171
- export type WhereCondition<
172
- TSchema,
173
- T extends keyof TSchema | string,
174
- TOriginal = any,
175
- TJoined = EmptyRecord,
176
- > = {
177
- [key in AvailableColumns<TSchema, T, TOriginal, TJoined>]?: ExtractColumnType<
178
- TSchema,
179
- T,
180
- key & string,
181
- TOriginal,
182
- TJoined
183
- >;
184
- };
94
+ // 결과 타입 가독성을 위한 타입 확장
95
+ export type Expand<T> = T extends any[]
96
+ ? { [K in keyof T[0]]: T[0][K] }[] // 배열이면 첫 번째 요소를 Expand하고 배열로 감쌈
97
+ : T extends object
98
+ ? { [K in keyof T]: T[K] }
99
+ : T;
100
+
101
+ type IsSingleKey<TTables extends Record<string, any>> =
102
+ keyof TTables extends infer K
103
+ ? K extends keyof TTables
104
+ ? keyof TTables extends K // 역방향 체크로 단일 키 확인
105
+ ? true
106
+ : false
107
+ : false
108
+ : false;
109
+
110
+ export type SingleTableValue<TTables extends Record<string, any>> =
111
+ IsSingleKey<TTables> extends true ? TTables[keyof TTables] : never;
185
112
 
186
- // Fulltext index 컬럼 추출 타입 (메인 테이블 + 조인된 테이블)
187
- export type FulltextColumns<
188
- TSchema,
189
- T extends keyof TSchema | string,
190
- TOriginal = any,
191
- TJoined = EmptyRecord,
192
- > = T extends keyof TSchema
193
- ? // 기존 테이블 케이스
194
- | (TSchema[T] extends { __fulltext__: readonly (infer Col)[] }
195
- ? Col & string
196
- : never)
197
- | (TSchema[T] extends { __fulltext__: readonly (infer Col)[] }
198
- ? `${T & string}.${Col & string}`
199
- : never)
200
- | (TJoined extends Record<string, any>
201
- ? {
202
- [K in keyof TJoined]: TJoined[K] extends {
203
- __fulltext__: readonly (infer Col)[];
204
- }
205
- ? (Col & string) | `${string & K}.${Col & string}`
206
- : never;
207
- }[keyof TJoined]
208
- : never)
209
- : // 서브쿼리 케이스 (T는 alias)
210
- | (TOriginal extends { __fulltext__: readonly (infer Col)[] }
211
- ? Col & string
212
- : never)
213
- | (TOriginal extends { __fulltext__: readonly (infer Col)[] }
214
- ? `${T & string}.${Col & string}`
215
- : never)
216
- | (TJoined extends Record<string, any>
217
- ? {
218
- [K in keyof TJoined]: TJoined[K] extends {
219
- __fulltext__: readonly (infer Col)[];
220
- }
221
- ? (Col & string) | `${string & K}.${Col & string}`
222
- : never;
223
- }[keyof TJoined]
224
- : never);
113
+ // Nullable을 Optional로 변환
114
+ type NullableToOptional<T> = {
115
+ [K in keyof T as T[K] extends null | undefined ? K : never]?: Exclude<
116
+ T[K],
117
+ null | undefined
118
+ >;
119
+ } & Partial<{
120
+ [K in keyof T as T[K] extends null | undefined ? never : K]: T[K];
121
+ }>;
225
122
 
226
123
  // Insert 타입: id, created_at 제외
227
124
  export type InsertData<T> = NullableToOptional<
@@ -1,14 +1,14 @@
1
- import type { PuriWrapper } from "./puri-wrapper";
1
+ import type { PuriTransactionWrapper } from "./puri-wrapper";
2
2
  import type { DBPreset } from "./db";
3
3
 
4
4
  export class TransactionContext {
5
- private transactions: Map<DBPreset, PuriWrapper> = new Map();
5
+ private transactions: Map<DBPreset, PuriTransactionWrapper> = new Map();
6
6
 
7
- getTransaction(preset: DBPreset): PuriWrapper | undefined {
7
+ getTransaction(preset: DBPreset): PuriTransactionWrapper | undefined {
8
8
  return this.transactions.get(preset);
9
9
  }
10
10
 
11
- setTransaction(preset: DBPreset, trx: PuriWrapper): void {
11
+ setTransaction(preset: DBPreset, trx: PuriTransactionWrapper): void {
12
12
  this.transactions.set(preset, trx);
13
13
  }
14
14
 
@@ -195,6 +195,7 @@ export async function ${methodNameAxios}${typeParamsDef}(${paramsDef}): Promise<
195
195
  return fetch({
196
196
  method: "GET",
197
197
  url: \`${apiBaseUrl}?\${qs.stringify(${payloadDef})}\`,
198
+ ${api.options.timeout ? `signal: AbortSignal.timeout(${api.options.timeout}),` : ""}
198
199
  });
199
200
  }
200
201
  `.trim();
@@ -205,6 +206,7 @@ export async function ${methodNameAxios}${typeParamsDef}(${paramsDef}): Promise<
205
206
  method: '${api.options.httpMethod}',
206
207
  url: \`${apiBaseUrl}\`,
207
208
  data: ${payloadDef},
209
+ ${api.options.timeout ? `signal: AbortSignal.timeout(${api.options.timeout}),` : ""}
208
210
  });
209
211
  }
210
212
  `.trim();
@@ -256,6 +258,7 @@ export async function ${api.methodName}${typeParamsDef}(
256
258
  },
257
259
  onUploadProgress,
258
260
  data: formData,
261
+ ${api.options.timeout ? `signal: AbortSignal.timeout(${api.options.timeout}),` : ""}
259
262
  });
260
263
  }
261
264
  `.trim();
@@ -283,6 +286,8 @@ export async function ${api.methodName}${typeParamsDef}(
283
286
  ${payloadDef},
284
287
  ], swrOptions?.conditional)${
285
288
  api.options.httpMethod === "POST" ? ", swrPostFetcher" : ""
289
+ }${
290
+ api.options.timeout ? `, { loadingTimeout: ${api.options.timeout} }` : ""
286
291
  });
287
292
  }`;
288
293
  }
@@ -296,7 +301,11 @@ export async function ${api.methodName}${typeParamsDef}(
296
301
  ) {
297
302
  return `
298
303
  export async function ${api.methodName}${typeParamsDef}(${paramsDef}): Promise<Response> {
299
- return window.fetch(\`${apiBaseUrl}?\${qs.stringify(${payloadDef})}\`);
304
+ return window.fetch(\`${apiBaseUrl}?\${qs.stringify(${payloadDef})}\`${
305
+ api.options.timeout
306
+ ? `, { signal: AbortSignal.timeout(${api.options.timeout}) }`
307
+ : ""
308
+ });
300
309
  }
301
310
  `.trim();
302
311
  }
@@ -847,7 +847,7 @@ export type SonamuFastifyConfig = {
847
847
  AuthContext,
848
848
  request: FastifyRequest,
849
849
  reply: FastifyReply
850
- ) => Context;
850
+ ) => Context | Promise<Context>;
851
851
  guardHandler: (
852
852
  guard: GuardKey,
853
853
  request: FastifyRequest,