@fluojs/prisma 1.0.0-beta.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/LICENSE +21 -0
- package/README.ko.md +159 -0
- package/README.md +159 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/module.d.ts +23 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +81 -0
- package/dist/service.d.ts +90 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +218 -0
- package/dist/status.d.ts +14 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +64 -0
- package/dist/tokens.d.ts +5 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +4 -0
- package/dist/transaction.d.ts +25 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +39 -0
- package/dist/types.d.ts +83 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fluo contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.ko.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# @fluojs/prisma
|
|
2
|
+
|
|
3
|
+
<p><a href="./README.md"><kbd>English</kbd></a> <strong><kbd>한국어</kbd></strong></p>
|
|
4
|
+
|
|
5
|
+
fluo 애플리케이션을 위한 Prisma 라이프사이클 및 ALS 기반 트랜잭션 컨텍스트 모듈입니다. Prisma 클라이언트를 모듈 시스템에 연결하여 자동 연결 관리 및 요청 범위 트랜잭션을 제공합니다.
|
|
6
|
+
|
|
7
|
+
## 목차
|
|
8
|
+
|
|
9
|
+
- [설치](#설치)
|
|
10
|
+
- [사용 시점](#사용-시점)
|
|
11
|
+
- [빠른 시작](#빠른-시작)
|
|
12
|
+
- [공통 패턴](#공통-패턴)
|
|
13
|
+
- [PrismaService와 current()](#prismaservice와-current)
|
|
14
|
+
- [수동 트랜잭션](#수동-트랜잭션)
|
|
15
|
+
- [자동 요청 트랜잭션](#자동-요청-트랜잭션)
|
|
16
|
+
- [수동 모듈 조합](#수동-모듈-조합)
|
|
17
|
+
- [공개 API 개요](#공개-api-개요)
|
|
18
|
+
- [관련 패키지](#관련-패키지)
|
|
19
|
+
- [예제 소스](#예제-소스)
|
|
20
|
+
|
|
21
|
+
## 설치
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add @fluojs/prisma
|
|
25
|
+
# @prisma/client도 함께 설치되어 있어야 합니다.
|
|
26
|
+
pnpm add @prisma/client
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 사용 시점
|
|
30
|
+
|
|
31
|
+
- Prisma를 ORM으로 사용하면서 fluo의 의존성 주입 및 라이프사이클 훅과 통합하고 싶을 때.
|
|
32
|
+
- 여러 서비스와 리포지토리 사이에서 `tx` 객체를 일일이 전달하지 않고도 트랜잭션 컨텍스트를 안정적으로 공유하고 싶을 때.
|
|
33
|
+
- 애플리케이션 시작 시 자동 `$connect`, 종료 시 자동 `$disconnect`가 필요할 때.
|
|
34
|
+
|
|
35
|
+
## 빠른 시작
|
|
36
|
+
|
|
37
|
+
루트 모듈에 `PrismaClient` 인스턴스를 전달하여 `PrismaModule`을 등록합니다.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { Module } from '@fluojs/core';
|
|
41
|
+
import { PrismaModule } from '@fluojs/prisma';
|
|
42
|
+
import { PrismaClient } from '@prisma/client';
|
|
43
|
+
|
|
44
|
+
const prisma = new PrismaClient();
|
|
45
|
+
|
|
46
|
+
@Module({
|
|
47
|
+
imports: [
|
|
48
|
+
PrismaModule.forRoot({ client: prisma }),
|
|
49
|
+
],
|
|
50
|
+
})
|
|
51
|
+
class AppModule {}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## 공통 패턴
|
|
55
|
+
|
|
56
|
+
### PrismaService와 current()
|
|
57
|
+
|
|
58
|
+
`PrismaService`는 Prisma와 상호작용하는 기본 방법입니다. `current()` 메서드는 트랜잭션 범위 내에 있으면 자동으로 트랜잭션용 클라이언트를, 그렇지 않으면 루트 클라이언트를 반환합니다.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { Inject } from '@fluojs/core';
|
|
62
|
+
import { PrismaService } from '@fluojs/prisma';
|
|
63
|
+
import { PrismaClient } from '@prisma/client';
|
|
64
|
+
|
|
65
|
+
@Inject(PrismaService)
|
|
66
|
+
export class UserRepository {
|
|
67
|
+
constructor(private readonly prisma: PrismaService<PrismaClient>) {}
|
|
68
|
+
|
|
69
|
+
async findById(id: string) {
|
|
70
|
+
// current()는 생성된 Prisma 타입과 자동완성을 그대로 유지합니다.
|
|
71
|
+
return this.prisma.current().user.findUnique({ where: { id } });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 수동 트랜잭션
|
|
77
|
+
|
|
78
|
+
`prisma.transaction()`을 사용하여 대화형 트랜잭션 블록을 생성합니다. 블록 내부의 모든 `current()` 호출은 트랜잭션 범위의 클라이언트를 사용합니다.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
await this.prisma.transaction(async () => {
|
|
82
|
+
const user = await this.prisma.current().user.create({ data });
|
|
83
|
+
await this.prisma.current().profile.create({ data: { userId: user.id } });
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 자동 요청 트랜잭션
|
|
88
|
+
|
|
89
|
+
컨트롤러나 메서드에 `PrismaTransactionInterceptor`를 적용하면 전체 요청을 자동으로 트랜잭션으로 감쌉니다.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { Post, UseInterceptors } from '@fluojs/http';
|
|
93
|
+
import { PrismaTransactionInterceptor } from '@fluojs/prisma';
|
|
94
|
+
|
|
95
|
+
@UseInterceptors(PrismaTransactionInterceptor)
|
|
96
|
+
class UserController {
|
|
97
|
+
@Post()
|
|
98
|
+
async create() {
|
|
99
|
+
// 이후 PrismaService.current()를 사용하는 모든 리포지토리 호출은 이 트랜잭션을 공유합니다.
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 수동 모듈 조합
|
|
105
|
+
|
|
106
|
+
`PrismaModule.forRoot(...)` / `forRootAsync(...)`를 사용해 Prisma를 등록합니다. 커스텀 `defineModule(...)` 등록 안에서 Prisma 지원을 조합해야 할 때도 동일한 모듈 entrypoint를 import해서 사용하세요.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { defineModule } from '@fluojs/runtime';
|
|
110
|
+
import { PrismaModule, PrismaService, PrismaTransactionInterceptor } from '@fluojs/prisma';
|
|
111
|
+
import { PrismaClient } from '@prisma/client';
|
|
112
|
+
|
|
113
|
+
const prisma = new PrismaClient();
|
|
114
|
+
|
|
115
|
+
class ManualPrismaModule {}
|
|
116
|
+
|
|
117
|
+
defineModule(ManualPrismaModule, {
|
|
118
|
+
exports: [PrismaService, PrismaTransactionInterceptor],
|
|
119
|
+
imports: [PrismaModule.forRoot({ client: prisma })],
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 공개 API 개요
|
|
124
|
+
|
|
125
|
+
### `PrismaModule`
|
|
126
|
+
|
|
127
|
+
- `PrismaModule.forRoot(options)` / `PrismaModule.forRootAsync(options)`
|
|
128
|
+
- `forRootAsync(...)`는 `AsyncModuleOptions<PrismaModuleOptions<...>>`를 받습니다.
|
|
129
|
+
- `strictTransactions: true` 설정 시 트랜잭션 미지원 환경에서 즉시 예외를 발생시킵니다.
|
|
130
|
+
|
|
131
|
+
### `PrismaService<TClient>`
|
|
132
|
+
|
|
133
|
+
- `current(): TClient | PrismaTransactionClient<TClient>`
|
|
134
|
+
- 현재 컨텍스트에 맞는 트랜잭션 클라이언트 또는 루트 클라이언트를 반환합니다.
|
|
135
|
+
- `transaction(fn, options?): Promise<T>`
|
|
136
|
+
- 대화형 트랜잭션 내에서 함수를 실행합니다.
|
|
137
|
+
- `requestTransaction(fn, signal?, options?): Promise<T>`
|
|
138
|
+
- HTTP 요청 라이프사이클에 특화된 트랜잭션 경계를 실행합니다.
|
|
139
|
+
|
|
140
|
+
### `PRISMA_CLIENT` (Token)
|
|
141
|
+
|
|
142
|
+
원시 `PrismaClient` 인스턴스를 위한 주입 토큰입니다.
|
|
143
|
+
|
|
144
|
+
### 관련 export 타입
|
|
145
|
+
|
|
146
|
+
- `PrismaModuleOptions`
|
|
147
|
+
- `PrismaTransactionClient<TClient>`
|
|
148
|
+
- `InferPrismaTransactionClient<TClient>`
|
|
149
|
+
- `InferPrismaTransactionOptions<TClient>`
|
|
150
|
+
|
|
151
|
+
## 관련 패키지
|
|
152
|
+
|
|
153
|
+
- `@fluojs/runtime`: 애플리케이션 라이프사이클 훅을 관리합니다.
|
|
154
|
+
- `@fluojs/http`: 인터셉터 시스템을 제공합니다.
|
|
155
|
+
- `@fluojs/terminus`: Prisma를 위한 헬스 인디케이터를 제공합니다.
|
|
156
|
+
|
|
157
|
+
## 예제 소스
|
|
158
|
+
|
|
159
|
+
- `packages/prisma/src/vertical-slice.test.ts`: 표준 DTO → 서비스 → 리포지토리 → Prisma 흐름 예제.
|
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# @fluojs/prisma
|
|
2
|
+
|
|
3
|
+
<p><strong><kbd>English</kbd></strong> <a href="./README.ko.md"><kbd>한국어</kbd></a></p>
|
|
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.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Installation](#installation)
|
|
10
|
+
- [When to Use](#when-to-use)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [Common Patterns](#common-patterns)
|
|
13
|
+
- [PrismaService and current()](#prismaservice-and-current)
|
|
14
|
+
- [Manual Transactions](#manual-transactions)
|
|
15
|
+
- [Automatic Request Transactions](#automatic-request-transactions)
|
|
16
|
+
- [Manual Module Composition](#manual-module-composition)
|
|
17
|
+
- [Public API Overview](#public-api-overview)
|
|
18
|
+
- [Related Packages](#related-packages)
|
|
19
|
+
- [Example Sources](#example-sources)
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add @fluojs/prisma
|
|
25
|
+
# Ensure @prisma/client is also installed
|
|
26
|
+
pnpm add @prisma/client
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## When to Use
|
|
30
|
+
|
|
31
|
+
- When using Prisma as your ORM and you want it integrated with fluo's dependency injection and lifecycle hooks.
|
|
32
|
+
- When you need a reliable way to share a transaction context across multiple services and repositories without passing a `tx` object everywhere.
|
|
33
|
+
- When you want automatic `$connect` on startup and `$disconnect` on shutdown.
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
Register the `PrismaModule` in your root module by providing a `PrismaClient` instance.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { Module } from '@fluojs/core';
|
|
41
|
+
import { PrismaModule } from '@fluojs/prisma';
|
|
42
|
+
import { PrismaClient } from '@prisma/client';
|
|
43
|
+
|
|
44
|
+
const prisma = new PrismaClient();
|
|
45
|
+
|
|
46
|
+
@Module({
|
|
47
|
+
imports: [
|
|
48
|
+
PrismaModule.forRoot({ client: prisma }),
|
|
49
|
+
],
|
|
50
|
+
})
|
|
51
|
+
class AppModule {}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Common Patterns
|
|
55
|
+
|
|
56
|
+
### PrismaService and current()
|
|
57
|
+
|
|
58
|
+
The `PrismaService` is the primary way to interact with Prisma. Its `current()` method automatically returns the active transaction client if inside a transaction scope, or the root client otherwise.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { Inject } from '@fluojs/core';
|
|
62
|
+
import { PrismaService } from '@fluojs/prisma';
|
|
63
|
+
import { PrismaClient } from '@prisma/client';
|
|
64
|
+
|
|
65
|
+
@Inject(PrismaService)
|
|
66
|
+
export class UserRepository {
|
|
67
|
+
constructor(private readonly prisma: PrismaService<PrismaClient>) {}
|
|
68
|
+
|
|
69
|
+
async findById(id: string) {
|
|
70
|
+
// current() preserves your generated Prisma types and autocomplete
|
|
71
|
+
return this.prisma.current().user.findUnique({ where: { id } });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Manual Transactions
|
|
77
|
+
|
|
78
|
+
Use `prisma.transaction()` to create an interactive transaction block. Any calls to `current()` inside the block will use the transaction-scoped client.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
await this.prisma.transaction(async () => {
|
|
82
|
+
const user = await this.prisma.current().user.create({ data });
|
|
83
|
+
await this.prisma.current().profile.create({ data: { userId: user.id } });
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Automatic Request Transactions
|
|
88
|
+
|
|
89
|
+
Apply the `PrismaTransactionInterceptor` to a controller or method to wrap the entire request in a transaction automatically.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { Post, UseInterceptors } from '@fluojs/http';
|
|
93
|
+
import { PrismaTransactionInterceptor } from '@fluojs/prisma';
|
|
94
|
+
|
|
95
|
+
@UseInterceptors(PrismaTransactionInterceptor)
|
|
96
|
+
class UserController {
|
|
97
|
+
@Post()
|
|
98
|
+
async create() {
|
|
99
|
+
// All downstream repository calls via PrismaService.current() share this tx
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Manual Module Composition
|
|
105
|
+
|
|
106
|
+
Use `PrismaModule.forRoot(...)` / `forRootAsync(...)` to register Prisma. When you need to compose Prisma support inside a custom `defineModule(...)` registration, import the module entrypoint there as well.
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { defineModule } from '@fluojs/runtime';
|
|
110
|
+
import { PrismaModule, PrismaService, PrismaTransactionInterceptor } from '@fluojs/prisma';
|
|
111
|
+
import { PrismaClient } from '@prisma/client';
|
|
112
|
+
|
|
113
|
+
const prisma = new PrismaClient();
|
|
114
|
+
|
|
115
|
+
class ManualPrismaModule {}
|
|
116
|
+
|
|
117
|
+
defineModule(ManualPrismaModule, {
|
|
118
|
+
exports: [PrismaService, PrismaTransactionInterceptor],
|
|
119
|
+
imports: [PrismaModule.forRoot({ client: prisma })],
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Public API Overview
|
|
124
|
+
|
|
125
|
+
### `PrismaModule`
|
|
126
|
+
|
|
127
|
+
- `PrismaModule.forRoot(options)` / `PrismaModule.forRootAsync(options)`
|
|
128
|
+
- `forRootAsync(...)` accepts `AsyncModuleOptions<PrismaModuleOptions<...>>`.
|
|
129
|
+
- Supports `strictTransactions: true` to throw if transaction support is missing.
|
|
130
|
+
|
|
131
|
+
### `PrismaService<TClient>`
|
|
132
|
+
|
|
133
|
+
- `current(): TClient | PrismaTransactionClient<TClient>`
|
|
134
|
+
- Returns the ambient transaction client or the root client.
|
|
135
|
+
- `transaction(fn, options?): Promise<T>`
|
|
136
|
+
- Runs a function within an interactive transaction.
|
|
137
|
+
- `requestTransaction(fn, signal?, options?): Promise<T>`
|
|
138
|
+
- Specialized transaction boundary for HTTP request lifecycles.
|
|
139
|
+
|
|
140
|
+
### `PRISMA_CLIENT` (Token)
|
|
141
|
+
|
|
142
|
+
Injectable token for the raw `PrismaClient` instance.
|
|
143
|
+
|
|
144
|
+
### Related exported types
|
|
145
|
+
|
|
146
|
+
- `PrismaModuleOptions`
|
|
147
|
+
- `PrismaTransactionClient<TClient>`
|
|
148
|
+
- `InferPrismaTransactionClient<TClient>`
|
|
149
|
+
- `InferPrismaTransactionOptions<TClient>`
|
|
150
|
+
|
|
151
|
+
## Related Packages
|
|
152
|
+
|
|
153
|
+
- `@fluojs/runtime`: Manages the application lifecycle hooks.
|
|
154
|
+
- `@fluojs/http`: Provides the interceptor system.
|
|
155
|
+
- `@fluojs/terminus`: Provides a health indicator for Prisma.
|
|
156
|
+
|
|
157
|
+
## Example Sources
|
|
158
|
+
|
|
159
|
+
- `packages/prisma/src/vertical-slice.test.ts`: DTO → Service → Repository → Prisma flow.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AsyncModuleOptions } from '@fluojs/core';
|
|
2
|
+
import { type ModuleType } from '@fluojs/runtime';
|
|
3
|
+
import type { InferPrismaTransactionClient, InferPrismaTransactionOptions, PrismaClientLike, PrismaModuleOptions } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Runtime module entrypoint for Prisma lifecycle and transaction wiring.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PrismaModule {
|
|
8
|
+
/**
|
|
9
|
+
* Registers Prisma providers from static options.
|
|
10
|
+
*
|
|
11
|
+
* @param options Prisma module options with client handle and strict transaction mode.
|
|
12
|
+
* @returns A module definition that exports `PrismaService` and `PrismaTransactionInterceptor`.
|
|
13
|
+
*/
|
|
14
|
+
static forRoot<TClient extends PrismaClientLike<TTransactionClient, TTransactionOptions>, TTransactionClient = InferPrismaTransactionClient<TClient>, TTransactionOptions = InferPrismaTransactionOptions<TClient>>(options: PrismaModuleOptions<TClient, TTransactionClient, TTransactionOptions>): ModuleType;
|
|
15
|
+
/**
|
|
16
|
+
* Registers Prisma providers from an async DI factory.
|
|
17
|
+
*
|
|
18
|
+
* @param options Async module options that resolve Prisma client/module configuration.
|
|
19
|
+
* @returns A module definition that memoizes async options resolution per module instance.
|
|
20
|
+
*/
|
|
21
|
+
static forRootAsync<TClient extends PrismaClientLike<TTransactionClient, TTransactionOptions>, TTransactionClient = InferPrismaTransactionClient<TClient>, TTransactionOptions = InferPrismaTransactionOptions<TClient>>(options: AsyncModuleOptions<PrismaModuleOptions<TClient, TTransactionClient, TTransactionOptions>>): ModuleType;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAgB,MAAM,cAAc,CAAC;AAErE,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AA6GpB;;GAEG;AACH,qBAAa,YAAY;IACvB;;;;;OAKG;IACH,MAAM,CAAC,OAAO,CACZ,OAAO,SAAS,gBAAgB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,EACzE,kBAAkB,GAAG,4BAA4B,CAAC,OAAO,CAAC,EAC1D,mBAAmB,GAAG,6BAA6B,CAAC,OAAO,CAAC,EAE5D,OAAO,EAAE,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,GAC7E,UAAU;IAIb;;;;;OAKG;IACH,MAAM,CAAC,YAAY,CACjB,OAAO,SAAS,gBAAgB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,EACzE,kBAAkB,GAAG,4BAA4B,CAAC,OAAO,CAAC,EAC1D,mBAAmB,GAAG,6BAA6B,CAAC,OAAO,CAAC,EAE5D,OAAO,EAAE,kBAAkB,CAAC,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,CAAC,GACjG,UAAU;CAGd"}
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { defineModule } from '@fluojs/runtime';
|
|
2
|
+
import { PrismaService } from './service.js';
|
|
3
|
+
import { PRISMA_CLIENT, PRISMA_OPTIONS } from './tokens.js';
|
|
4
|
+
import { PrismaTransactionInterceptor } from './transaction.js';
|
|
5
|
+
const PRISMA_NORMALIZED_OPTIONS = Symbol('fluo.prisma.normalized-options');
|
|
6
|
+
const PRISMA_MODULE_EXPORTS = [PrismaService, PrismaTransactionInterceptor];
|
|
7
|
+
function normalizePrismaModuleOptions(options) {
|
|
8
|
+
return {
|
|
9
|
+
client: options.client,
|
|
10
|
+
strictTransactions: options.strictTransactions ?? false
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function createPrismaRuntimeProviders(normalizedOptionsProvider) {
|
|
14
|
+
return [normalizedOptionsProvider, {
|
|
15
|
+
inject: [PRISMA_NORMALIZED_OPTIONS],
|
|
16
|
+
provide: PRISMA_CLIENT,
|
|
17
|
+
useFactory: options => options.client
|
|
18
|
+
}, {
|
|
19
|
+
inject: [PRISMA_NORMALIZED_OPTIONS],
|
|
20
|
+
provide: PRISMA_OPTIONS,
|
|
21
|
+
useFactory: options => ({
|
|
22
|
+
strictTransactions: options.strictTransactions
|
|
23
|
+
})
|
|
24
|
+
}, PrismaService, PrismaTransactionInterceptor];
|
|
25
|
+
}
|
|
26
|
+
function buildPrismaModule(options) {
|
|
27
|
+
class PrismaRootModuleDefinition {}
|
|
28
|
+
return defineModule(PrismaRootModuleDefinition, {
|
|
29
|
+
exports: PRISMA_MODULE_EXPORTS,
|
|
30
|
+
providers: createPrismaRuntimeProviders({
|
|
31
|
+
provide: PRISMA_NORMALIZED_OPTIONS,
|
|
32
|
+
useValue: normalizePrismaModuleOptions(options)
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function buildPrismaModuleAsync(options) {
|
|
37
|
+
class PrismaAsyncModuleDefinition {}
|
|
38
|
+
const factory = options.useFactory;
|
|
39
|
+
let cachedResult;
|
|
40
|
+
const memoizedFactory = (...deps) => {
|
|
41
|
+
if (!cachedResult) {
|
|
42
|
+
cachedResult = Promise.resolve(factory(...deps)).then(resolved => normalizePrismaModuleOptions(resolved));
|
|
43
|
+
}
|
|
44
|
+
return cachedResult;
|
|
45
|
+
};
|
|
46
|
+
const normalizedOptionsProvider = {
|
|
47
|
+
inject: options.inject,
|
|
48
|
+
provide: PRISMA_NORMALIZED_OPTIONS,
|
|
49
|
+
scope: 'singleton',
|
|
50
|
+
useFactory: (...deps) => memoizedFactory(...deps)
|
|
51
|
+
};
|
|
52
|
+
return defineModule(PrismaAsyncModuleDefinition, {
|
|
53
|
+
exports: PRISMA_MODULE_EXPORTS,
|
|
54
|
+
providers: createPrismaRuntimeProviders(normalizedOptionsProvider)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Runtime module entrypoint for Prisma lifecycle and transaction wiring.
|
|
60
|
+
*/
|
|
61
|
+
export class PrismaModule {
|
|
62
|
+
/**
|
|
63
|
+
* Registers Prisma providers from static options.
|
|
64
|
+
*
|
|
65
|
+
* @param options Prisma module options with client handle and strict transaction mode.
|
|
66
|
+
* @returns A module definition that exports `PrismaService` and `PrismaTransactionInterceptor`.
|
|
67
|
+
*/
|
|
68
|
+
static forRoot(options) {
|
|
69
|
+
return buildPrismaModule(options);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Registers Prisma providers from an async DI factory.
|
|
74
|
+
*
|
|
75
|
+
* @param options Async module options that resolve Prisma client/module configuration.
|
|
76
|
+
* @returns A module definition that memoizes async options resolution per module instance.
|
|
77
|
+
*/
|
|
78
|
+
static forRootAsync(options) {
|
|
79
|
+
return buildPrismaModuleAsync(options);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { OnApplicationShutdown, OnModuleInit } from '@fluojs/runtime';
|
|
2
|
+
import type { InferPrismaTransactionClient, InferPrismaTransactionOptions, PrismaClientLike, PrismaHandleProvider } from './types.js';
|
|
3
|
+
interface PrismaServiceOptions {
|
|
4
|
+
strictTransactions: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Prisma runtime facade that owns lifecycle hooks and transaction context access.
|
|
8
|
+
*
|
|
9
|
+
* @typeParam TClient Root Prisma client shape registered in the module.
|
|
10
|
+
* @typeParam TTransactionClient Transaction-scoped client resolved inside `$transaction(...)` callbacks.
|
|
11
|
+
* @typeParam TTransactionOptions Options forwarded to Prisma interactive transactions.
|
|
12
|
+
*/
|
|
13
|
+
export declare class PrismaService<TClient extends PrismaClientLike<TTransactionClient, TTransactionOptions>, TTransactionClient = InferPrismaTransactionClient<TClient>, TTransactionOptions = InferPrismaTransactionOptions<TClient>> implements PrismaHandleProvider<TClient, TTransactionClient, TTransactionOptions>, OnModuleInit, OnApplicationShutdown {
|
|
14
|
+
private readonly client;
|
|
15
|
+
private readonly serviceOptions;
|
|
16
|
+
private readonly transactions;
|
|
17
|
+
private readonly activeRequestTransactions;
|
|
18
|
+
private transactionAbortSignalSupport;
|
|
19
|
+
private lifecycleState;
|
|
20
|
+
constructor(client: TClient, serviceOptions?: PrismaServiceOptions);
|
|
21
|
+
/**
|
|
22
|
+
* Returns the active Prisma handle for the current async context.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const user = await prisma.current().user.findUnique({ where: { id } });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @returns The request/transaction-scoped client when a transaction is active; otherwise the root client.
|
|
30
|
+
*/
|
|
31
|
+
current(): TClient | TTransactionClient;
|
|
32
|
+
private runWithTransactionClient;
|
|
33
|
+
onModuleInit(): Promise<void>;
|
|
34
|
+
onApplicationShutdown(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Creates a shared platform-status snapshot for runtime/CLI/Studio health surfaces.
|
|
37
|
+
*
|
|
38
|
+
* @returns Platform snapshot data reflecting lifecycle state and transaction capability diagnostics.
|
|
39
|
+
*/
|
|
40
|
+
createPlatformStatusSnapshot(): import("@fluojs/runtime").PersistencePlatformStatusSnapshot;
|
|
41
|
+
/**
|
|
42
|
+
* Opens a Prisma interactive transaction boundary and executes the callback in that context.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* await prisma.transaction(async () => {
|
|
47
|
+
* await prisma.current().user.create({ data });
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @param fn Callback executed inside the transaction flow where `current()` resolves from ALS to the active transaction client,
|
|
52
|
+
* or reuses the already-active context / direct-execution path when no new boundary is opened.
|
|
53
|
+
* @param options Optional Prisma transaction options forwarded to `$transaction`.
|
|
54
|
+
* @returns The callback result, after commit when a new interactive transaction is opened, or from direct execution when
|
|
55
|
+
* nested context reuse or non-strict `$transaction` fallback applies.
|
|
56
|
+
* @throws {Error} When nested transaction options are provided while already inside an active transaction.
|
|
57
|
+
* @throws {Error} When strict transaction mode is enabled and the Prisma client does not implement `$transaction`.
|
|
58
|
+
*/
|
|
59
|
+
transaction<T>(fn: () => Promise<T>, options?: TTransactionOptions): Promise<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Opens an abort-aware request transaction boundary.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* await prisma.requestTransaction(async () => next.handle(), request.signal);
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @param fn Callback executed inside the request-scoped transaction flow where `current()` resolves from ALS to the active
|
|
69
|
+
* transaction client, or reuses the already-active context / direct-execution path when no new boundary is opened.
|
|
70
|
+
* @param signal Optional abort signal propagated to request transaction handling.
|
|
71
|
+
* @param options Optional Prisma transaction options forwarded to `$transaction`.
|
|
72
|
+
* @returns The callback result, after commit when a new interactive transaction is opened, or from direct execution when
|
|
73
|
+
* nested context reuse or non-strict `$transaction` fallback applies.
|
|
74
|
+
* @throws {Error} When nested transaction options are provided while already inside an active transaction.
|
|
75
|
+
* @throws {Error} When strict transaction mode is enabled and the Prisma client does not implement `$transaction`.
|
|
76
|
+
* @throws {Error} Propagates an abort-related error when `signal` aborts before the transaction callback settles; concrete
|
|
77
|
+
* error type/message depends on the runtime abort implementation.
|
|
78
|
+
*/
|
|
79
|
+
requestTransaction<T>(fn: () => Promise<T>, signal?: AbortSignal, options?: TTransactionOptions): Promise<T>;
|
|
80
|
+
private runRequestTransactionWithAbortSignal;
|
|
81
|
+
private canAttemptTransactionAbortSignalOption;
|
|
82
|
+
private runTransactionWithAbortSignalFallback;
|
|
83
|
+
private shouldRetryWithoutAbortSignal;
|
|
84
|
+
private toErrorMessage;
|
|
85
|
+
private withTransactionAbortSignal;
|
|
86
|
+
private trackActiveRequestTransaction;
|
|
87
|
+
private untrackActiveRequestTransaction;
|
|
88
|
+
}
|
|
89
|
+
export {};
|
|
90
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAK3E,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAKpB,UAAU,oBAAoB;IAC5B,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAcD;;;;;;GAMG;AACH,qBACa,aAAa,CACxB,OAAO,SAAS,gBAAgB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,EACzE,kBAAkB,GAAG,4BAA4B,CAAC,OAAO,CAAC,EAC1D,mBAAmB,GAAG,6BAA6B,CAAC,OAAO,CAAC,CAE5D,YAAW,oBAAoB,CAAC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,EAAE,YAAY,EAAE,qBAAqB;IAQpH,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAPjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA+C;IAC5E,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAAuC;IACjF,OAAO,CAAC,6BAA6B,CAA4C;IACjF,OAAO,CAAC,cAAc,CAAgE;gBAGnE,MAAM,EAAE,OAAO,EACf,cAAc,GAAE,oBAAoD;IAGvF;;;;;;;;;OASG;IACH,OAAO,IAAI,OAAO,GAAG,kBAAkB;YAIzB,wBAAwB;IA2BhC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5C;;;;OAIG;IACH,4BAA4B;IAY5B;;;;;;;;;;;;;;;;;OAiBG;IACG,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;IAQrF;;;;;;;;;;;;;;;;;;OAkBG;IACG,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;IAkBlH,OAAO,CAAC,oCAAoC;IAY5C,OAAO,CAAC,sCAAsC;YAYhC,qCAAqC;IAyBnD,OAAO,CAAC,6BAA6B;IAUrC,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,0BAA0B;IAWlC,OAAO,CAAC,6BAA6B;IAIrC,OAAO,CAAC,+BAA+B;CAGxC"}
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
let _initClass;
|
|
2
|
+
function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
|
|
3
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
4
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
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
|
+
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
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
8
|
+
import { createRequestAbortContext, raceWithAbort, trackActiveRequestTransaction, untrackActiveRequestTransaction } from '@fluojs/runtime';
|
|
9
|
+
import { Inject } from '@fluojs/core';
|
|
10
|
+
import { createPrismaPlatformStatusSnapshot } from './status.js';
|
|
11
|
+
import { PRISMA_CLIENT, PRISMA_OPTIONS } from './tokens.js';
|
|
12
|
+
const NESTED_TRANSACTION_OPTIONS_NOT_SUPPORTED_ERROR = 'Nested Prisma transaction options are not supported because the active transaction context is reused.';
|
|
13
|
+
let _PrismaService;
|
|
14
|
+
/**
|
|
15
|
+
* Prisma runtime facade that owns lifecycle hooks and transaction context access.
|
|
16
|
+
*
|
|
17
|
+
* @typeParam TClient Root Prisma client shape registered in the module.
|
|
18
|
+
* @typeParam TTransactionClient Transaction-scoped client resolved inside `$transaction(...)` callbacks.
|
|
19
|
+
* @typeParam TTransactionOptions Options forwarded to Prisma interactive transactions.
|
|
20
|
+
*/
|
|
21
|
+
class PrismaService {
|
|
22
|
+
static {
|
|
23
|
+
[_PrismaService, _initClass] = _applyDecs(this, [Inject(PRISMA_CLIENT, PRISMA_OPTIONS)], []).c;
|
|
24
|
+
}
|
|
25
|
+
transactions = new AsyncLocalStorage();
|
|
26
|
+
activeRequestTransactions = new Set();
|
|
27
|
+
transactionAbortSignalSupport = 'unknown';
|
|
28
|
+
lifecycleState = 'created';
|
|
29
|
+
constructor(client, serviceOptions = {
|
|
30
|
+
strictTransactions: false
|
|
31
|
+
}) {
|
|
32
|
+
this.client = client;
|
|
33
|
+
this.serviceOptions = serviceOptions;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Returns the active Prisma handle for the current async context.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* const user = await prisma.current().user.findUnique({ where: { id } });
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @returns The request/transaction-scoped client when a transaction is active; otherwise the root client.
|
|
45
|
+
*/
|
|
46
|
+
current() {
|
|
47
|
+
return this.transactions.getStore() ?? this.client;
|
|
48
|
+
}
|
|
49
|
+
async runWithTransactionClient(fn, run, options) {
|
|
50
|
+
if (this.transactions.getStore()) {
|
|
51
|
+
if (options !== undefined) {
|
|
52
|
+
throw new Error(NESTED_TRANSACTION_OPTIONS_NOT_SUPPORTED_ERROR);
|
|
53
|
+
}
|
|
54
|
+
return fn();
|
|
55
|
+
}
|
|
56
|
+
if (typeof this.client.$transaction !== 'function') {
|
|
57
|
+
if (this.serviceOptions.strictTransactions) {
|
|
58
|
+
throw new Error('Transaction not supported: Prisma client does not implement $transaction.');
|
|
59
|
+
}
|
|
60
|
+
return fn();
|
|
61
|
+
}
|
|
62
|
+
return run(transactionClient => this.transactions.run(transactionClient, fn), options);
|
|
63
|
+
}
|
|
64
|
+
async onModuleInit() {
|
|
65
|
+
if (typeof this.client.$connect === 'function') {
|
|
66
|
+
await this.client.$connect();
|
|
67
|
+
}
|
|
68
|
+
this.lifecycleState = 'ready';
|
|
69
|
+
}
|
|
70
|
+
async onApplicationShutdown() {
|
|
71
|
+
this.lifecycleState = 'shutting-down';
|
|
72
|
+
for (const transaction of this.activeRequestTransactions) {
|
|
73
|
+
transaction.abort(new Error('Application shutdown interrupted an open request transaction.'));
|
|
74
|
+
}
|
|
75
|
+
await Promise.allSettled(Array.from(this.activeRequestTransactions, transaction => transaction.settled));
|
|
76
|
+
if (typeof this.client.$disconnect === 'function') {
|
|
77
|
+
await this.client.$disconnect();
|
|
78
|
+
}
|
|
79
|
+
this.lifecycleState = 'stopped';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Creates a shared platform-status snapshot for runtime/CLI/Studio health surfaces.
|
|
84
|
+
*
|
|
85
|
+
* @returns Platform snapshot data reflecting lifecycle state and transaction capability diagnostics.
|
|
86
|
+
*/
|
|
87
|
+
createPlatformStatusSnapshot() {
|
|
88
|
+
return createPrismaPlatformStatusSnapshot({
|
|
89
|
+
activeRequestTransactions: this.activeRequestTransactions.size,
|
|
90
|
+
lifecycleState: this.lifecycleState,
|
|
91
|
+
strictTransactions: this.serviceOptions.strictTransactions,
|
|
92
|
+
supportsConnect: typeof this.client.$connect === 'function',
|
|
93
|
+
supportsDisconnect: typeof this.client.$disconnect === 'function',
|
|
94
|
+
supportsTransaction: typeof this.client.$transaction === 'function',
|
|
95
|
+
transactionAbortSignalSupport: this.transactionAbortSignalSupport
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Opens a Prisma interactive transaction boundary and executes the callback in that context.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* await prisma.transaction(async () => {
|
|
105
|
+
* await prisma.current().user.create({ data });
|
|
106
|
+
* });
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* @param fn Callback executed inside the transaction flow where `current()` resolves from ALS to the active transaction client,
|
|
110
|
+
* or reuses the already-active context / direct-execution path when no new boundary is opened.
|
|
111
|
+
* @param options Optional Prisma transaction options forwarded to `$transaction`.
|
|
112
|
+
* @returns The callback result, after commit when a new interactive transaction is opened, or from direct execution when
|
|
113
|
+
* nested context reuse or non-strict `$transaction` fallback applies.
|
|
114
|
+
* @throws {Error} When nested transaction options are provided while already inside an active transaction.
|
|
115
|
+
* @throws {Error} When strict transaction mode is enabled and the Prisma client does not implement `$transaction`.
|
|
116
|
+
*/
|
|
117
|
+
async transaction(fn, options) {
|
|
118
|
+
return this.runWithTransactionClient(fn, (callback, transactionOptions) => this.client.$transaction(callback, transactionOptions), options);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Opens an abort-aware request transaction boundary.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* await prisma.requestTransaction(async () => next.handle(), request.signal);
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* @param fn Callback executed inside the request-scoped transaction flow where `current()` resolves from ALS to the active
|
|
130
|
+
* transaction client, or reuses the already-active context / direct-execution path when no new boundary is opened.
|
|
131
|
+
* @param signal Optional abort signal propagated to request transaction handling.
|
|
132
|
+
* @param options Optional Prisma transaction options forwarded to `$transaction`.
|
|
133
|
+
* @returns The callback result, after commit when a new interactive transaction is opened, or from direct execution when
|
|
134
|
+
* nested context reuse or non-strict `$transaction` fallback applies.
|
|
135
|
+
* @throws {Error} When nested transaction options are provided while already inside an active transaction.
|
|
136
|
+
* @throws {Error} When strict transaction mode is enabled and the Prisma client does not implement `$transaction`.
|
|
137
|
+
* @throws {Error} Propagates an abort-related error when `signal` aborts before the transaction callback settles; concrete
|
|
138
|
+
* error type/message depends on the runtime abort implementation.
|
|
139
|
+
*/
|
|
140
|
+
async requestTransaction(fn, signal, options) {
|
|
141
|
+
const abortContext = createRequestAbortContext(signal);
|
|
142
|
+
const active = this.trackActiveRequestTransaction(abortContext.controller);
|
|
143
|
+
const transactionPromise = this.runWithTransactionClient(() => raceWithAbort(fn, abortContext.signal), (callback, transactionOptions) => this.runRequestTransactionWithAbortSignal(callback, abortContext.signal, transactionOptions), options);
|
|
144
|
+
try {
|
|
145
|
+
return await transactionPromise;
|
|
146
|
+
} finally {
|
|
147
|
+
abortContext.cleanup();
|
|
148
|
+
this.untrackActiveRequestTransaction(active);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
runRequestTransactionWithAbortSignal(callback, signal, options) {
|
|
152
|
+
if (!this.canAttemptTransactionAbortSignalOption(options)) {
|
|
153
|
+
return this.client.$transaction(callback, options);
|
|
154
|
+
}
|
|
155
|
+
return this.runTransactionWithAbortSignalFallback(callback, signal, options);
|
|
156
|
+
}
|
|
157
|
+
canAttemptTransactionAbortSignalOption(options) {
|
|
158
|
+
if (options !== undefined && (typeof options !== 'object' || options === null)) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
if (this.transactionAbortSignalSupport === 'unsupported') {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
async runTransactionWithAbortSignalFallback(callback, signal, options) {
|
|
167
|
+
let callbackInvoked = false;
|
|
168
|
+
const wrappedCallback = transactionClient => {
|
|
169
|
+
callbackInvoked = true;
|
|
170
|
+
return callback(transactionClient);
|
|
171
|
+
};
|
|
172
|
+
try {
|
|
173
|
+
const result = await this.client.$transaction(wrappedCallback, this.withTransactionAbortSignal(options, signal));
|
|
174
|
+
this.transactionAbortSignalSupport = 'supported';
|
|
175
|
+
return result;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
if (callbackInvoked || !this.shouldRetryWithoutAbortSignal(error)) {
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
this.transactionAbortSignalSupport = 'unsupported';
|
|
181
|
+
return this.client.$transaction(callback, options);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
shouldRetryWithoutAbortSignal(error) {
|
|
185
|
+
if (this.transactionAbortSignalSupport === 'supported') {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
const message = this.toErrorMessage(error);
|
|
189
|
+
return /signal/i.test(message) && /(argument|field|option|unknown|invalid|unexpected|unsupported|not support)/i.test(message);
|
|
190
|
+
}
|
|
191
|
+
toErrorMessage(error) {
|
|
192
|
+
if (error instanceof Error) {
|
|
193
|
+
return error.message;
|
|
194
|
+
}
|
|
195
|
+
return String(error);
|
|
196
|
+
}
|
|
197
|
+
withTransactionAbortSignal(options, signal) {
|
|
198
|
+
if (options === undefined) {
|
|
199
|
+
return {
|
|
200
|
+
signal
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
...options,
|
|
205
|
+
signal
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
trackActiveRequestTransaction(controller) {
|
|
209
|
+
return trackActiveRequestTransaction(this.activeRequestTransactions, controller);
|
|
210
|
+
}
|
|
211
|
+
untrackActiveRequestTransaction(handle) {
|
|
212
|
+
untrackActiveRequestTransaction(this.activeRequestTransactions, handle);
|
|
213
|
+
}
|
|
214
|
+
static {
|
|
215
|
+
_initClass();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
export { _PrismaService as PrismaService };
|
package/dist/status.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PersistencePlatformStatusSnapshot } from '@fluojs/runtime';
|
|
2
|
+
type PrismaPlatformLifecycleState = 'created' | 'ready' | 'shutting-down' | 'stopped';
|
|
3
|
+
type PrismaPlatformStatusSnapshotInput = {
|
|
4
|
+
activeRequestTransactions: number;
|
|
5
|
+
lifecycleState: PrismaPlatformLifecycleState;
|
|
6
|
+
strictTransactions: boolean;
|
|
7
|
+
supportsConnect: boolean;
|
|
8
|
+
supportsDisconnect: boolean;
|
|
9
|
+
supportsTransaction: boolean;
|
|
10
|
+
transactionAbortSignalSupport: 'unknown' | 'supported' | 'unsupported';
|
|
11
|
+
};
|
|
12
|
+
export declare function createPrismaPlatformStatusSnapshot(input: PrismaPlatformStatusSnapshotInput): PersistencePlatformStatusSnapshot;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iCAAiC,EAGlC,MAAM,iBAAiB,CAAC;AAEzB,KAAK,4BAA4B,GAAG,SAAS,GAAG,OAAO,GAAG,eAAe,GAAG,SAAS,CAAC;AAEtF,KAAK,iCAAiC,GAAG;IACvC,yBAAyB,EAAE,MAAM,CAAC;IAClC,cAAc,EAAE,4BAA4B,CAAC;IAC7C,kBAAkB,EAAE,OAAO,CAAC;IAC5B,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,6BAA6B,EAAE,SAAS,GAAG,WAAW,GAAG,aAAa,CAAC;CACxE,CAAC;AAqDF,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,iCAAiC,GACvC,iCAAiC,CAmBnC"}
|
package/dist/status.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
function createReadiness(input) {
|
|
2
|
+
if (input.lifecycleState === 'shutting-down') {
|
|
3
|
+
return {
|
|
4
|
+
critical: true,
|
|
5
|
+
reason: 'Prisma integration is shutting down.',
|
|
6
|
+
status: 'not-ready'
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
if (input.lifecycleState === 'stopped') {
|
|
10
|
+
return {
|
|
11
|
+
critical: true,
|
|
12
|
+
reason: 'Prisma integration is stopped.',
|
|
13
|
+
status: 'not-ready'
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
if (input.strictTransactions && !input.supportsTransaction) {
|
|
17
|
+
return {
|
|
18
|
+
critical: true,
|
|
19
|
+
reason: 'Prisma strictTransactions is enabled but client.$transaction is unavailable.',
|
|
20
|
+
status: 'not-ready'
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
critical: true,
|
|
25
|
+
status: 'ready'
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function createHealth(input) {
|
|
29
|
+
if (input.lifecycleState === 'stopped') {
|
|
30
|
+
return {
|
|
31
|
+
reason: 'Prisma integration has been disconnected.',
|
|
32
|
+
status: 'unhealthy'
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (input.lifecycleState === 'shutting-down') {
|
|
36
|
+
return {
|
|
37
|
+
reason: 'Prisma integration is draining request transactions during shutdown.',
|
|
38
|
+
status: 'degraded'
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
status: 'healthy'
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function createPrismaPlatformStatusSnapshot(input) {
|
|
46
|
+
return {
|
|
47
|
+
details: {
|
|
48
|
+
activeRequestTransactions: input.activeRequestTransactions,
|
|
49
|
+
lifecycleState: input.lifecycleState,
|
|
50
|
+
strictTransactions: input.strictTransactions,
|
|
51
|
+
supportsConnect: input.supportsConnect,
|
|
52
|
+
supportsDisconnect: input.supportsDisconnect,
|
|
53
|
+
supportsTransaction: input.supportsTransaction,
|
|
54
|
+
transactionAbortSignalSupport: input.transactionAbortSignalSupport,
|
|
55
|
+
transactionContext: 'als'
|
|
56
|
+
},
|
|
57
|
+
health: createHealth(input),
|
|
58
|
+
ownership: {
|
|
59
|
+
externallyManaged: true,
|
|
60
|
+
ownsResources: false
|
|
61
|
+
},
|
|
62
|
+
readiness: createReadiness(input)
|
|
63
|
+
};
|
|
64
|
+
}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Dependency-injection token for the raw Prisma client handle. */
|
|
2
|
+
export declare const PRISMA_CLIENT: unique symbol;
|
|
3
|
+
/** Dependency-injection token for normalized Prisma runtime options. */
|
|
4
|
+
export declare const PRISMA_OPTIONS: unique symbol;
|
|
5
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,eAAO,MAAM,aAAa,eAAmC,CAAC;AAC9D,wEAAwE;AACxE,eAAO,MAAM,cAAc,eAAoC,CAAC"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Dependency-injection token for the raw Prisma client handle. */
|
|
2
|
+
export const PRISMA_CLIENT = Symbol.for('fluo.prisma.client');
|
|
3
|
+
/** Dependency-injection token for normalized Prisma runtime options. */
|
|
4
|
+
export const PRISMA_OPTIONS = Symbol.for('fluo.prisma.options');
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Interceptor, InterceptorContext } from '@fluojs/http';
|
|
2
|
+
import { PrismaService } from './service.js';
|
|
3
|
+
import type { PrismaClientLike } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* HTTP interceptor that wraps a request handler in `PrismaService.requestTransaction(...)`.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Pair this with repository/service code that reads `PrismaService.current()` so downstream calls share the same
|
|
9
|
+
* request-scoped transaction client.
|
|
10
|
+
*/
|
|
11
|
+
export declare class PrismaTransactionInterceptor implements Interceptor {
|
|
12
|
+
private readonly prisma;
|
|
13
|
+
constructor(prisma: PrismaService<PrismaClientLike>);
|
|
14
|
+
/**
|
|
15
|
+
* Runs the downstream handler inside a Prisma request transaction boundary.
|
|
16
|
+
*
|
|
17
|
+
* @param context Interceptor context that supplies the request abort signal.
|
|
18
|
+
* @param next Downstream handler chain.
|
|
19
|
+
* @returns The downstream handler result after the request transaction settles.
|
|
20
|
+
*/
|
|
21
|
+
intercept(context: InterceptorContext, next: {
|
|
22
|
+
handle(): Promise<unknown>;
|
|
23
|
+
}): Promise<unknown>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD;;;;;;GAMG;AACH,qBACa,4BAA6B,YAAW,WAAW;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa,CAAC,gBAAgB,CAAC;IAEpE;;;;;;OAMG;IACG,SAAS,CAAC,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAE;QAAE,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAGrG"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
let _initClass;
|
|
2
|
+
function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; }
|
|
3
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
4
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
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
|
+
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
|
+
import { Inject } from '@fluojs/core';
|
|
8
|
+
import { PrismaService } from './service.js';
|
|
9
|
+
let _PrismaTransactionInt;
|
|
10
|
+
/**
|
|
11
|
+
* HTTP interceptor that wraps a request handler in `PrismaService.requestTransaction(...)`.
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* Pair this with repository/service code that reads `PrismaService.current()` so downstream calls share the same
|
|
15
|
+
* request-scoped transaction client.
|
|
16
|
+
*/
|
|
17
|
+
class PrismaTransactionInterceptor {
|
|
18
|
+
static {
|
|
19
|
+
[_PrismaTransactionInt, _initClass] = _applyDecs(this, [Inject(PrismaService)], []).c;
|
|
20
|
+
}
|
|
21
|
+
constructor(prisma) {
|
|
22
|
+
this.prisma = prisma;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Runs the downstream handler inside a Prisma request transaction boundary.
|
|
27
|
+
*
|
|
28
|
+
* @param context Interceptor context that supplies the request abort signal.
|
|
29
|
+
* @param next Downstream handler chain.
|
|
30
|
+
* @returns The downstream handler result after the request transaction settles.
|
|
31
|
+
*/
|
|
32
|
+
async intercept(context, next) {
|
|
33
|
+
return this.prisma.requestTransaction(async () => next.handle(), context.requestContext.request.signal);
|
|
34
|
+
}
|
|
35
|
+
static {
|
|
36
|
+
_initClass();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export { _PrismaTransactionInt as PrismaTransactionInterceptor };
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { MaybePromise } from '@fluojs/core';
|
|
2
|
+
type PrismaTransactionCallback<TTransactionClient, TResult> = (client: TTransactionClient) => Promise<TResult>;
|
|
3
|
+
/**
|
|
4
|
+
* Infers the transaction-scoped Prisma client type exposed inside `$transaction(...)` callbacks.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam TClient Root Prisma client shape registered with Fluo.
|
|
7
|
+
*/
|
|
8
|
+
export type InferPrismaTransactionClient<TClient> = TClient extends {
|
|
9
|
+
$transaction?: <T>(callback: PrismaTransactionCallback<infer TTransactionClient, T>, options?: infer _TTransactionOptions) => Promise<T>;
|
|
10
|
+
} ? TTransactionClient : TClient;
|
|
11
|
+
/**
|
|
12
|
+
* Infers the optional Prisma transaction options object accepted by the registered client.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam TClient Root Prisma client shape registered with Fluo.
|
|
15
|
+
*/
|
|
16
|
+
export type InferPrismaTransactionOptions<TClient> = TClient extends {
|
|
17
|
+
$transaction?: <T>(callback: PrismaTransactionCallback<infer _TTransactionClient, T>, options?: infer TTransactionOptions) => Promise<T>;
|
|
18
|
+
} ? TTransactionOptions : unknown;
|
|
19
|
+
/**
|
|
20
|
+
* Alias for the transaction-scoped Prisma client type derived from a root client.
|
|
21
|
+
*
|
|
22
|
+
* @typeParam TClient Root Prisma client shape registered with Fluo.
|
|
23
|
+
*/
|
|
24
|
+
export type PrismaTransactionClient<TClient> = InferPrismaTransactionClient<TClient>;
|
|
25
|
+
/**
|
|
26
|
+
* Minimal Prisma client seam required by the Fluo runtime wrapper.
|
|
27
|
+
*
|
|
28
|
+
* @typeParam TTransactionClient Client shape passed into interactive transaction callbacks.
|
|
29
|
+
* @typeParam TTransactionOptions Options shape forwarded to `$transaction(...)`.
|
|
30
|
+
*/
|
|
31
|
+
export interface PrismaClientLike<TTransactionClient = unknown, TTransactionOptions = unknown> {
|
|
32
|
+
$connect?(): MaybePromise<void>;
|
|
33
|
+
$disconnect?(): MaybePromise<void>;
|
|
34
|
+
$transaction?<T>(callback: PrismaTransactionCallback<TTransactionClient, T>, options?: TTransactionOptions): Promise<T>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Configures how `PrismaModule` wires a Prisma client into the Fluo lifecycle.
|
|
38
|
+
*
|
|
39
|
+
* @typeParam TClient Root Prisma client shape registered in the module.
|
|
40
|
+
* @typeParam TTransactionClient Transaction-scoped client resolved by `current()` inside transaction boundaries.
|
|
41
|
+
* @typeParam TTransactionOptions Options forwarded to Prisma interactive transactions.
|
|
42
|
+
*/
|
|
43
|
+
export interface PrismaModuleOptions<TClient extends PrismaClientLike<TTransactionClient, TTransactionOptions>, TTransactionClient = InferPrismaTransactionClient<TClient>, TTransactionOptions = InferPrismaTransactionOptions<TClient>> {
|
|
44
|
+
/** Root Prisma client shared outside ambient transaction scopes. */
|
|
45
|
+
client: TClient;
|
|
46
|
+
/**
|
|
47
|
+
* Throws when transaction helpers are used against a client that does not implement `$transaction(...)`.
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* Leave this disabled when you want `transaction()` / `requestTransaction()` to fall back to direct execution.
|
|
51
|
+
*/
|
|
52
|
+
strictTransactions?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Public transaction-aware Prisma contract exposed through dependency injection.
|
|
56
|
+
*
|
|
57
|
+
* @typeParam TClient Root Prisma client shape registered in the module.
|
|
58
|
+
* @typeParam TTransactionClient Transaction-scoped client resolved inside transaction callbacks.
|
|
59
|
+
* @typeParam TTransactionOptions Options forwarded to Prisma interactive transactions.
|
|
60
|
+
*/
|
|
61
|
+
export interface PrismaHandleProvider<TClient extends PrismaClientLike<TTransactionClient, TTransactionOptions>, TTransactionClient = InferPrismaTransactionClient<TClient>, TTransactionOptions = InferPrismaTransactionOptions<TClient>> {
|
|
62
|
+
/** Returns the ambient transaction client when present, or the root Prisma client otherwise. */
|
|
63
|
+
current(): TClient | TTransactionClient;
|
|
64
|
+
/**
|
|
65
|
+
* Opens an abort-aware request transaction boundary around `fn`.
|
|
66
|
+
*
|
|
67
|
+
* @param fn Callback executed within the request transaction scope.
|
|
68
|
+
* @param signal Optional abort signal linked to the request lifecycle.
|
|
69
|
+
* @param options Optional Prisma transaction options forwarded to `$transaction(...)`.
|
|
70
|
+
* @returns The callback result after the request transaction finishes or the direct-execution fallback completes.
|
|
71
|
+
*/
|
|
72
|
+
requestTransaction<T>(fn: () => Promise<T>, signal?: AbortSignal, options?: TTransactionOptions): Promise<T>;
|
|
73
|
+
/**
|
|
74
|
+
* Opens an interactive transaction boundary around `fn`.
|
|
75
|
+
*
|
|
76
|
+
* @param fn Callback executed within the transaction scope.
|
|
77
|
+
* @param options Optional Prisma transaction options forwarded to `$transaction(...)`.
|
|
78
|
+
* @returns The callback result after the transaction finishes or the direct-execution fallback completes.
|
|
79
|
+
*/
|
|
80
|
+
transaction<T>(fn: () => Promise<T>, options?: TTransactionOptions): Promise<T>;
|
|
81
|
+
}
|
|
82
|
+
export {};
|
|
83
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,KAAK,yBAAyB,CAAC,kBAAkB,EAAE,OAAO,IAAI,CAAC,MAAM,EAAE,kBAAkB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE/G;;;;GAIG;AACH,MAAM,MAAM,4BAA4B,CAAC,OAAO,IAAI,OAAO,SAAS;IAClE,YAAY,CAAC,EAAE,CAAC,CAAC,EACf,QAAQ,EAAE,yBAAyB,CAAC,MAAM,kBAAkB,EAAE,CAAC,CAAC,EAChE,OAAO,CAAC,EAAE,MAAM,oBAAoB,KACjC,OAAO,CAAC,CAAC,CAAC,CAAC;CACjB,GACG,kBAAkB,GAClB,OAAO,CAAC;AAEZ;;;;GAIG;AACH,MAAM,MAAM,6BAA6B,CAAC,OAAO,IAAI,OAAO,SAAS;IACnE,YAAY,CAAC,EAAE,CAAC,CAAC,EACf,QAAQ,EAAE,yBAAyB,CAAC,MAAM,mBAAmB,EAAE,CAAC,CAAC,EACjE,OAAO,CAAC,EAAE,MAAM,mBAAmB,KAChC,OAAO,CAAC,CAAC,CAAC,CAAC;CACjB,GACG,mBAAmB,GACnB,OAAO,CAAC;AAEZ;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,CAAC,OAAO,IAAI,4BAA4B,CAAC,OAAO,CAAC,CAAC;AAErF;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB,CAAC,kBAAkB,GAAG,OAAO,EAAE,mBAAmB,GAAG,OAAO;IAC3F,QAAQ,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,WAAW,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;IACnC,YAAY,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,yBAAyB,CAAC,kBAAkB,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACzH;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB,CAClC,OAAO,SAAS,gBAAgB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,EACzE,kBAAkB,GAAG,4BAA4B,CAAC,OAAO,CAAC,EAC1D,mBAAmB,GAAG,6BAA6B,CAAC,OAAO,CAAC;IAE5D,oEAAoE;IACpE,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB,CACnC,OAAO,SAAS,gBAAgB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,EACzE,kBAAkB,GAAG,4BAA4B,CAAC,OAAO,CAAC,EAC1D,mBAAmB,GAAG,6BAA6B,CAAC,OAAO,CAAC;IAE5D,gGAAgG;IAChG,OAAO,IAAI,OAAO,GAAG,kBAAkB,CAAC;IACxC;;;;;;;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/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fluojs/prisma",
|
|
3
|
+
"description": "Prisma lifecycle and ALS-backed transaction context for Fluo, with async module factory and strict transaction mode.",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"fluo",
|
|
6
|
+
"prisma",
|
|
7
|
+
"orm",
|
|
8
|
+
"database",
|
|
9
|
+
"transaction",
|
|
10
|
+
"als"
|
|
11
|
+
],
|
|
12
|
+
"version": "1.0.0-beta.1",
|
|
13
|
+
"private": false,
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/fluojs/fluo.git",
|
|
18
|
+
"directory": "packages/prisma"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20.0.0"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@fluojs/core": "^1.0.0-beta.1",
|
|
40
|
+
"@fluojs/http": "^1.0.0-beta.1",
|
|
41
|
+
"@fluojs/runtime": "^1.0.0-beta.1",
|
|
42
|
+
"@fluojs/validation": "^1.0.0-beta.1",
|
|
43
|
+
"@fluojs/di": "^1.0.0-beta.1"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@prisma/client": ">=5.0.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"@prisma/client": {
|
|
50
|
+
"optional": true
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"vitest": "^3.2.4"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|
|
58
|
+
"build": "pnpm exec babel src --extensions .ts --ignore 'src/**/*.test.ts' --out-dir dist --config-file ../../tooling/babel/babel.config.cjs && pnpm exec tsc -p tsconfig.build.json",
|
|
59
|
+
"typecheck": "pnpm exec tsc -p tsconfig.json --noEmit",
|
|
60
|
+
"test": "pnpm exec vitest run -c vitest.config.ts",
|
|
61
|
+
"test:watch": "pnpm exec vitest -c vitest.config.ts"
|
|
62
|
+
}
|
|
63
|
+
}
|