@fluojs/prisma 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.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 20+ 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
 
@@ -10,10 +10,10 @@ Prisma lifecycle and ALS-backed transaction context for fluo applications. Conne
10
10
  - [When to Use](#when-to-use)
11
11
  - [Quick Start](#quick-start)
12
12
  - [Common Patterns](#common-patterns)
13
- - [PrismaService and current()](#prismaservice-and-current)
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
- - [Manual Transactions](#manual-transactions)
16
- - [Automatic Request Transactions](#automatic-request-transactions)
16
+ - [Manual Transactions and current()](#manual-transactions-and-current)
17
17
  - [Shutdown and Status Contracts](#shutdown-and-status-contracts)
18
18
  - [Async Configuration and Isolation](#async-configuration-and-isolation)
19
19
  - [Manual Module Composition](#manual-module-composition)
@@ -31,7 +31,7 @@ pnpm add @prisma/client
31
31
 
32
32
  ## When to Use
33
33
 
34
- - 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 20+ and you want it integrated with fluo's dependency injection and lifecycle hooks.
35
35
  - When you need a reliable way to share a transaction context across multiple services and repositories without passing a `tx` object everywhere.
36
36
  - When you want automatic `$connect` on startup and `$disconnect` on shutdown.
37
37
 
@@ -56,33 +56,101 @@ class AppModule {}
56
56
 
57
57
  ## Common Patterns
58
58
 
59
- ### PrismaService and current()
59
+ ### Service Transaction Boundary (@Transaction)
60
60
 
61
- 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.
61
+ The `@Transaction()` decorator is the recommended way to define transaction boundaries in your service layer. It ensures that all repository calls made within the decorated method share the same Prisma transaction.
62
62
 
63
63
  ```typescript
64
64
  import { Inject } from '@fluojs/core';
65
- import { PrismaService } from '@fluojs/prisma';
65
+ import { PrismaService, Transaction, type PrismaServiceFacade } from '@fluojs/prisma';
66
66
  import { PrismaClient } from '@prisma/client';
67
+ import { UserRepository } from './user.repository';
68
+
69
+ export class UserService {
70
+ constructor(private readonly repo: UserRepository) {}
71
+
72
+ @Transaction()
73
+ async onboardUser(dto: CreateUserDto) {
74
+ const user = await this.repo.create(dto);
75
+ await this.repo.initProfile(user.id);
76
+ return user;
77
+ }
78
+ }
67
79
 
68
80
  @Inject(PrismaService)
69
81
  export class UserRepository {
70
- constructor(private readonly prisma: PrismaService<PrismaClient>) {}
82
+ constructor(private readonly prisma: PrismaServiceFacade<PrismaClient>) {}
83
+
84
+ async create(data: any) {
85
+ // The facade type exposes standard PrismaClient delegates.
86
+ // When called inside @Transaction(), they automatically participate in the ambient transaction.
87
+ return this.prisma.user.create({ data });
88
+ }
71
89
 
72
- async findById(id: string) {
73
- // current() preserves your generated Prisma types and autocomplete
74
- return this.prisma.current().user.findUnique({ where: { id } });
90
+ async initProfile(userId: string) {
91
+ return this.prisma.profile.create({ data: { userId } });
75
92
  }
76
93
  }
77
94
  ```
78
95
 
96
+ Calls to `@Transaction()` methods are reentrant. If a decorated method calls another decorated method, they share the same underlying Prisma transaction.
97
+
98
+ ### Request Transaction Interceptor Compatibility
99
+
100
+ `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`.
101
+
102
+ ```typescript
103
+ import { Controller, Post, UseInterceptors } from '@fluojs/http';
104
+ import { PrismaTransactionInterceptor } from '@fluojs/prisma';
105
+
106
+ @Controller('/orders')
107
+ export class OrdersController {
108
+ @Post('/')
109
+ @UseInterceptors(PrismaTransactionInterceptor)
110
+ createOrder() {
111
+ return this.orders.create();
112
+ }
113
+ }
114
+ ```
115
+
116
+ 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.
117
+
118
+ When request-wide atomicity is genuinely required, make the boundary and cancellation input visible in application code:
119
+
120
+ ```typescript
121
+ import { Inject } from '@fluojs/core';
122
+ import { Controller, Post, type RequestContext } from '@fluojs/http';
123
+ import { PrismaService } from '@fluojs/prisma';
124
+ import { PrismaClient } from '@prisma/client';
125
+
126
+ @Inject(PrismaService, OrdersService)
127
+ @Controller('/orders')
128
+ export class OrdersController {
129
+ constructor(
130
+ private readonly prisma: PrismaService<PrismaClient>,
131
+ private readonly orders: OrdersService,
132
+ ) {}
133
+
134
+ @Post('/checkout')
135
+ checkout(input: CheckoutInput, context: RequestContext) {
136
+ const { request } = context;
137
+ return this.prisma.requestTransaction(
138
+ () => this.orders.checkout(input),
139
+ request.signal,
140
+ );
141
+ }
142
+ }
143
+ ```
144
+
145
+ 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.
146
+
79
147
  ### Named Registrations for Multiple Clients
80
148
 
81
- 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)`.
149
+ 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.
82
150
 
83
151
  ```typescript
84
152
  import { Inject } from '@fluojs/core';
85
- import { PrismaModule, PrismaService, getPrismaServiceToken } from '@fluojs/prisma';
153
+ import { PrismaModule, PrismaService, getPrismaServiceToken, Transaction, type PrismaServiceFacade } from '@fluojs/prisma';
86
154
 
87
155
  const usersPrismaModule = PrismaModule.forRoot({ name: 'users', client: usersPrisma });
88
156
  const analyticsPrismaModule = PrismaModule.forRoot({ name: 'analytics', client: analyticsPrisma });
@@ -90,64 +158,69 @@ const analyticsPrismaModule = PrismaModule.forRoot({ name: 'analytics', client:
90
158
  @Inject(getPrismaServiceToken('users'), getPrismaServiceToken('analytics'))
91
159
  export class MultiDatabaseService {
92
160
  constructor(
93
- private readonly users: PrismaService<typeof usersPrisma>,
94
- private readonly analytics: PrismaService<typeof analyticsPrisma>,
161
+ private readonly users: PrismaServiceFacade<typeof usersPrisma>,
162
+ private readonly analytics: PrismaServiceFacade<typeof analyticsPrisma>,
95
163
  ) {}
96
164
 
97
- async loadDashboard(userId: string) {
98
- const user = await this.users.current().user.findUnique({ where: { id: userId } });
99
- const summary = await this.analytics.current().report.findMany();
100
- return { summary, user };
165
+ @Transaction((self) => self.users)
166
+ async updateAndLog(userId: string, data: any) {
167
+ const user = await this.users.user.update({ where: { id: userId }, data });
168
+ // This call is outside the 'users' transaction unless 'analytics' also opens one
169
+ await this.analytics.report.create({ data: { event: 'update', userId } });
170
+ return user;
101
171
  }
102
172
  }
103
173
  ```
104
174
 
105
- Unnamed registration remains the default single-client path for `PrismaService`, `PRISMA_CLIENT`, `PRISMA_OPTIONS`, and `PrismaTransactionInterceptor`. When you register multiple Prisma clients in the same container, use names for every additional client so token resolution stays explicit.
106
175
 
107
- ### Manual Transactions
176
+ ### Manual Transactions and current()
108
177
 
109
- Use `prisma.transaction()` to create an interactive transaction block. Any calls to `current()` inside the block will use the transaction-scoped client.
178
+ The `PrismaService` provides a `current()` method that returns the active transaction client if inside a transaction scope, or the root client otherwise. Use this as an escape hatch when you need to pass the client to external libraries or perform advanced manual transaction plumbing.
110
179
 
111
180
  ```typescript
112
- await this.prisma.transaction(async () => {
113
- const user = await this.prisma.current().user.create({ data });
114
- await this.prisma.current().profile.create({ data: { userId: user.id } });
115
- });
116
- ```
181
+ import { Inject } from '@fluojs/core';
182
+ import { PrismaService } from '@fluojs/prisma';
183
+ import { PrismaClient } from '@prisma/client';
117
184
 
118
- When `transaction()` is called while a transaction context is already active, `PrismaService` reuses the active transaction client instead of opening a nested Prisma transaction. Nested calls must not pass transaction options such as isolation levels; providing options in an active context is rejected so the package does not silently drop caller intent while reusing the ambient transaction.
185
+ @Inject(PrismaService)
186
+ export class AdvancedRepository {
187
+ constructor(private readonly prisma: PrismaService<PrismaClient>) {}
119
188
 
120
- ### Automatic Request Transactions
189
+ async customOperation() {
190
+ const tx = this.prisma.current();
191
+ // Use tx for operations that fluo doesn't automatically wrap,
192
+ // or when passing to an external utility that expects a PrismaClient.
193
+ return tx.user.findMany();
194
+ }
195
+ }
196
+ ```
121
197
 
122
- Apply the `PrismaTransactionInterceptor` to a controller or method to wrap the entire request in a transaction automatically.
198
+ Use `prisma.transaction()` for manual interactive transaction blocks:
123
199
 
124
200
  ```typescript
125
- import { Post, UseInterceptors } from '@fluojs/http';
126
- import { PrismaTransactionInterceptor } from '@fluojs/prisma';
127
-
128
- @UseInterceptors(PrismaTransactionInterceptor)
129
- class UserController {
130
- @Post()
131
- async create() {
132
- // All downstream repository calls via PrismaService.current() share this tx
133
- }
134
- }
201
+ await this.prisma.transaction(async () => {
202
+ const tx = this.prisma.current();
203
+ const user = await tx.user.create({ data });
204
+ await tx.profile.create({ data: { userId: user.id } });
205
+ });
135
206
  ```
136
207
 
137
- `PrismaTransactionInterceptor` targets the default unnamed `PrismaService`. For named multi-client registrations, inject the corresponding named `PrismaService` and open explicit `transaction()` / `requestTransaction()` boundaries where needed.
208
+ When `transaction()` is called while a transaction context is already active, `PrismaService` reuses the active transaction client instead of opening a nested Prisma transaction. Nested calls must not pass transaction options such as isolation levels; providing options in an active context is rejected so the package does not silently drop caller intent while reusing the ambient transaction.
138
209
 
139
210
  ### Shutdown and Status Contracts
140
211
 
141
- `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.
212
+ `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. 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.
142
213
 
143
214
  `createPrismaPlatformStatusSnapshot(...)` and `PrismaService.createPlatformStatusSnapshot()` expose the same lifecycle contract to diagnostics surfaces:
144
215
 
145
- - `readiness.status` is `not-ready` before `onModuleInit()` connects the client, while Prisma is shutting down or stopped, and when `strictTransactions` is enabled without `$transaction(...)` support.
216
+ - `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.
146
217
  - `health.status` is `degraded` while request transactions are draining during shutdown and `unhealthy` after disconnect.
147
218
  - `details.activeRequestTransactions`, `details.lifecycleState`, `details.strictTransactions`, `details.supportsTransaction`, and `details.transactionAbortSignalSupport` describe the current request transaction and transaction-capability state.
148
- - `details.transactionContext: 'als'` identifies the async-local transaction context used by request and service transaction boundaries.
219
+ - `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.
149
220
  - `ownership.externallyManaged: false` and `ownership.ownsResources: true` mean the package owns the registered client's `$connect()` / `$disconnect()` lifecycle hooks inside the fluo application lifecycle.
150
221
 
222
+ 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 20+ 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.
223
+
151
224
  ### Async Configuration and Isolation
152
225
 
153
226
  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.
@@ -167,7 +240,7 @@ PrismaModule.forRootAsync({
167
240
 
168
241
  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.
169
242
 
170
- Transaction boundaries require host-provided `AsyncLocalStorage` support. `@fluojs/prisma` resolves it 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, `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.
243
+ Transaction boundaries require host-provided `AsyncLocalStorage` support. The package manifest declares `engines.node >=20.0.0`, and the root wrapper is the documented Node.js 20+ 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.
171
244
 
172
245
  ### Manual Module Composition
173
246
 
@@ -175,7 +248,7 @@ Use `PrismaModule.forRoot(...)` / `forRootAsync(...)` to register Prisma. When y
175
248
 
176
249
  ```typescript
177
250
  import { defineModule } from '@fluojs/runtime';
178
- import { PrismaModule, PrismaService, PrismaTransactionInterceptor } from '@fluojs/prisma';
251
+ import { PrismaModule } from '@fluojs/prisma';
179
252
  import { PrismaClient } from '@prisma/client';
180
253
 
181
254
  const prisma = new PrismaClient();
@@ -183,7 +256,6 @@ const prisma = new PrismaClient();
183
256
  class ManualPrismaModule {}
184
257
 
185
258
  defineModule(ManualPrismaModule, {
186
- exports: [PrismaService, PrismaTransactionInterceptor],
187
259
  imports: [PrismaModule.forRoot({ client: prisma })],
188
260
  });
189
261
  ```
@@ -193,7 +265,7 @@ defineModule(ManualPrismaModule, {
193
265
  ### `PrismaModule`
194
266
 
195
267
  - `PrismaModule.forRoot(options)` / `PrismaModule.forRootAsync(options)`
196
- - `forRoot(...)` and `forRootAsync(...)` also accept `name` for named/scoped registrations.
268
+ - `forRoot(...)` and `forRootAsync(...)` also accept `name` for named/scoped registrations, and `global?: boolean` for unnamed registrations that should export their providers globally.
197
269
  - `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.
198
270
  - `forRootAsync(...)` resolves options once per application container, preserving client lifecycle and request transaction isolation across separate bootstraps.
199
271
  - Supports `strictTransactions: true` to throw if transaction support is missing.
@@ -206,13 +278,21 @@ defineModule(ManualPrismaModule, {
206
278
  - `current(): TClient | PrismaTransactionClient<TClient>`
207
279
  - Returns the ambient transaction client or the root client.
208
280
  - `transaction(fn, options?): Promise<T>`
209
- - 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.
281
+ - 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.
210
282
  - `requestTransaction(fn, signal?, options?): Promise<T>`
211
283
  - 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.
212
284
 
213
- ### `PrismaTransactionInterceptor`
285
+ Use `PrismaService<TClient>` when a provider only needs wrapper methods such as `current()`, `transaction(...)`, `requestTransaction(...)`, or `createPlatformStatusSnapshot()`. Use `PrismaServiceFacade<TClient>` for repository injections that call generated Prisma Client delegates directly; the facade forwards those calls to the active transaction client when one exists and to the root client otherwise. `PrismaService.createFacade(...)` is retained as a low-level compatibility helper for module-provider wiring; application code should prefer `PrismaModule.forRoot(...)` / `forRootAsync(...)`.
286
+
287
+ ### `Transaction`
288
+
289
+ - 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.
290
+
291
+ ### `PrismaTransactionInterceptor` (deprecated compatibility)
214
292
 
215
- - HTTP interceptor for the default unnamed `PrismaService` registration. It wraps a request handler in `PrismaService.requestTransaction(...)` so downstream `current()` calls share the same transaction client.
293
+ - Request-wide HTTP compatibility interceptor for existing 1.x imports.
294
+ - Delegates to `PrismaService.requestTransaction(...)` and forwards request cancellation.
295
+ - Prefer service `@Transaction()` or an explicit request boundary in new code.
216
296
 
217
297
  ### `PRISMA_CLIENT` (Token)
218
298
 
@@ -243,6 +323,7 @@ token are deliberately not exported.
243
323
  - `PrismaModuleOptions`
244
324
  - `PrismaClientLike`
245
325
  - `PrismaHandleProvider`
326
+ - `PrismaServiceFacade<TClient>`
246
327
  - `PrismaTransactionClient<TClient>`
247
328
  - `InferPrismaTransactionClient<TClient>`
248
329
  - `InferPrismaTransactionOptions<TClient>`
@@ -250,7 +331,7 @@ token are deliberately not exported.
250
331
  ## Related Packages
251
332
 
252
333
  - `@fluojs/runtime`: Manages the application lifecycle hooks.
253
- - `@fluojs/http`: Provides the interceptor system.
334
+ - `@fluojs/http`: Provides request lifecycle primitives that can be paired with explicit `requestTransaction(...)` boundaries.
254
335
  - `@fluojs/terminus`: Provides a health indicator for Prisma.
255
336
 
256
337
  ## Example Sources
package/dist/module.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type AsyncModuleOptions } from '@fluojs/core';
1
+ import type { AsyncModuleOptions } from '@fluojs/core';
2
2
  import { type ModuleType } from '@fluojs/runtime';
3
3
  import type { InferPrismaTransactionClient, InferPrismaTransactionOptions, PrismaClientLike, PrismaModuleOptions } from './types.js';
4
4
  type PrismaAsyncModuleOptions<TClient extends PrismaClientLike<TTransactionClient, TTransactionOptions>, TTransactionClient, TTransactionOptions> = AsyncModuleOptions<Omit<PrismaModuleOptions<TClient, TTransactionClient, TTransactionOptions>, 'global' | 'name'>> & {
@@ -13,7 +13,7 @@ export declare class PrismaModule {
13
13
  * Registers Prisma providers from static options.
14
14
  *
15
15
  * @param options Prisma module options with client handle and strict transaction mode.
16
- * @returns A module definition that exports `PrismaService` and `PrismaTransactionInterceptor`.
16
+ * @returns A module definition that exports `PrismaService`, compatibility interceptor, and related Prisma tokens.
17
17
  */
18
18
  static forRoot<TClient extends PrismaClientLike<TTransactionClient, TTransactionOptions>, TTransactionClient = InferPrismaTransactionClient<TClient>, TTransactionOptions = InferPrismaTransactionOptions<TClient>>(options: PrismaModuleOptions<TClient, TTransactionClient, TTransactionOptions>): ModuleType;
19
19
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,kBAAkB,EAAc,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAShE,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAapB,KAAK,wBAAwB,CAC3B,OAAO,SAAS,gBAAgB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,EACzE,kBAAkB,EAClB,mBAAmB,IACjB,kBAAkB,CAAC,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,GAAG;IACvH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AA0LF;;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,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,GAClF,UAAU;CAGd"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAS,MAAM,cAAc,CAAC;AAE9D,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAShE,OAAO,KAAK,EACV,4BAA4B,EAC5B,6BAA6B,EAC7B,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAapB,KAAK,wBAAwB,CAC3B,OAAO,SAAS,gBAAgB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,EACzE,kBAAkB,EAClB,mBAAmB,IACjB,kBAAkB,CAAC,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC,CAAC,GAAG;IACvH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AA8MF;;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,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,CAAC,GAClF,UAAU;CAGd"}
package/dist/module.js CHANGED
@@ -1,4 +1,3 @@
1
- import { Inject } from '@fluojs/core';
2
1
  import { defineModule } from '@fluojs/runtime';
3
2
  import { PrismaService } from './service.js';
4
3
  import { getPrismaClientToken, getPrismaOptionsToken, getPrismaServiceToken } from './tokens.js';
@@ -35,16 +34,12 @@ function normalizePrismaModuleOptions(options) {
35
34
  strictTransactions: options.strictTransactions ?? false
36
35
  };
37
36
  }
38
- function createNamedPrismaServiceProvider(name) {
39
- const clientToken = getPrismaClientToken(name);
40
- const optionsToken = getPrismaOptionsToken(name);
41
- const serviceToken = getPrismaServiceToken(name);
42
- class NamedPrismaService extends PrismaService {}
43
- Inject(clientToken, optionsToken)(NamedPrismaService, {});
44
- return [NamedPrismaService, {
45
- provide: serviceToken,
46
- useExisting: NamedPrismaService
47
- }];
37
+ function createPrismaServiceProvider(provide, clientToken, optionsToken) {
38
+ return {
39
+ inject: [clientToken, optionsToken],
40
+ provide,
41
+ useFactory: (client, serviceOptions) => PrismaService.createFacade(client, serviceOptions)
42
+ };
48
43
  }
49
44
  function createPrismaRuntimeProviders(normalizedOptionsProvider, name) {
50
45
  const normalizedOptionsToken = getPrismaNormalizedOptionsToken(name);
@@ -60,10 +55,10 @@ function createPrismaRuntimeProviders(normalizedOptionsProvider, name) {
60
55
  useFactory: options => ({
61
56
  strictTransactions: options.strictTransactions
62
57
  })
63
- }, ...(name === undefined ? [PrismaService, {
58
+ }, ...(name === undefined ? [createPrismaServiceProvider(PrismaService, clientToken, optionsToken), {
64
59
  provide: getPrismaServiceToken(),
65
60
  useExisting: PrismaService
66
- }, PrismaTransactionInterceptor] : createNamedPrismaServiceProvider(name))];
61
+ }, PrismaTransactionInterceptor] : [createPrismaServiceProvider(getPrismaServiceToken(name), clientToken, optionsToken)])];
67
62
  }
68
63
  function buildPrismaModule(options) {
69
64
  class PrismaRootModuleDefinition {}
@@ -115,7 +110,7 @@ export class PrismaModule {
115
110
  * Registers Prisma providers from static options.
116
111
  *
117
112
  * @param options Prisma module options with client handle and strict transaction mode.
118
- * @returns A module definition that exports `PrismaService` and `PrismaTransactionInterceptor`.
113
+ * @returns A module definition that exports `PrismaService`, compatibility interceptor, and related Prisma tokens.
119
114
  */
120
115
  static forRoot(options) {
121
116
  return buildPrismaModule(options);
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Marks an internal Prisma service or facade handle for default `@Transaction()` resolution.
3
+ *
4
+ * @internal
5
+ */
6
+ export declare function markPrismaServiceHandle<THandle extends object>(handle: THandle): THandle;
7
+ /**
8
+ * Checks whether a value is an internally marked Prisma service or facade handle.
9
+ *
10
+ * @internal
11
+ */
12
+ export declare function isPrismaServiceHandle(value: unknown): value is object;
13
+ //# sourceMappingURL=prisma-service-brand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma-service-brand.d.ts","sourceRoot":"","sources":["../src/prisma-service-brand.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,SAAS,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAIxF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAMrE"}
@@ -0,0 +1,23 @@
1
+ const brandedPrismaServiceHandles = new WeakSet();
2
+
3
+ /**
4
+ * Marks an internal Prisma service or facade handle for default `@Transaction()` resolution.
5
+ *
6
+ * @internal
7
+ */
8
+ export function markPrismaServiceHandle(handle) {
9
+ brandedPrismaServiceHandles.add(handle);
10
+ return handle;
11
+ }
12
+
13
+ /**
14
+ * Checks whether a value is an internally marked Prisma service or facade handle.
15
+ *
16
+ * @internal
17
+ */
18
+ export function isPrismaServiceHandle(value) {
19
+ if (typeof value !== 'object' && typeof value !== 'function' || value === null) {
20
+ return false;
21
+ }
22
+ return brandedPrismaServiceHandles.has(value);
23
+ }
package/dist/service.d.ts CHANGED
@@ -15,9 +15,24 @@ export declare class PrismaService<TClient extends PrismaClientLike<TTransaction
15
15
  private readonly serviceOptions;
16
16
  private readonly transactions;
17
17
  private readonly activeRequestTransactions;
18
+ private readonly activeTransactionBoundaries;
18
19
  private transactionAbortSignalSupport;
19
20
  private lifecycleState;
20
21
  constructor(client: TClient, serviceOptions?: PrismaServiceOptions);
22
+ /**
23
+ * Creates the low-level DI facade that forwards unknown Prisma API properties to the ambient `current()` client.
24
+ *
25
+ * @remarks
26
+ * This compatibility helper is used by `PrismaModule` provider wiring. Application code should prefer
27
+ * `PrismaModule.forRoot(...)` or `PrismaModule.forRootAsync(...)`, then type injected repository handles as
28
+ * `PrismaServiceFacade<TClient>` when direct generated Prisma delegates are needed.
29
+ *
30
+ * @param client Root Prisma client registered in the module.
31
+ * @param serviceOptions Runtime transaction options consumed by the Fluo wrapper.
32
+ * @returns A transaction-aware facade that exposes wrapper methods plus the root Prisma client surface.
33
+ */
34
+ static createFacade<TClient extends PrismaClientLike<TTransactionClient, TTransactionOptions>, TTransactionClient = InferPrismaTransactionClient<TClient>, TTransactionOptions = InferPrismaTransactionOptions<TClient>>(client: TClient, serviceOptions?: PrismaServiceOptions): PrismaServiceFacade<TClient, TTransactionClient, TTransactionOptions>;
35
+ private installCurrentClientFacade;
21
36
  /**
22
37
  * Returns the active Prisma handle for the current async context.
23
38
  *
@@ -80,6 +95,7 @@ export declare class PrismaService<TClient extends PrismaClientLike<TTransaction
80
95
  private runWithRequestTransactionClient;
81
96
  private runNestedRequestTransaction;
82
97
  private assertRequestTransactionsAvailable;
98
+ private assertTransactionBoundariesAvailable;
83
99
  private assertTransactionContextAvailable;
84
100
  private throwIfRequestAborted;
85
101
  private runRequestTransactionWithAbortSignal;
@@ -90,6 +106,21 @@ export declare class PrismaService<TClient extends PrismaClientLike<TTransaction
90
106
  private withTransactionAbortSignal;
91
107
  private trackActiveRequestTransaction;
92
108
  private untrackActiveRequestTransaction;
109
+ private trackActiveTransactionBoundary;
110
+ private untrackActiveTransactionBoundary;
93
111
  }
112
+ /**
113
+ * Injection-facing Prisma facade type that combines the Fluo wrapper methods with the registered Prisma client surface.
114
+ *
115
+ * @remarks
116
+ * `PrismaModule` resolves `PrismaService` to a facade that forwards unknown properties to `current()`. Use this type in
117
+ * repositories that call generated Prisma delegates directly, and use `PrismaService<TClient>` when only wrapper methods
118
+ * (`current()`, `transaction(...)`, `requestTransaction(...)`, and status snapshots) are needed.
119
+ *
120
+ * @typeParam TClient Root Prisma client shape registered in the module.
121
+ * @typeParam TTransactionClient Transaction-scoped client resolved inside `$transaction(...)` callbacks.
122
+ * @typeParam TTransactionOptions Options forwarded to Prisma interactive transactions.
123
+ */
124
+ export type PrismaServiceFacade<TClient extends PrismaClientLike<TTransactionClient, TTransactionOptions>, TTransactionClient = InferPrismaTransactionClient<TClient>, TTransactionOptions = InferPrismaTransactionOptions<TClient>> = PrismaService<TClient, TTransactionClient, TTransactionOptions> & Omit<TClient, keyof PrismaService<TClient, TTransactionClient, TTransactionOptions>>;
94
125
  export {};
95
126
  //# sourceMappingURL=service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAOA,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;AAQpB,UAAU,oBAAoB;IAC5B,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AA8FD;;;;;;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,CAAuD;IACpF,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;IAyChC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5C;;;;OAIG;IACH,4BAA4B;IAa5B;;;;;;;;;;;;;;;;;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;YAkCpG,+BAA+B;YAyB/B,2BAA2B;IAqCzC,OAAO,CAAC,kCAAkC;IAM1C,OAAO,CAAC,iCAAiC;IAMzC,OAAO,CAAC,qBAAqB;IAM7B,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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}