@fluojs/drizzle 1.1.0 → 1.1.1
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.
- package/README.ko.md +25 -16
- package/README.md +25 -16
- package/dist/database.d.ts +4 -1
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +34 -24
- package/dist/transaction.d.ts +4 -3
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +5 -4
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
package/README.ko.md
CHANGED
|
@@ -80,41 +80,50 @@ export class AppModule {}
|
|
|
80
80
|
`@Transaction()` 데코레이터는 서비스 레이어에서 트랜잭션 경계를 정의하는 권장 방법입니다. 이 데코레이터가 적용된 메서드 내부에서 발생하는 모든 리포지토리 호출은 동일한 Drizzle 트랜잭션을 공유합니다.
|
|
81
81
|
|
|
82
82
|
```ts
|
|
83
|
+
import { Inject } from '@fluojs/core';
|
|
83
84
|
import { Transaction, DrizzleDatabase, type DrizzleDatabaseFacade } from '@fluojs/drizzle';
|
|
84
85
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
85
86
|
import { users, profiles } from './schema';
|
|
86
87
|
|
|
87
88
|
type AppDatabase = ReturnType<typeof drizzle>;
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
constructor(private readonly repo: UserRepository) {}
|
|
91
|
-
|
|
92
|
-
@Transaction()
|
|
93
|
-
async onboardUser(dto: any) {
|
|
94
|
-
const user = await this.repo.create(dto);
|
|
95
|
-
await this.repo.initProfile(user.id);
|
|
96
|
-
return user;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
90
|
+
@Inject(DrizzleDatabase)
|
|
100
91
|
export class UserRepository {
|
|
101
92
|
constructor(private readonly db: DrizzleDatabaseFacade<AppDatabase>) {}
|
|
102
93
|
|
|
103
94
|
async create(data: any) {
|
|
104
95
|
// facade 타입은 표준 Drizzle 메서드를 노출합니다.
|
|
105
96
|
// @Transaction() 내부에서 호출되면 자동으로 활성 트랜잭션에 참여합니다.
|
|
106
|
-
|
|
97
|
+
const [user] = await this.db.insert(users).values(data).returning();
|
|
98
|
+
|
|
99
|
+
if (!user) {
|
|
100
|
+
throw new Error('User insert did not return a row.');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return user;
|
|
107
104
|
}
|
|
108
105
|
|
|
109
106
|
async initProfile(userId: string) {
|
|
110
107
|
return this.db.insert(profiles).values({ userId });
|
|
111
108
|
}
|
|
112
109
|
}
|
|
110
|
+
|
|
111
|
+
@Inject(UserRepository)
|
|
112
|
+
export class UserService {
|
|
113
|
+
constructor(private readonly repo: UserRepository) {}
|
|
114
|
+
|
|
115
|
+
@Transaction()
|
|
116
|
+
async onboardUser(dto: any) {
|
|
117
|
+
const user = await this.repo.create(dto);
|
|
118
|
+
await this.repo.initProfile(user.id);
|
|
119
|
+
return user;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
113
122
|
```
|
|
114
123
|
|
|
115
124
|
`@Transaction()` 메서드 호출은 재진입(reentrant)이 가능합니다. 데코레이터가 적용된 메서드가 다른 데코레이터 적용 메서드를 호출하더라도 하나의 동일한 Drizzle 트랜잭션 안에서 실행됩니다.
|
|
116
125
|
|
|
117
|
-
기본적으로 `@Transaction()`은 작은 host-object heuristic으로 대상을 고릅니다. 먼저 `this.db`를 확인하고, 그다음 데코레이터가 붙은 인스턴스의 직접 property, 마지막으로 그 값들의 중첩 `.db` property 중 `transaction(...)` 메서드를 노출하는 첫 값을 사용합니다. 이 덕분에 `constructor(private readonly db: DrizzleDatabase<...>)` 같은 일반
|
|
126
|
+
기본적으로 `@Transaction()`은 작은 host-object heuristic으로 대상을 고릅니다. 먼저 `this.db`를 확인하고, 그다음 데코레이터가 붙은 인스턴스의 직접 property, 마지막으로 그 값들의 중첩 `.db` property 중 `transaction(...)` 메서드를 노출하는 첫 값을 사용합니다. 이 후보들이 모두 맞지 않으면 데코레이터가 붙은 인스턴스 자체를 transaction 대상으로 사용합니다. 이 덕분에 `constructor(private readonly db: DrizzleDatabase<...>)` 같은 일반 서비스와 자체 facade host는 간결하게 유지할 수 있지만, 하나의 서비스가 Drizzle wrapper를 둘 이상 소유한다면 property 순서에 의존하지 마세요. 데코레이터가 붙은 host가 여러 transaction-capable client를 갖거나 `.db`를 노출하는 repository를 감싸는 경우에는 `@Transaction((self) => self.ordersDb)` 또는 `@Transaction((self) => self.analyticsDb, options)`처럼 명시적 accessor를 전달하세요.
|
|
118
127
|
|
|
119
128
|
### 수동 트랜잭션과 current()
|
|
120
129
|
|
|
@@ -182,11 +191,11 @@ export class CheckoutController {
|
|
|
182
191
|
}
|
|
183
192
|
```
|
|
184
193
|
|
|
185
|
-
import할 수 있는 Drizzle `*TransactionInterceptor` export는 없습니다. 기존 NestJS interceptor 설계는 대부분의 transaction boundary를 서비스로 옮기고, 전체 request 작업이 서비스 메서드 하나가 아니라 같은 boundary를 공유해야 하는 드문 controller-level 호환성 사례에만 명시적 `requestTransaction(...)`을 남기세요.
|
|
194
|
+
import할 수 있는 Drizzle `*TransactionInterceptor` export는 없습니다. 기존 NestJS interceptor 설계는 대부분의 transaction boundary를 서비스로 옮기고, 전체 request 작업이 서비스 메서드 하나가 아니라 같은 boundary를 공유해야 하는 드문 controller-level 호환성 사례에만 명시적 `requestTransaction(...)`을 남기세요. controller가 명시적 `DrizzleDatabase` 대상을 소유한다면 controller method에 `@Transaction()`을 붙이는 방식도 호환성 경로로 유지되지만, request `AbortSignal`을 직접 받을 수 있는 `requestTransaction(...)`이 더 명확한 request-wide API입니다.
|
|
186
195
|
|
|
187
196
|
### 종료와 상태 계약
|
|
188
197
|
|
|
189
|
-
애플리케이션 종료 중에는 `DrizzleDatabase`가 아직 활성 상태인 요청 트랜잭션을 abort하고, 열린 요청 및 수동 transaction callback이 settle되거나 rollback될 때까지 기다린 뒤 선택적 `dispose(database)` hook을 실행합니다.
|
|
198
|
+
애플리케이션 종료 중에는 `DrizzleDatabase`가 아직 활성 상태인 요청 트랜잭션을 abort하고, 열린 요청 및 수동 transaction callback이 settle되거나 rollback될 때까지 기다린 뒤 선택적 `dispose(database)` hook을 실행합니다. 여기에는 `database.transaction(...)`을 사용할 수 없고 `strictTransactions`가 `false`일 때의 fail-open 수동 `transaction(...)` callback도 포함되므로, 직접 실행 fallback도 pool이나 외부 관리 리소스를 닫기 전에 drain됩니다.
|
|
190
199
|
기존 요청 boundary 안에서 열린 중첩 `requestTransaction(...)` 호출은 활성 Drizzle transaction을 재사용하면서도 ambient request abort signal을 관찰합니다. 기존 수동 transaction boundary 안에서 열린 중첩 `requestTransaction(...)` 호출도 두 번째 Drizzle transaction을 열지 않고 shutdown settlement tracking에 참여하며, 해당 settlement handle은 바깥 수동 transaction이 settle될 때까지 tracking에 남아 shutdown이 `dispose(database)`를 실행하기 전에 그 바깥 경계까지 drain하게 합니다. 단, platform status activity count는 더 짧게 유지됩니다. 중첩 request callback이 settle되는 즉시, 바깥 수동 transaction이 계속 실행 중이어도 `details.activeRequestTransactions`는 감소합니다.
|
|
191
200
|
종료가 시작된 뒤 새 `transaction(...)` 및 `requestTransaction(...)` 호출은 거부되므로, 종료 boundary를 지난 뒤 시작되는 늦은 트랜잭션보다 dispose가 먼저 실행되는 상황을 방지합니다.
|
|
192
201
|
요청 callback이 완료된 뒤 underlying Drizzle transaction runner가 commit 또는 rollback을 끝내기 전에 request signal이 abort되면, `requestTransaction(...)`은 먼저 해당 runner가 settle될 때까지 기다린 다음 abort reason으로 reject합니다. 이 동작은 Drizzle cleanup을 request cancellation과 직렬화하면서, 완료된 callback 결과를 반환하는 대신 늦은 request abort를 caller에게 드러냅니다.
|
|
@@ -235,7 +244,7 @@ defineModule(ManualDrizzleModule, {
|
|
|
235
244
|
|
|
236
245
|
provider가 `current()`, `transaction(...)`, `requestTransaction(...)`, `createPlatformStatusSnapshot()` 같은 wrapper 메서드만 필요로 하면 `DrizzleDatabase<TDatabase>`를 사용하세요. 리포지토리 주입에서 Drizzle query 메서드를 직접 호출해야 한다면 `DrizzleDatabaseFacade<TDatabase>`를 사용합니다. 이 facade는 활성 트랜잭션 handle이 있으면 그 handle로, 없으면 root handle로 호출을 전달합니다. `DrizzleDatabase.createFacade(...)`는 module provider wiring을 위한 low-level compatibility helper로 유지됩니다. 애플리케이션 코드는 `DrizzleModule.forRoot(...)` / `forRootAsync(...)`를 우선 사용하세요.
|
|
237
246
|
|
|
238
|
-
`Transaction`은 서비스 계층 트랜잭션 경계를 위한 표준 TC39 method decorator입니다. 데코레이터가 붙은 host에서 `this.db`, 직접 property, 중첩 `.db` property 순서로 transaction-capable 대상을 resolve
|
|
247
|
+
`Transaction`은 서비스 계층 트랜잭션 경계를 위한 표준 TC39 method decorator입니다. 데코레이터가 붙은 host에서 `this.db`, 직접 property, 중첩 `.db` property 순서로 transaction-capable 대상을 resolve한 뒤, 후보가 없으면 데코레이터가 붙은 인스턴스 자체로 fallback합니다. 명시적 client 선택에는 accessor를 받을 수 있으며, 외부 경계에는 Drizzle transaction option을 전달할 수 있습니다.
|
|
239
248
|
|
|
240
249
|
### `DrizzleModule`
|
|
241
250
|
|
package/README.md
CHANGED
|
@@ -80,41 +80,50 @@ export class AppModule {}
|
|
|
80
80
|
The `@Transaction()` decorator is the recommended way to define transaction boundaries in your service layer. It ensures that all repository calls made within the decorated method share the same Drizzle transaction.
|
|
81
81
|
|
|
82
82
|
```ts
|
|
83
|
+
import { Inject } from '@fluojs/core';
|
|
83
84
|
import { Transaction, DrizzleDatabase, type DrizzleDatabaseFacade } from '@fluojs/drizzle';
|
|
84
85
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
85
86
|
import { users, profiles } from './schema';
|
|
86
87
|
|
|
87
88
|
type AppDatabase = ReturnType<typeof drizzle>;
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
constructor(private readonly repo: UserRepository) {}
|
|
91
|
-
|
|
92
|
-
@Transaction()
|
|
93
|
-
async onboardUser(dto: any) {
|
|
94
|
-
const user = await this.repo.create(dto);
|
|
95
|
-
await this.repo.initProfile(user.id);
|
|
96
|
-
return user;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
90
|
+
@Inject(DrizzleDatabase)
|
|
100
91
|
export class UserRepository {
|
|
101
92
|
constructor(private readonly db: DrizzleDatabaseFacade<AppDatabase>) {}
|
|
102
93
|
|
|
103
94
|
async create(data: any) {
|
|
104
95
|
// The facade type exposes standard Drizzle methods.
|
|
105
96
|
// When called inside @Transaction(), they automatically participate in the ambient transaction.
|
|
106
|
-
|
|
97
|
+
const [user] = await this.db.insert(users).values(data).returning();
|
|
98
|
+
|
|
99
|
+
if (!user) {
|
|
100
|
+
throw new Error('User insert did not return a row.');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return user;
|
|
107
104
|
}
|
|
108
105
|
|
|
109
106
|
async initProfile(userId: string) {
|
|
110
107
|
return this.db.insert(profiles).values({ userId });
|
|
111
108
|
}
|
|
112
109
|
}
|
|
110
|
+
|
|
111
|
+
@Inject(UserRepository)
|
|
112
|
+
export class UserService {
|
|
113
|
+
constructor(private readonly repo: UserRepository) {}
|
|
114
|
+
|
|
115
|
+
@Transaction()
|
|
116
|
+
async onboardUser(dto: any) {
|
|
117
|
+
const user = await this.repo.create(dto);
|
|
118
|
+
await this.repo.initProfile(user.id);
|
|
119
|
+
return user;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
113
122
|
```
|
|
114
123
|
|
|
115
124
|
Calls to `@Transaction()` methods are reentrant. If a decorated method calls another decorated method, they share the same underlying Drizzle transaction.
|
|
116
125
|
|
|
117
|
-
By default, `@Transaction()` selects its target with a small host-object heuristic: it first checks `this.db`, then direct properties on the decorated instance, then a nested `.db` property on those values, and uses the first value that exposes a `transaction(...)` method. This keeps common `constructor(private readonly db: DrizzleDatabase<...>)` services concise, but services with more than one Drizzle wrapper should not rely on property order. Pass an explicit accessor such as `@Transaction((self) => self.ordersDb)` or `@Transaction((self) => self.analyticsDb, options)` whenever the decorated host owns multiple transaction-capable clients or wraps a repository that also exposes `.db`.
|
|
126
|
+
By default, `@Transaction()` selects its target with a small host-object heuristic: it first checks `this.db`, then direct properties on the decorated instance, then a nested `.db` property on those values, and uses the first value that exposes a `transaction(...)` method. If none of those candidates match, the decorated instance itself becomes the transaction target. This keeps common `constructor(private readonly db: DrizzleDatabase<...>)` services and self-contained facade hosts concise, but services with more than one Drizzle wrapper should not rely on property order. Pass an explicit accessor such as `@Transaction((self) => self.ordersDb)` or `@Transaction((self) => self.analyticsDb, options)` whenever the decorated host owns multiple transaction-capable clients or wraps a repository that also exposes `.db`.
|
|
118
127
|
|
|
119
128
|
### Manual Transactions and current()
|
|
120
129
|
|
|
@@ -182,11 +191,11 @@ export class CheckoutController {
|
|
|
182
191
|
}
|
|
183
192
|
```
|
|
184
193
|
|
|
185
|
-
There is no Drizzle `*TransactionInterceptor` export to import. Existing NestJS interceptor designs should move most transaction boundaries to services and reserve explicit `requestTransaction(...)` for rare controller-level compatibility cases where all request work, not just a service method, must share the same boundary.
|
|
194
|
+
There is no Drizzle `*TransactionInterceptor` export to import. Existing NestJS interceptor designs should move most transaction boundaries to services and reserve explicit `requestTransaction(...)` for rare controller-level compatibility cases where all request work, not just a service method, must share the same boundary. Decorating a controller method with `@Transaction()` remains a compatibility path when the controller owns an explicit `DrizzleDatabase` target, but `requestTransaction(...)` is the clearer request-wide API because it can receive the request `AbortSignal` directly.
|
|
186
195
|
|
|
187
196
|
### Shutdown and status contracts
|
|
188
197
|
|
|
189
|
-
During application shutdown, `DrizzleDatabase` aborts any still-active request transaction, waits for open request and manual transaction callbacks to settle or roll back, and only then runs the optional `dispose(database)` hook. This
|
|
198
|
+
During application shutdown, `DrizzleDatabase` aborts any still-active request transaction, waits for open request and manual transaction callbacks to settle or roll back, and only then runs the optional `dispose(database)` hook. This includes fail-open manual `transaction(...)` callbacks when `database.transaction(...)` is unavailable and `strictTransactions` is `false`, so direct-execution fallbacks still drain before pools or externally managed resources are closed.
|
|
190
199
|
Nested `requestTransaction(...)` calls opened inside an existing request boundary observe the ambient request abort signal while still reusing the active Drizzle transaction. Nested `requestTransaction(...)` calls opened inside an existing manual transaction boundary also join shutdown settlement tracking without opening a second Drizzle transaction, and their settlement handle remains tracked until the outer manual transaction settles so shutdown drains that outer boundary before `dispose(database)` runs. The platform status activity count is intentionally shorter lived: once the nested request callback settles, `details.activeRequestTransactions` is decremented even if the outer manual transaction continues running.
|
|
191
200
|
New `transaction(...)` and `requestTransaction(...)` calls are rejected once shutdown begins, so disposal cannot overtake a late transaction that starts after the shutdown boundary is crossed.
|
|
192
201
|
If the request signal aborts after the request callback has completed but before the underlying Drizzle transaction runner finishes committing or rolling back, `requestTransaction(...)` waits for that runner to settle first and then rejects with the abort reason. This keeps Drizzle cleanup serialized with request cancellation while making the late request abort visible to the caller instead of returning the completed callback result.
|
|
@@ -235,7 +244,7 @@ defineModule(ManualDrizzleModule, {
|
|
|
235
244
|
|
|
236
245
|
Use `DrizzleDatabase<TDatabase>` when a provider only needs wrapper methods such as `current()`, `transaction(...)`, `requestTransaction(...)`, or `createPlatformStatusSnapshot()`. Use `DrizzleDatabaseFacade<TDatabase>` for repository injections that call Drizzle query methods directly; the facade forwards those calls to the active transaction handle when one exists and to the root handle otherwise. `DrizzleDatabase.createFacade(...)` is retained as a low-level compatibility helper for module-provider wiring; application code should prefer `DrizzleModule.forRoot(...)` / `forRootAsync(...)`.
|
|
237
246
|
|
|
238
|
-
`Transaction` is a standard TC39 method decorator for service-layer transaction boundaries. It resolves a transaction-capable target from the decorated host by checking `this.db`, then direct properties, then nested `.db` properties, accepts an accessor for explicit client selection
|
|
247
|
+
`Transaction` is a standard TC39 method decorator for service-layer transaction boundaries. It resolves a transaction-capable target from the decorated host by checking `this.db`, then direct properties, then nested `.db` properties, then falling back to the decorated instance itself; it also accepts an accessor for explicit client selection and can forward Drizzle transaction options to the outer boundary.
|
|
239
248
|
|
|
240
249
|
### `DrizzleModule`
|
|
241
250
|
|
package/dist/database.d.ts
CHANGED
|
@@ -26,7 +26,8 @@ export declare class DrizzleDatabase<TDatabase extends DrizzleDatabaseLike<TTran
|
|
|
26
26
|
* @remarks
|
|
27
27
|
* This compatibility helper is used by `DrizzleModule` provider wiring. Application code should prefer
|
|
28
28
|
* `DrizzleModule.forRoot(...)` or `DrizzleModule.forRootAsync(...)`, then type injected repository handles as
|
|
29
|
-
* `DrizzleDatabaseFacade<TDatabase>` when direct Drizzle methods are needed.
|
|
29
|
+
* `DrizzleDatabaseFacade<TDatabase>` when direct Drizzle methods are needed. Wrapper and lifecycle methods remain
|
|
30
|
+
* bound to the lifecycle owner while unknown Drizzle query properties forward to the ambient `current()` handle.
|
|
30
31
|
*
|
|
31
32
|
* @param database Root Drizzle database handle registered in the module.
|
|
32
33
|
* @param dispose Optional shutdown hook used to close pools or driver resources.
|
|
@@ -79,6 +80,7 @@ export declare class DrizzleDatabase<TDatabase extends DrizzleDatabaseLike<TTran
|
|
|
79
80
|
*/
|
|
80
81
|
requestTransaction<T>(fn: () => Promise<T>, signal?: AbortSignal, options?: TTransactionOptions): Promise<T>;
|
|
81
82
|
private executeTransaction;
|
|
83
|
+
private executeManualRootTransaction;
|
|
82
84
|
private executeRequestTransaction;
|
|
83
85
|
private executeNestedRequestTransaction;
|
|
84
86
|
private executeRequestFallback;
|
|
@@ -89,6 +91,7 @@ export declare class DrizzleDatabase<TDatabase extends DrizzleDatabaseLike<TTran
|
|
|
89
91
|
private untrackActiveRequestTransaction;
|
|
90
92
|
private markRequestTransactionInactiveForStatus;
|
|
91
93
|
private trackActiveTransactionScope;
|
|
94
|
+
private trackAvailableTransactionScope;
|
|
92
95
|
private resolveTransactionRunner;
|
|
93
96
|
}
|
|
94
97
|
/**
|
package/dist/database.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"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"}
|
package/dist/database.js
CHANGED
|
@@ -5,19 +5,23 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
|
|
|
5
5
|
function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
|
|
6
6
|
function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
|
|
7
7
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
8
|
-
import { createAbortError, createRequestAbortContext, raceWithAbort, trackActiveRequestTransaction, untrackActiveRequestTransaction } from '@fluojs/runtime';
|
|
9
8
|
import { Inject } from '@fluojs/core';
|
|
10
|
-
import {
|
|
9
|
+
import { createAbortError, createRequestAbortContext, raceWithAbort, trackActiveRequestTransaction, untrackActiveRequestTransaction } from '@fluojs/runtime';
|
|
11
10
|
import { createDrizzlePlatformStatusSnapshot } from './status.js';
|
|
11
|
+
import { DRIZZLE_DATABASE, DRIZZLE_DISPOSE, DRIZZLE_OPTIONS } from './tokens.js';
|
|
12
12
|
const TRANSACTION_NOT_SUPPORTED_ERROR = 'Transaction not supported: Drizzle database does not implement transaction.';
|
|
13
13
|
const NESTED_TRANSACTION_OPTIONS_NOT_SUPPORTED_ERROR = 'Nested Drizzle transaction options are not supported because the active transaction context is reused.';
|
|
14
14
|
const TRANSACTION_UNAVAILABLE_ERROR = 'Drizzle transactions are not available during application shutdown.';
|
|
15
15
|
const REQUEST_TRANSACTION_UNAVAILABLE_ERROR = 'Drizzle request transactions are not available during shutdown.';
|
|
16
16
|
function createCurrentlessDrizzleFacade(target) {
|
|
17
17
|
return new Proxy(target, {
|
|
18
|
-
get(database, prop
|
|
18
|
+
get(database, prop) {
|
|
19
19
|
if (prop in database) {
|
|
20
|
-
|
|
20
|
+
const value = Reflect.get(database, prop, database);
|
|
21
|
+
if (typeof value === 'function') {
|
|
22
|
+
return value.bind(database);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
21
25
|
}
|
|
22
26
|
const currentDatabase = database.current();
|
|
23
27
|
const value = Reflect.get(currentDatabase, prop, currentDatabase);
|
|
@@ -92,7 +96,8 @@ class DrizzleDatabase {
|
|
|
92
96
|
* @remarks
|
|
93
97
|
* This compatibility helper is used by `DrizzleModule` provider wiring. Application code should prefer
|
|
94
98
|
* `DrizzleModule.forRoot(...)` or `DrizzleModule.forRootAsync(...)`, then type injected repository handles as
|
|
95
|
-
* `DrizzleDatabaseFacade<TDatabase>` when direct Drizzle methods are needed.
|
|
99
|
+
* `DrizzleDatabaseFacade<TDatabase>` when direct Drizzle methods are needed. Wrapper and lifecycle methods remain
|
|
100
|
+
* bound to the lifecycle owner while unknown Drizzle query properties forward to the ambient `current()` handle.
|
|
96
101
|
*
|
|
97
102
|
* @param database Root Drizzle database handle registered in the module.
|
|
98
103
|
* @param dispose Optional shutdown hook used to close pools or driver resources.
|
|
@@ -194,31 +199,32 @@ class DrizzleDatabase {
|
|
|
194
199
|
return fn();
|
|
195
200
|
}
|
|
196
201
|
if (!requestScoped) {
|
|
197
|
-
this.
|
|
202
|
+
return this.executeManualRootTransaction(fn, options);
|
|
198
203
|
}
|
|
199
204
|
const transactionRunner = this.resolveTransactionRunner();
|
|
200
205
|
if (!transactionRunner) {
|
|
201
|
-
|
|
202
|
-
return this.executeRequestFallback(fn, signal);
|
|
203
|
-
}
|
|
204
|
-
return fn();
|
|
206
|
+
return this.executeRequestFallback(fn, signal);
|
|
205
207
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
for (const handle of deferredRequestTransactionSettlements) {
|
|
216
|
-
this.untrackActiveRequestTransaction(handle);
|
|
217
|
-
}
|
|
218
|
-
activeTransactionScope.settle();
|
|
208
|
+
return this.executeRequestTransaction(transactionRunner, fn, options, signal);
|
|
209
|
+
}
|
|
210
|
+
async executeManualRootTransaction(fn, options) {
|
|
211
|
+
const deferredRequestTransactionSettlements = new Set();
|
|
212
|
+
const activeTransactionScope = this.trackAvailableTransactionScope();
|
|
213
|
+
try {
|
|
214
|
+
const transactionRunner = this.resolveTransactionRunner();
|
|
215
|
+
if (!transactionRunner) {
|
|
216
|
+
return await fn();
|
|
219
217
|
}
|
|
218
|
+
return await transactionRunner(transactionDatabase => this.transactions.run({
|
|
219
|
+
database: transactionDatabase,
|
|
220
|
+
deferredRequestTransactionSettlements
|
|
221
|
+
}, fn), options);
|
|
222
|
+
} finally {
|
|
223
|
+
for (const handle of deferredRequestTransactionSettlements) {
|
|
224
|
+
this.untrackActiveRequestTransaction(handle);
|
|
225
|
+
}
|
|
226
|
+
activeTransactionScope.settle();
|
|
220
227
|
}
|
|
221
|
-
return this.executeRequestTransaction(transactionRunner, fn, options, signal);
|
|
222
228
|
}
|
|
223
229
|
async executeRequestTransaction(transactionRunner, fn, options, signal) {
|
|
224
230
|
this.assertRequestTransactionsAvailable();
|
|
@@ -328,6 +334,10 @@ class DrizzleDatabase {
|
|
|
328
334
|
}
|
|
329
335
|
};
|
|
330
336
|
}
|
|
337
|
+
trackAvailableTransactionScope() {
|
|
338
|
+
this.assertTransactionsAvailable();
|
|
339
|
+
return this.trackActiveTransactionScope();
|
|
340
|
+
}
|
|
331
341
|
resolveTransactionRunner() {
|
|
332
342
|
if (typeof this.database.transaction !== 'function') {
|
|
333
343
|
if (this.databaseOptions.strictTransactions) {
|
package/dist/transaction.d.ts
CHANGED
|
@@ -7,9 +7,10 @@ type TransactionMethod<THost, TArgs extends unknown[], TResult> = (this: THost,
|
|
|
7
7
|
* Standard TC39 method decorator that runs a service method inside a Drizzle transaction boundary.
|
|
8
8
|
*
|
|
9
9
|
* @remarks
|
|
10
|
-
* `@Transaction()`
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* `@Transaction()` selects the first transaction-capable target by checking `this.db`, then direct host properties,
|
|
11
|
+
* then nested `.db` properties on those values. If none of those candidates exposes `transaction(...)`, the decorated
|
|
12
|
+
* instance itself is used as the transaction target. Pass an accessor such as `@Transaction((self) => self.analyticsDb)`
|
|
13
|
+
* to select another Drizzle wrapper explicitly. Non-function factory input is forwarded as Drizzle transaction options.
|
|
13
14
|
*
|
|
14
15
|
* @param accessorOrOptions Optional target accessor, or Drizzle transaction options.
|
|
15
16
|
* @param options Optional Drizzle transaction options when an accessor is supplied.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA,KAAK,yBAAyB,CAAC,mBAAmB,GAAG,OAAO,IAAI;IAC9D,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjF,CAAC;AAEF,KAAK,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,IAAI,CACrD,IAAI,EAAE,KAAK,KACR,yBAAyB,CAAC,mBAAmB,CAAC,CAAC;AAEpD,KAAK,iBAAiB,CAAC,KAAK,EAAE,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,IAAI,CAChE,IAAI,EAAE,KAAK,EACX,GAAG,IAAI,EAAE,KAAK,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAwCtB
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAAA,KAAK,yBAAyB,CAAC,mBAAmB,GAAG,OAAO,IAAI;IAC9D,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjF,CAAC;AAEF,KAAK,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,IAAI,CACrD,IAAI,EAAE,KAAK,KACR,yBAAyB,CAAC,mBAAmB,CAAC,CAAC;AAEpD,KAAK,iBAAiB,CAAC,KAAK,EAAE,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,IAAI,CAChE,IAAI,EAAE,KAAK,EACX,GAAG,IAAI,EAAE,KAAK,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAwCtB;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,EAC9D,iBAAiB,CAAC,EAAE,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,CAAC,GAAG,mBAAmB,EACzF,OAAO,CAAC,EAAE,mBAAmB,IAOrB,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EACtC,OAAO,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAC/C,SAAS,2BAA2B,CAAC,KAAK,EAAE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,KACpF,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAc5C"}
|
package/dist/transaction.js
CHANGED
|
@@ -29,9 +29,10 @@ function resolveDefaultTransactionTarget(self) {
|
|
|
29
29
|
* Standard TC39 method decorator that runs a service method inside a Drizzle transaction boundary.
|
|
30
30
|
*
|
|
31
31
|
* @remarks
|
|
32
|
-
* `@Transaction()`
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* `@Transaction()` selects the first transaction-capable target by checking `this.db`, then direct host properties,
|
|
33
|
+
* then nested `.db` properties on those values. If none of those candidates exposes `transaction(...)`, the decorated
|
|
34
|
+
* instance itself is used as the transaction target. Pass an accessor such as `@Transaction((self) => self.analyticsDb)`
|
|
35
|
+
* to select another Drizzle wrapper explicitly. Non-function factory input is forwarded as Drizzle transaction options.
|
|
35
36
|
*
|
|
36
37
|
* @param accessorOrOptions Optional target accessor, or Drizzle transaction options.
|
|
37
38
|
* @param options Optional Drizzle transaction options when an accessor is supplied.
|
|
@@ -40,7 +41,7 @@ function resolveDefaultTransactionTarget(self) {
|
|
|
40
41
|
export function Transaction(accessorOrOptions, options) {
|
|
41
42
|
const accessor = typeof accessorOrOptions === 'function' ? accessorOrOptions : undefined;
|
|
42
43
|
const transactionOptions = accessor ? options : accessorOrOptions;
|
|
43
|
-
return
|
|
44
|
+
return (value, context) => {
|
|
44
45
|
if (context.kind !== 'method') {
|
|
45
46
|
throw new Error('@Transaction() can only decorate methods.');
|
|
46
47
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MaybePromise } from '@fluojs/core';
|
|
2
|
+
import type { PersistencePlatformStatusSnapshot } from '@fluojs/runtime';
|
|
2
3
|
type DrizzleTransactionCallback<TTransactionDatabase, TResult> = (database: TTransactionDatabase) => Promise<TResult>;
|
|
3
4
|
type DrizzleTransactionRunner<TTransactionDatabase, TTransactionOptions> = <T>(callback: DrizzleTransactionCallback<TTransactionDatabase, T>, options?: TTransactionOptions) => Promise<T>;
|
|
4
5
|
/**
|
|
@@ -40,6 +41,8 @@ export interface DrizzleModuleOptions<TDatabase extends DrizzleDatabaseLike<TTra
|
|
|
40
41
|
* @typeParam TTransactionOptions Options forwarded to `database.transaction(...)`.
|
|
41
42
|
*/
|
|
42
43
|
export interface DrizzleHandleProvider<TDatabase extends DrizzleDatabaseLike<TTransactionDatabase, TTransactionOptions>, TTransactionDatabase = TDatabase, TTransactionOptions = unknown> {
|
|
44
|
+
/** Produces the platform diagnostics snapshot for health and readiness integrations. */
|
|
45
|
+
createPlatformStatusSnapshot(): PersistencePlatformStatusSnapshot;
|
|
43
46
|
/** Returns the ambient transaction database when present, or the root Drizzle handle otherwise. */
|
|
44
47
|
current(): TDatabase | TTransactionDatabase;
|
|
45
48
|
/**
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,iBAAiB,CAAC;AAEzE,KAAK,0BAA0B,CAAC,oBAAoB,EAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,oBAAoB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtH,KAAK,wBAAwB,CAAC,oBAAoB,EAAE,mBAAmB,IAAI,CAAC,CAAC,EAC3E,QAAQ,EAAE,0BAA0B,CAAC,oBAAoB,EAAE,CAAC,CAAC,EAC7D,OAAO,CAAC,EAAE,mBAAmB,KAC1B,OAAO,CAAC,CAAC,CAAC,CAAC;AAEhB;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB,CAAC,oBAAoB,GAAG,OAAO,EAAE,mBAAmB,GAAG,OAAO;IAChG,WAAW,CAAC,EAAE,wBAAwB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,CAAC;CACnF;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB,CAAC,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAAE,oBAAoB,GAAG,SAAS,EAAE,mBAAmB,GAAG,OAAO;IACrL,8EAA8E;IAC9E,QAAQ,EAAE,SAAS,CAAC;IACpB,kGAAkG;IAClG,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,KAAK,YAAY,CAAC,IAAI,CAAC,CAAC;IACtD,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB,CAAC,SAAS,SAAS,mBAAmB,CAAC,oBAAoB,EAAE,mBAAmB,CAAC,EAAE,oBAAoB,GAAG,SAAS,EAAE,mBAAmB,GAAG,OAAO;IACtL,wFAAwF;IACxF,4BAA4B,IAAI,iCAAiC,CAAC;IAClE,mGAAmG;IACnG,OAAO,IAAI,SAAS,GAAG,oBAAoB,CAAC;IAC5C;;;;;;;OAOG;IACH,kBAAkB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7G;;;;;;OAMG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjF"}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"transaction",
|
|
10
10
|
"als"
|
|
11
11
|
],
|
|
12
|
-
"version": "1.1.
|
|
12
|
+
"version": "1.1.1",
|
|
13
13
|
"private": false,
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@fluojs/core": "^1.0
|
|
40
|
-
"@fluojs/di": "^
|
|
41
|
-
"@fluojs/runtime": "^
|
|
39
|
+
"@fluojs/core": "^1.1.0",
|
|
40
|
+
"@fluojs/di": "^2.0.0",
|
|
41
|
+
"@fluojs/runtime": "^2.0.1"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"drizzle-orm": ">=0.30.0"
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"vitest": "^3.2.4",
|
|
53
|
-
"@fluojs/http": "^
|
|
53
|
+
"@fluojs/http": "^2.0.1"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|
|
56
56
|
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|