@fluojs/prisma 1.1.0 → 2.0.0
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 +75 -22
- package/README.md +75 -22
- package/dist/integration.d.ts +93 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +145 -0
- package/dist/module.d.ts +12 -6
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +19 -11
- package/dist/prisma-service-brand.d.ts +13 -0
- package/dist/prisma-service-brand.d.ts.map +1 -0
- package/dist/prisma-service-brand.js +23 -0
- package/dist/service.d.ts +34 -2
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +85 -22
- package/dist/status.d.ts +45 -5
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +18 -4
- package/dist/transaction.d.ts +27 -0
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +64 -15
- package/package.json +8 -8
package/README.ko.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
|
|
4
4
|
|
|
5
|
-
fluo 애플리케이션을 위한 Prisma lifecycle 및 ALS 기반 transaction context입니다. `PrismaClient`를 모듈 시스템에 연결하고 자동 연결 관리와 요청 범위 트랜잭션을 제공합니다.
|
|
5
|
+
fluo 애플리케이션을 위한 Node.js `>=24.0.0 <27` Prisma lifecycle 및 ALS 기반 transaction context입니다. `PrismaClient`를 모듈 시스템에 연결하고 자동 연결 관리와 요청 범위 트랜잭션을 제공합니다.
|
|
6
6
|
|
|
7
7
|
## 목차
|
|
8
8
|
|
|
@@ -11,6 +11,7 @@ fluo 애플리케이션을 위한 Prisma lifecycle 및 ALS 기반 transaction co
|
|
|
11
11
|
- [빠른 시작](#빠른-시작)
|
|
12
12
|
- [공통 패턴](#공통-패턴)
|
|
13
13
|
- [서비스 트랜잭션 경계 (@Transaction)](#서비스-트랜잭션-경계-transaction)
|
|
14
|
+
- [요청 트랜잭션 인터셉터 호환성](#요청-트랜잭션-인터셉터-호환성)
|
|
14
15
|
- [여러 클라이언트를 위한 이름 있는 등록](#여러-클라이언트를-위한-이름-있는-등록)
|
|
15
16
|
- [수동 트랜잭션과 current()](#수동-트랜잭션과-current)
|
|
16
17
|
- [종료와 status 계약](#종료와-status-계약)
|
|
@@ -30,7 +31,7 @@ pnpm add @prisma/client
|
|
|
30
31
|
|
|
31
32
|
## 사용 시점
|
|
32
33
|
|
|
33
|
-
- Prisma를 ORM으로 사용하면서 fluo의 의존성 주입 및 라이프사이클 훅과 통합하고 싶을 때.
|
|
34
|
+
- Node.js `>=24.0.0 <27`에서 Prisma를 ORM으로 사용하면서 fluo의 의존성 주입 및 라이프사이클 훅과 통합하고 싶을 때.
|
|
34
35
|
- 여러 서비스와 리포지토리 사이에서 `tx` 객체를 일일이 전달하지 않고도 트랜잭션 컨텍스트를 안정적으로 공유하고 싶을 때.
|
|
35
36
|
- 애플리케이션 시작 시 자동 `$connect`, 종료 시 자동 `$disconnect`가 필요할 때.
|
|
36
37
|
|
|
@@ -65,6 +66,7 @@ import { PrismaService, Transaction, type PrismaServiceFacade } from '@fluojs/pr
|
|
|
65
66
|
import { PrismaClient } from '@prisma/client';
|
|
66
67
|
import { UserRepository } from './user.repository';
|
|
67
68
|
|
|
69
|
+
@Inject(UserRepository)
|
|
68
70
|
export class UserService {
|
|
69
71
|
constructor(private readonly repo: UserRepository) {}
|
|
70
72
|
|
|
@@ -75,28 +77,67 @@ export class UserService {
|
|
|
75
77
|
return user;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
80
|
+
```
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
export class UserRepository {
|
|
81
|
-
constructor(private readonly prisma: PrismaServiceFacade<PrismaClient>) {}
|
|
82
|
+
`@Transaction()` 메서드 호출은 재진입(reentrant)이 가능합니다. 데코레이터가 적용된 메서드가 다른 데코레이터 적용 메서드를 호출하더라도 하나의 동일한 Prisma 트랜잭션 안에서 실행됩니다.
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
### 요청 트랜잭션 인터셉터 호환성
|
|
85
|
+
|
|
86
|
+
`PrismaTransactionInterceptor`는 기존 `@UseInterceptors(...)` request-wide boundary를 위한 deprecated 1.x 호환성 export로 복원되었습니다. 이름 없는 `PrismaModule.forRoot(...)`와 `forRootAsync(...)` 등록이 이 interceptor를 provider 및 export로 제공하며, `PrismaService.requestTransaction(...)`에 위임하고 request `AbortSignal`을 전달합니다.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { Inject } from '@fluojs/core';
|
|
90
|
+
import { Controller, Post, UseInterceptors } from '@fluojs/http';
|
|
91
|
+
import { PrismaTransactionInterceptor } from '@fluojs/prisma';
|
|
92
|
+
import { OrdersService } from './orders.service';
|
|
93
|
+
|
|
94
|
+
@Controller('/orders')
|
|
95
|
+
@Inject(OrdersService)
|
|
96
|
+
export class OrdersController {
|
|
97
|
+
constructor(private readonly orders: OrdersService) {}
|
|
98
|
+
|
|
99
|
+
@Post('/')
|
|
100
|
+
@UseInterceptors(PrismaTransactionInterceptor)
|
|
101
|
+
createOrder() {
|
|
102
|
+
return this.orders.create();
|
|
87
103
|
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
88
106
|
|
|
89
|
-
|
|
90
|
-
|
|
107
|
+
새 비즈니스 작업에는 서비스 계층 `@Transaction()`을 우선 사용하세요. 전체 요청에 하나의 트랜잭션이 정말 필요하거나 이름 있는/여러 Prisma 등록에서 특정 서비스를 선택해야 한다면 명시적 `requestTransaction(...)`을 사용하세요. 호환성 interceptor는 이름 없는 기본 등록만 대상으로 합니다.
|
|
108
|
+
|
|
109
|
+
요청 전체 원자성이 정말 필요한 경우에는 application code에서 boundary와 cancellation input을 명시적으로 드러내세요.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { Inject } from '@fluojs/core';
|
|
113
|
+
import { Controller, Post, type RequestContext } from '@fluojs/http';
|
|
114
|
+
import { PrismaService } from '@fluojs/prisma';
|
|
115
|
+
import { PrismaClient } from '@prisma/client';
|
|
116
|
+
|
|
117
|
+
@Inject(PrismaService, OrdersService)
|
|
118
|
+
@Controller('/orders')
|
|
119
|
+
export class OrdersController {
|
|
120
|
+
constructor(
|
|
121
|
+
private readonly prisma: PrismaService<PrismaClient>,
|
|
122
|
+
private readonly orders: OrdersService,
|
|
123
|
+
) {}
|
|
124
|
+
|
|
125
|
+
@Post('/checkout')
|
|
126
|
+
checkout(input: CheckoutInput, context: RequestContext) {
|
|
127
|
+
const { request } = context;
|
|
128
|
+
return this.prisma.requestTransaction(
|
|
129
|
+
() => this.orders.checkout(input),
|
|
130
|
+
request.signal,
|
|
131
|
+
);
|
|
91
132
|
}
|
|
92
133
|
}
|
|
93
134
|
```
|
|
94
135
|
|
|
95
|
-
`@Transaction()
|
|
136
|
+
이는 서비스 `@Transaction()`을 대체하는 방식이 아니라 좁은 호환성 패턴입니다. 요청 전체 트랜잭션은 HTTP 작업 전체에서 데이터베이스 lock을 유지할 수 있으므로 boundary를 짧고 명시적으로 유지하세요.
|
|
96
137
|
|
|
97
138
|
### 여러 클라이언트를 위한 이름 있는 등록
|
|
98
139
|
|
|
99
|
-
하나의 애플리케이션 컨테이너 안에서 여러 Prisma Client가 필요하다면 각 등록에 명시적인 `name`을 부여하고 `getPrismaServiceToken(name)`으로 대응되는 토큰을 주입하세요. 이름 있는 클라이언트를 사용할 때는 `@Transaction()`에 해당 서비스로 접근할 수 있는 accessor를 전달하세요.
|
|
140
|
+
하나의 애플리케이션 컨테이너 안에서 여러 Prisma Client가 필요하다면 각 등록에 명시적인 `name`을 부여하고 `getPrismaServiceToken(name)`으로 대응되는 토큰을 주입하세요. 이름 있는 클라이언트를 사용할 때는 `@Transaction()`에 해당 서비스로 접근할 수 있는 accessor를 전달하세요. 기본 `@Transaction()` 해석은 Prisma service/facade 형태의 속성만 선택합니다. 다른 persistence 통합의 transaction-like 객체는 무시되므로 모호한 host에서는 명시적 accessor를 사용해야 합니다.
|
|
100
141
|
|
|
101
142
|
```typescript
|
|
102
143
|
import { Inject } from '@fluojs/core';
|
|
@@ -158,16 +199,19 @@ await this.prisma.transaction(async () => {
|
|
|
158
199
|
|
|
159
200
|
### 종료와 status 계약
|
|
160
201
|
|
|
161
|
-
`PrismaService.requestTransaction(...)`은 정상 serving 전과 중에는 사용할 수 있지만, 애플리케이션 shutdown이 시작된 뒤에는 새 요청 범위 트랜잭션을 거부합니다. 종료 중에는 열린 요청 트랜잭션을 abort하고, 가장 바깥 transaction boundary가 settle될 때까지 추적한 다음 `$disconnect()` 실행 전에 drain합니다. 기존 수동 `transaction(...)` boundary 안에서 열린 중첩 `requestTransaction(...)` 호출도 동일합니다. 해당 호출은 ambient Prisma transaction client를 재사용하고, 바깥 boundary가 끝날 때까지 `details.activeRequestTransactions`에 표시되며, 두 번째 Prisma transaction을 열지 않습니다.
|
|
202
|
+
`PrismaService.requestTransaction(...)`은 정상 serving 전과 중에는 사용할 수 있지만, 애플리케이션 shutdown이 시작된 뒤에는 새 요청 범위 트랜잭션을 거부합니다. 새 outer 수동 `transaction(...)` 및 서비스 `@Transaction()` boundary도 shutdown 시작 후에는 거부됩니다. 이미 열린 boundary는 `$disconnect()` 전에 drain되므로 shutdown이 활성 Prisma transaction과 경합하지 않습니다. Shutdown은 진행 중인 `$connect()`가 settle될 때까지 기다린 뒤 `$disconnect()`를 실행하며, shutdown 시작 뒤 늦게 완료된 connect는 ready 상태를 복원하거나 새 transaction work를 허용할 수 없습니다. 종료 중에는 열린 요청 트랜잭션을 abort하고, 가장 바깥 transaction boundary가 settle될 때까지 추적한 다음 `$disconnect()` 실행 전에 drain합니다. 기존 수동 `transaction(...)` boundary 안에서 열린 중첩 `requestTransaction(...)` 호출도 동일합니다. 해당 호출은 ambient Prisma transaction client를 재사용하고, 바깥 boundary가 끝날 때까지 `details.activeRequestTransactions`에 표시되며, 두 번째 Prisma transaction을 열지 않습니다.
|
|
162
203
|
|
|
163
204
|
`createPrismaPlatformStatusSnapshot(...)`와 `PrismaService.createPlatformStatusSnapshot()`은 같은 라이프사이클 계약을 진단 surface에 노출합니다.
|
|
164
205
|
|
|
165
|
-
- `readiness.status`는 `onModuleInit()`이 클라이언트를 연결하기 전, Prisma가 종료 중이거나 stopped 상태일 때,
|
|
166
|
-
- `health.status`는 종료 중
|
|
167
|
-
- `details.activeRequestTransactions`, `details.lifecycleState`, `details.strictTransactions`, `details.supportsTransaction`, `details.transactionAbortSignalSupport`는 현재
|
|
168
|
-
- `details.
|
|
206
|
+
- `readiness.status`는 `onModuleInit()`이 클라이언트를 연결하기 전, Prisma가 종료 중이거나 stopped 상태일 때, `strictTransactions`가 켜져 있는데 `$transaction(...)`을 지원하지 않을 때, 그리고 클라이언트가 interactive transaction을 지원하지만 호스트 런타임이 `AsyncLocalStorage`를 제공하지 않을 때 `not-ready`입니다. ALS 미지원 상태의 readiness reason은 `Prisma transaction context requires AsyncLocalStorage support from the host runtime.`이며 `details.transactionContext`가 `unavailable`로 보고됩니다. 이 상태는 Prisma 클라이언트 자체는 연결되어 있고 기능적으로 정상일 수 있으므로 일반 database readiness 실패와 구분됩니다.
|
|
207
|
+
- `health.status`는 종료 중 열린 요청, 수동 또는 서비스 트랜잭션 경계를 drain하는 동안 `degraded`, disconnect 이후 `unhealthy`입니다.
|
|
208
|
+
- `details.activeRequestTransactions`, `details.activeTransactionBoundaries`, `details.lifecycleState`, `details.strictTransactions`, `details.supportsTransaction`, `details.transactionAbortSignalSupport`는 현재 트랜잭션과 트랜잭션 capability 상태를 설명합니다.
|
|
209
|
+
- `details.activeTransactionBoundaries`는 shutdown이 `$disconnect()` 전에 drain하는 현재 열린 바깥 `transaction(...)` 및 service `@Transaction()` boundary 수를 나타냅니다. 요청 전용 `requestTransaction(...)` activity는 포함하지 않으며, 해당 activity는 `details.activeRequestTransactions`에서 별도로 확인할 수 있습니다.
|
|
210
|
+
- `details.transactionContext: 'als'`는 요청 및 서비스 트랜잭션 경계가 사용하는 async-local transaction context를 식별합니다. `details.transactionContext: 'unavailable'`은 호스트 런타임이 사용 가능한 `AsyncLocalStorage`를 노출하지 않았음을 나타내며, 이 경우 `transaction()`과 `requestTransaction()`은 Prisma 트랜잭션을 열기 전에 예외를 던집니다.
|
|
169
211
|
- `ownership.externallyManaged: false`와 `ownership.ownsResources: true`는 패키지가 fluo 애플리케이션 라이프사이클 안에서 등록된 클라이언트의 `$connect()` / `$disconnect()` lifecycle hook을 소유한다는 의미입니다.
|
|
170
212
|
|
|
213
|
+
`details.transactionContext`가 `unavailable`이면 패키지는 동기 stack 기반 컨텍스트로 fallback하지 않습니다. async boundary 사이에서 `current()`를 잃기 때문입니다. fallback boundary는 애플리케이션이 소유합니다. 트랜잭션 컨텍스트 없이도 데이터베이스 접근이 필요한 호출자는 (예: `PRISMA_CLIENT` 토큰을 통해) 원시 `PrismaClient`를 직접 호출하고 자체 일관성 semantics를 관리하거나, `AsyncLocalStorage`를 제공하는 호스트 런타임(Node.js `>=24.0.0 <27`가 문서화된 경로)에서 실행해야 합니다. `unavailable` readiness 상태는 운영적으로 실행 가능한 신호로 취급하세요. health check에 노출하고, 호스트가 ALS를 제공하거나 애플리케이션이 비트랜잭션 접근 경로로 전환할 때까지 트랜잭션 의존 handler로 트래픽을 라우팅하지 마세요.
|
|
214
|
+
|
|
171
215
|
### 비동기 설정과 격리
|
|
172
216
|
|
|
173
217
|
주입된 설정이나 다른 비동기 소스에서 Prisma 클라이언트를 만들어야 할 때는 `PrismaModule.forRootAsync(...)`를 사용하세요. 비동기 factory는 애플리케이션 컨테이너마다 한 번 resolve되며, 테스트나 여러 앱을 띄우는 프로세스에서 같은 모듈 정의를 재사용하더라도 별도 bootstrap 사이에서 공유되지 않습니다.
|
|
@@ -187,7 +231,7 @@ PrismaModule.forRootAsync({
|
|
|
187
231
|
|
|
188
232
|
하나의 컴파일된 애플리케이션 안에서는 하위 provider가 동일하게 resolve된 `PrismaService`, ALS 트랜잭션 컨텍스트, 라이프사이클 관리 대상 클라이언트를 공유합니다. 서로 다른 애플리케이션 컨테이너는 독립된 factory 결과를 받으므로 `$connect` / `$disconnect` 소유권과 요청 트랜잭션 상태가 격리됩니다.
|
|
189
233
|
|
|
190
|
-
트랜잭션 경계에는 호스트가 제공하는 `AsyncLocalStorage` 지원이 필요합니다. `@fluojs/prisma`는 런타임이 노출하는 `globalThis.AsyncLocalStorage` 또는 Node.js의 `process.getBuiltinModule('node:async_hooks')` 호스트 경계를 통해
|
|
234
|
+
트랜잭션 경계에는 호스트가 제공하는 `AsyncLocalStorage` 지원이 필요합니다. 패키지 manifest는 `engines.node >=24.0.0 <27`을 선언하며, root wrapper는 문서화된 Node.js `>=24.0.0 <27` Prisma 통합 경로입니다. `@fluojs/prisma`는 런타임이 노출하는 `globalThis.AsyncLocalStorage` 또는 Node.js의 `process.getBuiltinModule('node:async_hooks')` 호스트 경계를 통해 ALS를 resolve합니다. 두 경로 모두 사용할 수 없거나 host builtin lookup이 실패하면 동기 stack fallback으로 async boundary 사이의 `current()`를 잃는 대신, Prisma 트랜잭션을 열기 전에 `transaction()`과 `requestTransaction()`이 예외를 던집니다. 이 상태는 `createPlatformStatusSnapshot().details.transactionContext`에 `unavailable`로 보고됩니다.
|
|
191
235
|
|
|
192
236
|
### 수동 모듈 조합
|
|
193
237
|
|
|
@@ -212,7 +256,7 @@ defineModule(ManualPrismaModule, {
|
|
|
212
256
|
### `PrismaModule`
|
|
213
257
|
|
|
214
258
|
- `PrismaModule.forRoot(options)` / `PrismaModule.forRootAsync(options)`
|
|
215
|
-
- `forRoot(...)`와 `forRootAsync(...)`도 이름 있는/scoped 등록을 위해 `name`을 받을 수 있습니다.
|
|
259
|
+
- `forRoot(...)`와 `forRootAsync(...)`도 이름 있는/scoped 등록을 위해 `name`을 받을 수 있으며, 이름 없는 등록을 전역 provider로 export해야 할 때 `global?: boolean`을 받을 수 있습니다.
|
|
216
260
|
- `forRootAsync(...)`는 client와 transaction 설정을 factory에서 반환하는 DI-aware Prisma 옵션을 받습니다. 모듈 identity와 visibility가 factory 실행 전에 결정되도록 `name` 또는 `global`은 최상위 async 등록 옵션에 전달하세요.
|
|
217
261
|
- `forRootAsync(...)`는 애플리케이션 컨테이너마다 옵션을 한 번 resolve하여, 별도 bootstrap 사이에서 클라이언트 라이프사이클과 요청 트랜잭션 격리를 보존합니다.
|
|
218
262
|
- `strictTransactions: true` 설정 시 트랜잭션 미지원 환경에서 즉시 예외를 발생시킵니다.
|
|
@@ -225,7 +269,7 @@ defineModule(ManualPrismaModule, {
|
|
|
225
269
|
- `current(): TClient | PrismaTransactionClient<TClient>`
|
|
226
270
|
- 현재 컨텍스트에 맞는 트랜잭션 클라이언트 또는 루트 클라이언트를 반환합니다.
|
|
227
271
|
- `transaction(fn, options?): Promise<T>`
|
|
228
|
-
- 대화형 트랜잭션 내에서 함수를 실행합니다. 이미 트랜잭션 컨텍스트가 활성화되어 있으면 callback은 그 컨텍스트를 재사용하며, 새 Prisma 트랜잭션 경계가 열리지 않기 때문에 중첩 트랜잭션 옵션은 거부됩니다.
|
|
272
|
+
- 대화형 트랜잭션 내에서 함수를 실행합니다. 이미 트랜잭션 컨텍스트가 활성화되어 있으면 callback은 그 컨텍스트를 재사용하며, 새 Prisma 트랜잭션 경계가 열리지 않기 때문에 중첩 트랜잭션 옵션은 거부됩니다. shutdown이 시작된 뒤에는 새 outer transaction boundary를 거부합니다.
|
|
229
273
|
- `requestTransaction(fn, signal?, options?): Promise<T>`
|
|
230
274
|
- HTTP 요청 라이프사이클에 특화된 트랜잭션 경계를 실행합니다. Abort를 인식하고, shutdown 중에는 disconnect 전에 열린 요청 트랜잭션을 drain하며, Prisma client가 `signal` 옵션을 거부하면 해당 옵션 없이 재시도합니다. `transaction()`과 마찬가지로 중첩 호출은 활성 트랜잭션 컨텍스트를 재사용하고, 트랜잭션 설정을 조용히 무시하지 않도록 중첩 옵션을 거부합니다.
|
|
231
275
|
|
|
@@ -233,7 +277,13 @@ Provider가 `current()`, `transaction(...)`, `requestTransaction(...)`, `createP
|
|
|
233
277
|
|
|
234
278
|
### `Transaction`
|
|
235
279
|
|
|
236
|
-
- 서비스 계층 트랜잭션 경계를 위한 표준 TC39 method decorator입니다. 기본적으로
|
|
280
|
+
- 서비스 계층 트랜잭션 경계를 위한 표준 TC39 method decorator입니다. 기본적으로 Prisma service/facade 형태의 속성을 resolve하고, 이름 있는 client나 모호한 host에는 accessor를 받을 수 있으며, 외부 경계에는 Prisma transaction option을 전달할 수 있습니다.
|
|
281
|
+
|
|
282
|
+
### `PrismaTransactionInterceptor` (deprecated 호환성)
|
|
283
|
+
|
|
284
|
+
- 기존 1.x import를 위한 request-wide HTTP 호환성 interceptor입니다.
|
|
285
|
+
- `PrismaService.requestTransaction(...)`에 위임하고 request cancellation을 전달합니다.
|
|
286
|
+
- 새 코드에서는 서비스 `@Transaction()` 또는 명시적 request boundary를 우선 사용하세요.
|
|
237
287
|
|
|
238
288
|
### `PRISMA_CLIENT` (Token)
|
|
239
289
|
|
|
@@ -259,6 +309,7 @@ Provider가 `current()`, `transaction(...)`, `requestTransaction(...)`, `createP
|
|
|
259
309
|
|
|
260
310
|
### 관련 export 타입
|
|
261
311
|
|
|
312
|
+
- `PrismaAsyncModuleOptions<TClient, TTransactionClient, TTransactionOptions>`
|
|
262
313
|
- `PrismaModuleOptions`
|
|
263
314
|
- `PrismaClientLike`
|
|
264
315
|
- `PrismaHandleProvider`
|
|
@@ -266,6 +317,8 @@ Provider가 `current()`, `transaction(...)`, `requestTransaction(...)`, `createP
|
|
|
266
317
|
- `PrismaTransactionClient<TClient>`
|
|
267
318
|
- `InferPrismaTransactionClient<TClient>`
|
|
268
319
|
- `InferPrismaTransactionOptions<TClient>`
|
|
320
|
+
- `PrismaPlatformStatusSnapshotInput`
|
|
321
|
+
- `createPrismaPlatformStatusSnapshot(...)`의 입력 계약입니다. 바깥 service 또는 manual transaction boundary가 열려 있지 않다면 `activeTransactionBoundaries`를 생략할 수 있으며 snapshot은 `0`으로 보고합니다.
|
|
269
322
|
|
|
270
323
|
## 관련 패키지
|
|
271
324
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
|
|
4
4
|
|
|
5
|
-
Prisma lifecycle and ALS-backed transaction context for fluo applications. Connects a `PrismaClient` to the module system with automatic connection management and request-scoped transactions.
|
|
5
|
+
Node.js `>=24.0.0 <27` Prisma lifecycle and ALS-backed transaction context for fluo applications. Connects a `PrismaClient` to the module system with automatic connection management and request-scoped transactions.
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
|
@@ -11,6 +11,7 @@ Prisma lifecycle and ALS-backed transaction context for fluo applications. Conne
|
|
|
11
11
|
- [Quick Start](#quick-start)
|
|
12
12
|
- [Common Patterns](#common-patterns)
|
|
13
13
|
- [Service Transaction Boundary (@Transaction)](#service-transaction-boundary-transaction)
|
|
14
|
+
- [Request Transaction Interceptor Compatibility](#request-transaction-interceptor-compatibility)
|
|
14
15
|
- [Named Registrations for Multiple Clients](#named-registrations-for-multiple-clients)
|
|
15
16
|
- [Manual Transactions and current()](#manual-transactions-and-current)
|
|
16
17
|
- [Shutdown and Status Contracts](#shutdown-and-status-contracts)
|
|
@@ -30,7 +31,7 @@ pnpm add @prisma/client
|
|
|
30
31
|
|
|
31
32
|
## When to Use
|
|
32
33
|
|
|
33
|
-
- When using Prisma as your ORM and you want it integrated with fluo's dependency injection and lifecycle hooks.
|
|
34
|
+
- When using Prisma as your ORM on Node.js `>=24.0.0 <27` and you want it integrated with fluo's dependency injection and lifecycle hooks.
|
|
34
35
|
- When you need a reliable way to share a transaction context across multiple services and repositories without passing a `tx` object everywhere.
|
|
35
36
|
- When you want automatic `$connect` on startup and `$disconnect` on shutdown.
|
|
36
37
|
|
|
@@ -65,6 +66,7 @@ import { PrismaService, Transaction, type PrismaServiceFacade } from '@fluojs/pr
|
|
|
65
66
|
import { PrismaClient } from '@prisma/client';
|
|
66
67
|
import { UserRepository } from './user.repository';
|
|
67
68
|
|
|
69
|
+
@Inject(UserRepository)
|
|
68
70
|
export class UserService {
|
|
69
71
|
constructor(private readonly repo: UserRepository) {}
|
|
70
72
|
|
|
@@ -75,28 +77,67 @@ export class UserService {
|
|
|
75
77
|
return user;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
80
|
+
```
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
export class UserRepository {
|
|
81
|
-
constructor(private readonly prisma: PrismaServiceFacade<PrismaClient>) {}
|
|
82
|
+
Calls to `@Transaction()` methods are reentrant. If a decorated method calls another decorated method, they share the same underlying Prisma transaction.
|
|
82
83
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
### Request Transaction Interceptor Compatibility
|
|
85
|
+
|
|
86
|
+
`PrismaTransactionInterceptor` is restored as a deprecated 1.x compatibility export for existing `@UseInterceptors(...)` request-wide boundaries. The unnamed `PrismaModule.forRoot(...)` and `forRootAsync(...)` registrations provide and export it. It delegates to `PrismaService.requestTransaction(...)` and forwards the request `AbortSignal`.
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { Inject } from '@fluojs/core';
|
|
90
|
+
import { Controller, Post, UseInterceptors } from '@fluojs/http';
|
|
91
|
+
import { PrismaTransactionInterceptor } from '@fluojs/prisma';
|
|
92
|
+
import { OrdersService } from './orders.service';
|
|
93
|
+
|
|
94
|
+
@Controller('/orders')
|
|
95
|
+
@Inject(OrdersService)
|
|
96
|
+
export class OrdersController {
|
|
97
|
+
constructor(private readonly orders: OrdersService) {}
|
|
98
|
+
|
|
99
|
+
@Post('/')
|
|
100
|
+
@UseInterceptors(PrismaTransactionInterceptor)
|
|
101
|
+
createOrder() {
|
|
102
|
+
return this.orders.create();
|
|
87
103
|
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
88
106
|
|
|
89
|
-
|
|
90
|
-
|
|
107
|
+
Prefer service-layer `@Transaction()` for new business operations. Use explicit `requestTransaction(...)` when a complete request truly needs one transaction or named/multiple Prisma registrations must select a specific service; the compatibility interceptor targets only the unnamed default registration.
|
|
108
|
+
|
|
109
|
+
When request-wide atomicity is genuinely required, make the boundary and cancellation input visible in application code:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { Inject } from '@fluojs/core';
|
|
113
|
+
import { Controller, Post, type RequestContext } from '@fluojs/http';
|
|
114
|
+
import { PrismaService } from '@fluojs/prisma';
|
|
115
|
+
import { PrismaClient } from '@prisma/client';
|
|
116
|
+
|
|
117
|
+
@Inject(PrismaService, OrdersService)
|
|
118
|
+
@Controller('/orders')
|
|
119
|
+
export class OrdersController {
|
|
120
|
+
constructor(
|
|
121
|
+
private readonly prisma: PrismaService<PrismaClient>,
|
|
122
|
+
private readonly orders: OrdersService,
|
|
123
|
+
) {}
|
|
124
|
+
|
|
125
|
+
@Post('/checkout')
|
|
126
|
+
checkout(input: CheckoutInput, context: RequestContext) {
|
|
127
|
+
const { request } = context;
|
|
128
|
+
return this.prisma.requestTransaction(
|
|
129
|
+
() => this.orders.checkout(input),
|
|
130
|
+
request.signal,
|
|
131
|
+
);
|
|
91
132
|
}
|
|
92
133
|
}
|
|
93
134
|
```
|
|
94
135
|
|
|
95
|
-
|
|
136
|
+
This is a narrow compatibility pattern, not a replacement for service `@Transaction()`. A request-wide transaction can hold database locks for the entire HTTP operation, so keep the boundary short and explicit.
|
|
96
137
|
|
|
97
138
|
### Named Registrations for Multiple Clients
|
|
98
139
|
|
|
99
|
-
When one application container needs more than one Prisma client, register each client with an explicit `name` and inject the matching token with `getPrismaServiceToken(name)`. For named clients, pass an accessor to `@Transaction()` to target the correct service.
|
|
140
|
+
When one application container needs more than one Prisma client, register each client with an explicit `name` and inject the matching token with `getPrismaServiceToken(name)`. For named clients, pass an accessor to `@Transaction()` to target the correct service. Default `@Transaction()` resolution only selects Prisma service/facade-shaped properties; transaction-like objects from other persistence integrations are ignored so ambiguous hosts must use an explicit accessor.
|
|
100
141
|
|
|
101
142
|
```typescript
|
|
102
143
|
import { Inject } from '@fluojs/core';
|
|
@@ -159,16 +200,19 @@ When `transaction()` is called while a transaction context is already active, `P
|
|
|
159
200
|
|
|
160
201
|
### Shutdown and Status Contracts
|
|
161
202
|
|
|
162
|
-
`PrismaService.requestTransaction(...)` is available before and during normal serving, but new request-scoped transactions are rejected once application shutdown has started. During shutdown, open request transactions are aborted, tracked until their outer transaction boundary has settled, and drained before `$disconnect()` runs. This includes nested `requestTransaction(...)` calls opened inside an existing manual `transaction(...)` boundary: they reuse the ambient Prisma transaction client, stay visible in `details.activeRequestTransactions` until the outer boundary finishes, and do not open a second Prisma transaction.
|
|
203
|
+
`PrismaService.requestTransaction(...)` is available before and during normal serving, but new request-scoped transactions are rejected once application shutdown has started. New outer manual `transaction(...)` and service `@Transaction()` boundaries are also rejected after shutdown begins; boundaries that were already open are drained before `$disconnect()` so shutdown does not race an active Prisma transaction. Shutdown also waits for an in-flight `$connect()` to settle before `$disconnect()`; once shutdown starts, a late connect completion cannot restore ready state or admit new transaction work. During shutdown, open request transactions are aborted, tracked until their outer transaction boundary has settled, and drained before `$disconnect()` runs. This includes nested `requestTransaction(...)` calls opened inside an existing manual `transaction(...)` boundary: they reuse the ambient Prisma transaction client, stay visible in `details.activeRequestTransactions` until the outer boundary finishes, and do not open a second Prisma transaction.
|
|
163
204
|
|
|
164
205
|
`createPrismaPlatformStatusSnapshot(...)` and `PrismaService.createPlatformStatusSnapshot()` expose the same lifecycle contract to diagnostics surfaces:
|
|
165
206
|
|
|
166
|
-
- `readiness.status` is `not-ready` before `onModuleInit()` connects the client, while Prisma is shutting down or stopped,
|
|
167
|
-
- `health.status` is `degraded` while request
|
|
168
|
-
- `details.activeRequestTransactions`, `details.lifecycleState`, `details.strictTransactions`, `details.supportsTransaction`, and `details.transactionAbortSignalSupport` describe the current
|
|
169
|
-
- `details.
|
|
207
|
+
- `readiness.status` is `not-ready` before `onModuleInit()` connects the client, while Prisma is shutting down or stopped, when `strictTransactions` is enabled without `$transaction(...)` support, and when the host runtime does not provide `AsyncLocalStorage` while the client supports interactive transactions. In the ALS-unavailable case the readiness reason is `Prisma transaction context requires AsyncLocalStorage support from the host runtime.` and `details.transactionContext` reports `unavailable`; this state is distinct from an ordinary database readiness failure because the Prisma client itself may be connected and otherwise functional.
|
|
208
|
+
- `health.status` is `degraded` while open request, manual, or service transaction boundaries are draining during shutdown and `unhealthy` after disconnect.
|
|
209
|
+
- `details.activeRequestTransactions`, `details.activeTransactionBoundaries`, `details.lifecycleState`, `details.strictTransactions`, `details.supportsTransaction`, and `details.transactionAbortSignalSupport` describe the current transaction and transaction-capability state.
|
|
210
|
+
- `details.activeTransactionBoundaries` counts currently open outer `transaction(...)` and service `@Transaction()` boundaries that shutdown drains before `$disconnect()`. It excludes request-only `requestTransaction(...)` activity, which remains visible separately through `details.activeRequestTransactions`.
|
|
211
|
+
- `details.transactionContext: 'als'` identifies the async-local transaction context used by request and service transaction boundaries. `details.transactionContext: 'unavailable'` indicates the host runtime did not expose a usable `AsyncLocalStorage`, so `transaction()` and `requestTransaction()` reject before opening a Prisma transaction.
|
|
170
212
|
- `ownership.externallyManaged: false` and `ownership.ownsResources: true` mean the package owns the registered client's `$connect()` / `$disconnect()` lifecycle hooks inside the fluo application lifecycle.
|
|
171
213
|
|
|
214
|
+
When `details.transactionContext` is `unavailable`, the package does not fall back to a synchronous stack-based context because that would lose `current()` across async boundaries. The application owns the fallback boundary: callers that still need database access without a transaction context must invoke the raw `PrismaClient` directly (for example through the `PRISMA_CLIENT` token) and manage their own consistency semantics, or run on a host runtime that provides `AsyncLocalStorage` (Node.js `>=24.0.0 <27` is the documented path). Treat the `unavailable` readiness state as operationally actionable — surface it in health checks and route traffic away from transaction-dependent handlers until the host provides ALS or the application switches to a non-transactional access path.
|
|
215
|
+
|
|
172
216
|
### Async Configuration and Isolation
|
|
173
217
|
|
|
174
218
|
Use `PrismaModule.forRootAsync(...)` when the Prisma client must be created from injected configuration or another async source. The async factory is resolved once per application container and is not shared across separate bootstraps, even when the same module definition is reused in tests or multi-app processes.
|
|
@@ -188,7 +232,7 @@ PrismaModule.forRootAsync({
|
|
|
188
232
|
|
|
189
233
|
Within one compiled application, downstream providers share the same resolved `PrismaService`, ALS transaction context, and lifecycle-managed client. Separate application containers receive independent factory results, so `$connect` / `$disconnect` ownership and request transaction state remain isolated.
|
|
190
234
|
|
|
191
|
-
Transaction boundaries require host-provided `AsyncLocalStorage` support. `@fluojs/prisma` resolves
|
|
235
|
+
Transaction boundaries require host-provided `AsyncLocalStorage` support. The package manifest declares `engines.node >=24.0.0 <27`, and the root wrapper is the documented Node.js `>=24.0.0 <27` Prisma integration path. `@fluojs/prisma` resolves ALS through `globalThis.AsyncLocalStorage` when a runtime exposes one, or through the host's `process.getBuiltinModule('node:async_hooks')` boundary on Node.js. If neither path is available or the host builtin lookup fails, `transaction()` and `requestTransaction()` reject before opening a Prisma transaction instead of using a synchronous stack fallback that would lose `current()` across async boundaries; `createPlatformStatusSnapshot().details.transactionContext` reports `unavailable` in that state.
|
|
192
236
|
|
|
193
237
|
### Manual Module Composition
|
|
194
238
|
|
|
@@ -213,7 +257,7 @@ defineModule(ManualPrismaModule, {
|
|
|
213
257
|
### `PrismaModule`
|
|
214
258
|
|
|
215
259
|
- `PrismaModule.forRoot(options)` / `PrismaModule.forRootAsync(options)`
|
|
216
|
-
- `forRoot(...)` and `forRootAsync(...)` also accept `name` for named/scoped registrations.
|
|
260
|
+
- `forRoot(...)` and `forRootAsync(...)` also accept `name` for named/scoped registrations, and `global?: boolean` for unnamed registrations that should export their providers globally.
|
|
217
261
|
- `forRootAsync(...)` accepts DI-aware Prisma options whose factory returns the client and transaction settings; pass `name` or `global` on the top-level async registration so module identity and visibility are decided before the factory runs.
|
|
218
262
|
- `forRootAsync(...)` resolves options once per application container, preserving client lifecycle and request transaction isolation across separate bootstraps.
|
|
219
263
|
- Supports `strictTransactions: true` to throw if transaction support is missing.
|
|
@@ -226,7 +270,7 @@ defineModule(ManualPrismaModule, {
|
|
|
226
270
|
- `current(): TClient | PrismaTransactionClient<TClient>`
|
|
227
271
|
- Returns the ambient transaction client or the root client.
|
|
228
272
|
- `transaction(fn, options?): Promise<T>`
|
|
229
|
-
- Runs a function within an interactive transaction. If a transaction context is already active, the callback reuses that context; nested transaction options are rejected because no new Prisma transaction boundary is opened.
|
|
273
|
+
- Runs a function within an interactive transaction. If a transaction context is already active, the callback reuses that context; nested transaction options are rejected because no new Prisma transaction boundary is opened. New outer transaction boundaries are rejected once shutdown starts.
|
|
230
274
|
- `requestTransaction(fn, signal?, options?): Promise<T>`
|
|
231
275
|
- Specialized transaction boundary for HTTP request lifecycles. It is abort-aware, drains during shutdown before disconnect, and retries without `signal` when a Prisma client rejects that option. Like `transaction()`, nested calls reuse the active transaction context and reject nested options to avoid silently ignoring transaction settings.
|
|
232
276
|
|
|
@@ -234,7 +278,13 @@ Use `PrismaService<TClient>` when a provider only needs wrapper methods such as
|
|
|
234
278
|
|
|
235
279
|
### `Transaction`
|
|
236
280
|
|
|
237
|
-
- Standard TC39 method decorator for service-layer transaction boundaries. It resolves
|
|
281
|
+
- Standard TC39 method decorator for service-layer transaction boundaries. It resolves a Prisma service/facade-shaped property by default, accepts an accessor for named clients or ambiguous hosts, and can forward Prisma transaction options to the outer boundary.
|
|
282
|
+
|
|
283
|
+
### `PrismaTransactionInterceptor` (deprecated compatibility)
|
|
284
|
+
|
|
285
|
+
- Request-wide HTTP compatibility interceptor for existing 1.x imports.
|
|
286
|
+
- Delegates to `PrismaService.requestTransaction(...)` and forwards request cancellation.
|
|
287
|
+
- Prefer service `@Transaction()` or an explicit request boundary in new code.
|
|
238
288
|
|
|
239
289
|
### `PRISMA_CLIENT` (Token)
|
|
240
290
|
|
|
@@ -262,6 +312,7 @@ token are deliberately not exported.
|
|
|
262
312
|
|
|
263
313
|
### Related exported types
|
|
264
314
|
|
|
315
|
+
- `PrismaAsyncModuleOptions<TClient, TTransactionClient, TTransactionOptions>`
|
|
265
316
|
- `PrismaModuleOptions`
|
|
266
317
|
- `PrismaClientLike`
|
|
267
318
|
- `PrismaHandleProvider`
|
|
@@ -269,6 +320,8 @@ token are deliberately not exported.
|
|
|
269
320
|
- `PrismaTransactionClient<TClient>`
|
|
270
321
|
- `InferPrismaTransactionClient<TClient>`
|
|
271
322
|
- `InferPrismaTransactionOptions<TClient>`
|
|
323
|
+
- `PrismaPlatformStatusSnapshotInput`
|
|
324
|
+
- The input contract for `createPrismaPlatformStatusSnapshot(...)`; omit `activeTransactionBoundaries` when no outer service or manual transaction boundary is open and the snapshot reports `0`.
|
|
272
325
|
|
|
273
326
|
## Related Packages
|
|
274
327
|
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Module, type Constructor, type Token } from '@fluojs/core';
|
|
2
|
+
import type { Provider } from '@fluojs/di';
|
|
3
|
+
import type { MiddlewareLike } from '@fluojs/http';
|
|
4
|
+
type PrismaModuleDefinition = Parameters<typeof Module>[0] & {
|
|
5
|
+
controllers?: Constructor[];
|
|
6
|
+
exports?: Token[];
|
|
7
|
+
imports?: PrismaModuleType[];
|
|
8
|
+
middleware?: MiddlewareLike[];
|
|
9
|
+
providers?: Provider[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Module class accepted by the Fluo runtime module graph.
|
|
13
|
+
*/
|
|
14
|
+
export type PrismaModuleType = Constructor & {
|
|
15
|
+
definition?: PrismaModuleDefinition;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Defines the lifecycle hook invoked after module initialization.
|
|
19
|
+
*/
|
|
20
|
+
export interface OnModuleInit {
|
|
21
|
+
onModuleInit(): Promise<void> | void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Defines the lifecycle hook invoked during application shutdown.
|
|
25
|
+
*/
|
|
26
|
+
export interface OnApplicationShutdown {
|
|
27
|
+
onApplicationShutdown(): Promise<void> | void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Defines one active request transaction.
|
|
31
|
+
*/
|
|
32
|
+
export type ActiveRequestTransaction = {
|
|
33
|
+
abort(reason?: unknown): void;
|
|
34
|
+
settled: Promise<void>;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Defines one active request transaction registration.
|
|
38
|
+
*/
|
|
39
|
+
export type ActiveRequestTransactionHandle = {
|
|
40
|
+
active: ActiveRequestTransaction;
|
|
41
|
+
settle(): void;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Creates a module class with metadata consumed by the runtime module graph.
|
|
45
|
+
*
|
|
46
|
+
* @param definition Module composition metadata.
|
|
47
|
+
* @param moduleName Constructor name used by runtime diagnostics.
|
|
48
|
+
* @returns A new module class carrying the supplied metadata.
|
|
49
|
+
*/
|
|
50
|
+
export declare function definePrismaModule(definition: Parameters<typeof Module>[0], moduleName: string): PrismaModuleType;
|
|
51
|
+
/**
|
|
52
|
+
* Races an operation against an abort signal.
|
|
53
|
+
*
|
|
54
|
+
* @param fn Async operation to execute while observing the abort signal.
|
|
55
|
+
* @param signal Abort signal that can cancel the operation.
|
|
56
|
+
* @returns The resolved value from `fn` when no abort happens first.
|
|
57
|
+
*/
|
|
58
|
+
export declare function raceWithAbort<T>(fn: () => Promise<T>, signal: AbortSignal): Promise<T>;
|
|
59
|
+
/**
|
|
60
|
+
* Normalizes an abort reason into an AbortError.
|
|
61
|
+
*
|
|
62
|
+
* @param reason Abort reason attached to the triggering signal.
|
|
63
|
+
* @returns A normalized abort error.
|
|
64
|
+
*/
|
|
65
|
+
export declare function createAbortError(reason: unknown): Error;
|
|
66
|
+
/**
|
|
67
|
+
* Creates an abort context that forwards an optional caller signal.
|
|
68
|
+
*
|
|
69
|
+
* @param signal Optional caller-owned abort signal.
|
|
70
|
+
* @returns The owned controller, signal, and listener cleanup.
|
|
71
|
+
*/
|
|
72
|
+
export declare function createRequestAbortContext(signal?: AbortSignal): {
|
|
73
|
+
controller: AbortController;
|
|
74
|
+
cleanup(): void;
|
|
75
|
+
signal: AbortSignal;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Tracks a request transaction until its caller settles it.
|
|
79
|
+
*
|
|
80
|
+
* @param activeRequestTransactions Active request transaction set.
|
|
81
|
+
* @param controller Controller used to abort the transaction.
|
|
82
|
+
* @returns The tracked transaction and its settlement function.
|
|
83
|
+
*/
|
|
84
|
+
export declare function trackActiveRequestTransaction(activeRequestTransactions: Set<ActiveRequestTransaction>, controller: AbortController): ActiveRequestTransactionHandle;
|
|
85
|
+
/**
|
|
86
|
+
* Stops tracking a settled request transaction.
|
|
87
|
+
*
|
|
88
|
+
* @param activeRequestTransactions Active request transaction set.
|
|
89
|
+
* @param handle Transaction registration to remove.
|
|
90
|
+
*/
|
|
91
|
+
export declare function untrackActiveRequestTransaction(activeRequestTransactions: Set<ActiveRequestTransaction>, handle: ActiveRequestTransactionHandle): void;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=integration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../src/integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG;IAC3D,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,cAAc,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG;IAC3C,UAAU,CAAC,EAAE,sBAAsB,CAAC;CACrC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC/C;AAED;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG;IAC3C,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,EACxC,UAAU,EAAE,MAAM,GACjB,gBAAgB,CAalB;AAED;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAuB5F;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,CAKvD;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG;IAC/D,UAAU,EAAE,eAAe,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;CACrB,CAiBA;AAED;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,yBAAyB,EAAE,GAAG,CAAC,wBAAwB,CAAC,EACxD,UAAU,EAAE,eAAe,GAC1B,8BAA8B,CAehC;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,yBAAyB,EAAE,GAAG,CAAC,wBAAwB,CAAC,EACxD,MAAM,EAAE,8BAA8B,GACrC,IAAI,CAGN"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Module } from '@fluojs/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module class accepted by the Fluo runtime module graph.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Defines the lifecycle hook invoked after module initialization.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Defines the lifecycle hook invoked during application shutdown.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Defines one active request transaction.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Defines one active request transaction registration.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a module class with metadata consumed by the runtime module graph.
|
|
25
|
+
*
|
|
26
|
+
* @param definition Module composition metadata.
|
|
27
|
+
* @param moduleName Constructor name used by runtime diagnostics.
|
|
28
|
+
* @returns A new module class carrying the supplied metadata.
|
|
29
|
+
*/
|
|
30
|
+
export function definePrismaModule(definition, moduleName) {
|
|
31
|
+
const moduleType = {
|
|
32
|
+
[moduleName]: class {}
|
|
33
|
+
}[moduleName];
|
|
34
|
+
Module(definition)(moduleType, {
|
|
35
|
+
addInitializer() {},
|
|
36
|
+
kind: 'class',
|
|
37
|
+
metadata: {},
|
|
38
|
+
name: moduleName
|
|
39
|
+
});
|
|
40
|
+
return moduleType;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Races an operation against an abort signal.
|
|
45
|
+
*
|
|
46
|
+
* @param fn Async operation to execute while observing the abort signal.
|
|
47
|
+
* @param signal Abort signal that can cancel the operation.
|
|
48
|
+
* @returns The resolved value from `fn` when no abort happens first.
|
|
49
|
+
*/
|
|
50
|
+
export async function raceWithAbort(fn, signal) {
|
|
51
|
+
if (signal.aborted) {
|
|
52
|
+
throw createAbortError(signal.reason);
|
|
53
|
+
}
|
|
54
|
+
return await new Promise((resolve, reject) => {
|
|
55
|
+
const onAbort = () => {
|
|
56
|
+
reject(createAbortError(signal.reason));
|
|
57
|
+
};
|
|
58
|
+
signal.addEventListener('abort', onAbort, {
|
|
59
|
+
once: true
|
|
60
|
+
});
|
|
61
|
+
let fnResultPromise;
|
|
62
|
+
try {
|
|
63
|
+
fnResultPromise = Promise.resolve(fn());
|
|
64
|
+
} catch (syncError) {
|
|
65
|
+
fnResultPromise = Promise.reject(syncError);
|
|
66
|
+
}
|
|
67
|
+
fnResultPromise.then(resolve, reject).finally(() => {
|
|
68
|
+
signal.removeEventListener('abort', onAbort);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Normalizes an abort reason into an AbortError.
|
|
75
|
+
*
|
|
76
|
+
* @param reason Abort reason attached to the triggering signal.
|
|
77
|
+
* @returns A normalized abort error.
|
|
78
|
+
*/
|
|
79
|
+
export function createAbortError(reason) {
|
|
80
|
+
const message = reason instanceof Error ? reason.message : 'Request aborted before response commit.';
|
|
81
|
+
const error = new Error(message);
|
|
82
|
+
error.name = 'AbortError';
|
|
83
|
+
return error;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Creates an abort context that forwards an optional caller signal.
|
|
88
|
+
*
|
|
89
|
+
* @param signal Optional caller-owned abort signal.
|
|
90
|
+
* @returns The owned controller, signal, and listener cleanup.
|
|
91
|
+
*/
|
|
92
|
+
export function createRequestAbortContext(signal) {
|
|
93
|
+
const controller = new AbortController();
|
|
94
|
+
const forwardAbort = () => controller.abort(signal?.reason);
|
|
95
|
+
if (signal?.aborted) {
|
|
96
|
+
forwardAbort();
|
|
97
|
+
} else {
|
|
98
|
+
signal?.addEventListener('abort', forwardAbort, {
|
|
99
|
+
once: true
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
controller,
|
|
104
|
+
cleanup: () => {
|
|
105
|
+
signal?.removeEventListener('abort', forwardAbort);
|
|
106
|
+
},
|
|
107
|
+
signal: controller.signal
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Tracks a request transaction until its caller settles it.
|
|
113
|
+
*
|
|
114
|
+
* @param activeRequestTransactions Active request transaction set.
|
|
115
|
+
* @param controller Controller used to abort the transaction.
|
|
116
|
+
* @returns The tracked transaction and its settlement function.
|
|
117
|
+
*/
|
|
118
|
+
export function trackActiveRequestTransaction(activeRequestTransactions, controller) {
|
|
119
|
+
let settle;
|
|
120
|
+
const settled = new Promise(resolve => {
|
|
121
|
+
settle = resolve;
|
|
122
|
+
});
|
|
123
|
+
const active = {
|
|
124
|
+
abort(reason) {
|
|
125
|
+
controller.abort(reason);
|
|
126
|
+
},
|
|
127
|
+
settled
|
|
128
|
+
};
|
|
129
|
+
activeRequestTransactions.add(active);
|
|
130
|
+
return {
|
|
131
|
+
active,
|
|
132
|
+
settle
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Stops tracking a settled request transaction.
|
|
138
|
+
*
|
|
139
|
+
* @param activeRequestTransactions Active request transaction set.
|
|
140
|
+
* @param handle Transaction registration to remove.
|
|
141
|
+
*/
|
|
142
|
+
export function untrackActiveRequestTransaction(activeRequestTransactions, handle) {
|
|
143
|
+
activeRequestTransactions.delete(handle.active);
|
|
144
|
+
handle.settle();
|
|
145
|
+
}
|