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
@@ -5,8 +5,8 @@ import { DatabaseSchemaExtend } from "../types/types";
5
5
  import chalk from "chalk";
6
6
  import { DBPreset } from "./db";
7
7
 
8
- type TableName<DBSchema extends DatabaseSchemaExtend> = Extract<
9
- keyof DBSchema,
8
+ type TableName<TSchema extends DatabaseSchemaExtend> = Extract<
9
+ keyof TSchema,
10
10
  string
11
11
  >;
12
12
 
@@ -17,7 +17,7 @@ export type TransactionalOptions = {
17
17
  };
18
18
 
19
19
  export class PuriWrapper<
20
- DBSchema extends DatabaseSchemaExtend = DatabaseSchemaExtend,
20
+ TSchema extends DatabaseSchemaExtend = DatabaseSchemaExtend,
21
21
  > {
22
22
  constructor(
23
23
  public knex: Knex,
@@ -27,60 +27,138 @@ export class PuriWrapper<
27
27
  raw(sql: string): Knex.Raw {
28
28
  return this.knex.raw(sql);
29
29
  }
30
- // 기존: 테이블로 시작
31
- table<TTable extends TableName<DBSchema>>(
30
+
31
+ // 테이블명으로 시작
32
+ from<TTable extends keyof TSchema>(
32
33
  tableName: TTable
33
- ): Puri<DBSchema, TTable> {
34
- return new Puri(this.knex, tableName as any);
34
+ ): Puri<
35
+ TSchema,
36
+ Record<TTable, TSchema[TTable]>,
37
+ Omit<TSchema[TTable], "__fulltext__">
38
+ >;
39
+ // 테이블명 + Alias로 시작
40
+ from<TTable extends keyof TSchema, TAlias extends string>(spec: {
41
+ [K in TAlias]: TTable;
42
+ }): Puri<
43
+ TSchema,
44
+ Record<TAlias, TSchema[TTable]>,
45
+ Omit<TSchema[TTable], "__fulltext__">
46
+ >;
47
+ // 서브쿼리로 시작
48
+ from<TAlias extends string, TSubResult>(spec: {
49
+ [K in TAlias]: Puri<TSchema, any, TSubResult>;
50
+ }): Puri<
51
+ TSchema,
52
+ Record<TAlias, TSubResult>,
53
+ Omit<TSubResult, "__fulltext__">
54
+ >;
55
+ from(spec: any): any {
56
+ return new Puri(this.knex, spec);
35
57
  }
36
58
 
37
- // 새로 추가: 서브쿼리로 시작
38
- fromSubquery<TSubResult, TAlias extends string>(
39
- subquery: Puri<DBSchema, any, any, TSubResult, any>,
40
- alias: TAlias extends string ? TAlias : never
41
- ): Puri<DBSchema, TAlias, TSubResult, TSubResult, {}> {
42
- return new Puri(this.knex, subquery, alias);
59
+ // 테이블명으로 시작
60
+ table<TTable extends keyof TSchema>(
61
+ tableName: TTable
62
+ ): Puri<
63
+ TSchema,
64
+ Record<TTable, TSchema[TTable]>,
65
+ Omit<TSchema[TTable], "__fulltext__">
66
+ >;
67
+ // 테이블명 + Alias로 시작
68
+ table<TTable extends keyof TSchema, TAlias extends string>(spec: {
69
+ [K in TAlias]: TTable;
70
+ }): Puri<
71
+ TSchema,
72
+ Record<TAlias, TSchema[TTable]>,
73
+ Omit<TSchema[TTable], "__fulltext__">
74
+ >;
75
+ // 서브쿼리로 시작
76
+ table<TAlias extends string, TSubResult>(spec: {
77
+ [K in TAlias]: Puri<TSchema, any, TSubResult>;
78
+ }): Puri<
79
+ TSchema,
80
+ Record<TAlias, TSubResult>,
81
+ Omit<TSubResult, "__fulltext__">
82
+ >;
83
+ table(spec: any): any {
84
+ return new Puri(this.knex, spec);
43
85
  }
44
86
 
45
87
  async transaction<T>(
46
88
  callback: (trx: PuriTransactionWrapper) => Promise<T>,
47
89
  options: TransactionalOptions = {}
48
90
  ): Promise<T> {
49
- const { isolation, readOnly } = options;
91
+ const { isolation, readOnly, dbPreset = "w" } = options;
92
+
93
+ // @transactional 데코레이터와 동일한 로직: 이미 트랜잭션 컨텍스트가 있는지 확인
94
+ const { DB } = await import("./db");
95
+ const existingContext = DB.transactionStorage.getStore();
96
+
97
+ // AsyncLocalStorage 컨텍스트가 없거나 해당 preset의 트랜잭션이 없으면 새로 시작
98
+ const startTransaction = async (
99
+ knex: Knex | Knex.Transaction,
100
+ upsertBuilder: UpsertBuilder
101
+ ) => {
102
+ return knex.transaction(
103
+ async (trx) => {
104
+ const trxWrapper = new PuriTransactionWrapper(trx, upsertBuilder);
105
+
106
+ // TransactionContext에 트랜잭션 저장
107
+ DB.getTransactionContext().setTransaction(dbPreset, trxWrapper);
108
+
109
+ try {
110
+ return await callback(trxWrapper);
111
+ } finally {
112
+ // 트랜잭션 제거
113
+ DB.getTransactionContext().deleteTransaction(dbPreset);
114
+ }
115
+ },
116
+ { isolationLevel: isolation, readOnly }
117
+ );
118
+ };
119
+
120
+ // AsyncLocalStorage 컨텍스트가 없으면 새로 생성
121
+ if (!existingContext) {
122
+ return DB.runWithTransaction(() =>
123
+ startTransaction(this.knex, this.upsertBuilder)
124
+ );
125
+ }
50
126
 
51
- return this.knex.transaction(
52
- async (trx) => {
53
- return callback(new PuriTransactionWrapper(trx, this.upsertBuilder));
54
- },
55
- { isolationLevel: isolation, readOnly }
56
- );
127
+ // 해당 preset의 트랜잭션이 이미 있으면 SAVEPOINT로 중첩 트랜잭션 생성
128
+ const existingTrx = existingContext.getTransaction(dbPreset);
129
+ if (existingTrx) {
130
+ return startTransaction(existingTrx.trx, existingTrx.upsertBuilder);
131
+ } else {
132
+ // 컨텍스트는 있지만 이 preset의 트랜잭션은 없는 경우 (같은 컨텍스트 내에서 실행)
133
+ return startTransaction(this.knex, this.upsertBuilder);
134
+ }
57
135
  }
58
136
 
59
- ubRegister<TTable extends TableName<DBSchema>>(
137
+ ubRegister<TTable extends TableName<TSchema>>(
60
138
  tableName: TTable,
61
139
  row: Partial<{
62
- [K in keyof DBSchema[TTable]]: DBSchema[TTable][K] | UBRef;
140
+ [K in keyof TSchema[TTable]]: TSchema[TTable][K] | UBRef;
63
141
  }>
64
142
  ): UBRef {
65
143
  return this.upsertBuilder.register(tableName, row);
66
144
  }
67
145
 
68
146
  ubUpsert(
69
- tableName: TableName<DBSchema>,
147
+ tableName: TableName<TSchema>,
70
148
  chunkSize?: number
71
149
  ): Promise<number[]> {
72
150
  return this.upsertBuilder.upsert(this.knex, tableName, chunkSize);
73
151
  }
74
152
 
75
153
  ubInsertOnly(
76
- tableName: TableName<DBSchema>,
154
+ tableName: TableName<TSchema>,
77
155
  chunkSize?: number
78
156
  ): Promise<number[]> {
79
157
  return this.upsertBuilder.insertOnly(this.knex, tableName, chunkSize);
80
158
  }
81
159
 
82
160
  ubUpsertOrInsert(
83
- tableName: TableName<DBSchema>,
161
+ tableName: TableName<TSchema>,
84
162
  mode: "upsert" | "insert",
85
163
  chunkSize?: number
86
164
  ): Promise<number[]> {
@@ -93,7 +171,7 @@ export class PuriWrapper<
93
171
  }
94
172
 
95
173
  ubUpdateBatch(
96
- tableName: TableName<DBSchema>,
174
+ tableName: TableName<TSchema>,
97
175
  options?: { chunkSize?: number; where?: string | string[] }
98
176
  ): Promise<void> {
99
177
  return this.upsertBuilder.updateBatch(this.knex, tableName, options);