@fluojs/prisma 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ko.md CHANGED
@@ -142,7 +142,7 @@ class UserController {
142
142
 
143
143
  `createPrismaPlatformStatusSnapshot(...)`와 `PrismaService.createPlatformStatusSnapshot()`은 같은 라이프사이클 계약을 진단 surface에 노출합니다.
144
144
 
145
- - `readiness.status`는 Prisma가 종료 중이거나 stopped 상태일 때, 그리고 `strictTransactions`가 켜져 있는데 `$transaction(...)`을 지원하지 않을 때 `not-ready`입니다.
145
+ - `readiness.status`는 `onModuleInit()`이 클라이언트를 연결하기 전, Prisma가 종료 중이거나 stopped 상태일 때, 그리고 `strictTransactions`가 켜져 있는데 `$transaction(...)`을 지원하지 않을 때 `not-ready`입니다.
146
146
  - `health.status`는 종료 중 요청 트랜잭션을 drain하는 동안 `degraded`, disconnect 이후 `unhealthy`입니다.
147
147
  - `details.activeRequestTransactions`, `details.lifecycleState`, `details.strictTransactions`, `details.supportsTransaction`, `details.transactionAbortSignalSupport`는 현재 요청 트랜잭션과 트랜잭션 capability 상태를 설명합니다.
148
148
  - `details.transactionContext: 'als'`는 요청 및 서비스 트랜잭션 경계가 사용하는 async-local transaction context를 식별합니다.
@@ -167,6 +167,8 @@ PrismaModule.forRootAsync({
167
167
 
168
168
  하나의 컴파일된 애플리케이션 안에서는 하위 provider가 동일하게 resolve된 `PrismaService`, ALS 트랜잭션 컨텍스트, 라이프사이클 관리 대상 클라이언트를 공유합니다. 서로 다른 애플리케이션 컨테이너는 독립된 factory 결과를 받으므로 `$connect` / `$disconnect` 소유권과 요청 트랜잭션 상태가 격리됩니다.
169
169
 
170
+ 트랜잭션 경계에는 호스트가 제공하는 `AsyncLocalStorage` 지원이 필요합니다. `@fluojs/prisma`는 런타임이 노출하는 `globalThis.AsyncLocalStorage` 또는 Node.js의 `process.getBuiltinModule('node:async_hooks')` 호스트 경계를 통해 이를 resolve합니다. 두 경로 모두 사용할 수 없으면 동기 stack fallback으로 async boundary 사이의 `current()`를 잃는 대신, Prisma 트랜잭션을 열기 전에 `transaction()`과 `requestTransaction()`이 예외를 던집니다. 이 상태는 `createPlatformStatusSnapshot().details.transactionContext`에 `unavailable`로 보고됩니다.
171
+
170
172
  ### 수동 모듈 조합
171
173
 
172
174
  `PrismaModule.forRoot(...)` / `forRootAsync(...)`를 사용해 Prisma를 등록합니다. 커스텀 `defineModule(...)` 등록 안에서 Prisma 지원을 조합해야 할 때도 동일한 모듈 entrypoint를 import해서 사용하세요.
@@ -196,7 +198,8 @@ defineModule(ManualPrismaModule, {
196
198
  - `forRootAsync(...)`는 애플리케이션 컨테이너마다 옵션을 한 번 resolve하여, 별도 bootstrap 사이에서 클라이언트 라이프사이클과 요청 트랜잭션 격리를 보존합니다.
197
199
  - `strictTransactions: true` 설정 시 트랜잭션 미지원 환경에서 즉시 예외를 발생시킵니다.
198
200
  - `strictTransactions`가 `false`이면 클라이언트가 interactive `$transaction`을 제공하지 않을 때 직접 실행으로 fallback합니다.
199
- - 이름 있는 등록의 `name`은 trim되며, 이름은 거부됩니다.
201
+ - sync async 등록 모두에서 `client`는 실제 object/function handle이어야 하며, 누락된 handle은 모듈 등록 또는 async bootstrap 중 거부됩니다.
202
+ - 이름 있는 등록의 `name`은 public token 생성 전에 trim되며, 빈 이름은 거부됩니다.
200
203
 
201
204
  ### `PrismaService<TClient>`
202
205
 
@@ -207,6 +210,10 @@ defineModule(ManualPrismaModule, {
207
210
  - `requestTransaction(fn, signal?, options?): Promise<T>`
208
211
  - HTTP 요청 라이프사이클에 특화된 트랜잭션 경계를 실행합니다. Abort를 인식하고, shutdown 중에는 disconnect 전에 열린 요청 트랜잭션을 drain하며, Prisma client가 `signal` 옵션을 거부하면 해당 옵션 없이 재시도합니다. `transaction()`과 마찬가지로 중첩 호출은 활성 트랜잭션 컨텍스트를 재사용하고, 트랜잭션 설정을 조용히 무시하지 않도록 중첩 옵션을 거부합니다.
209
212
 
213
+ ### `PrismaTransactionInterceptor`
214
+
215
+ - 기본 이름 없는 `PrismaService` 등록을 위한 HTTP interceptor입니다. 요청 handler를 `PrismaService.requestTransaction(...)`으로 감싸 downstream `current()` 호출이 같은 transaction client를 공유하게 합니다.
216
+
210
217
  ### `PRISMA_CLIENT` (Token)
211
218
 
212
219
  원시 `PrismaClient` 인스턴스를 위한 주입 토큰입니다.
package/README.md CHANGED
@@ -142,7 +142,7 @@ class UserController {
142
142
 
143
143
  `createPrismaPlatformStatusSnapshot(...)` and `PrismaService.createPlatformStatusSnapshot()` expose the same lifecycle contract to diagnostics surfaces:
144
144
 
145
- - `readiness.status` is `not-ready` while Prisma is shutting down or stopped, and when `strictTransactions` is enabled without `$transaction(...)` support.
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.
146
146
  - `health.status` is `degraded` while request transactions are draining during shutdown and `unhealthy` after disconnect.
147
147
  - `details.activeRequestTransactions`, `details.lifecycleState`, `details.strictTransactions`, `details.supportsTransaction`, and `details.transactionAbortSignalSupport` describe the current request transaction and transaction-capability state.
148
148
  - `details.transactionContext: 'als'` identifies the async-local transaction context used by request and service transaction boundaries.
@@ -167,6 +167,8 @@ PrismaModule.forRootAsync({
167
167
 
168
168
  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
169
 
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.
171
+
170
172
  ### Manual Module Composition
171
173
 
172
174
  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.
@@ -196,7 +198,8 @@ defineModule(ManualPrismaModule, {
196
198
  - `forRootAsync(...)` resolves options once per application container, preserving client lifecycle and request transaction isolation across separate bootstraps.
197
199
  - Supports `strictTransactions: true` to throw if transaction support is missing.
198
200
  - When `strictTransactions` is `false`, PrismaService falls back to direct execution if the client does not expose interactive `$transaction`.
199
- - Names are trimmed for named registrations, and blank names are rejected.
201
+ - `client` must be a concrete object/function handle for both sync and async registration; missing handles are rejected during module registration or async bootstrap.
202
+ - Names are trimmed for named registrations, and blank names are rejected before public tokens are created.
200
203
 
201
204
  ### `PrismaService<TClient>`
202
205
 
@@ -207,6 +210,10 @@ defineModule(ManualPrismaModule, {
207
210
  - `requestTransaction(fn, signal?, options?): Promise<T>`
208
211
  - 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.
209
212
 
213
+ ### `PrismaTransactionInterceptor`
214
+
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.
216
+
210
217
  ### `PRISMA_CLIENT` (Token)
211
218
 
212
219
  Injectable token for the raw `PrismaClient` instance.
@@ -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;AA8KF;;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,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"}
package/dist/module.js CHANGED
@@ -4,10 +4,16 @@ import { PrismaService } from './service.js';
4
4
  import { getPrismaClientToken, getPrismaOptionsToken, getPrismaServiceToken } from './tokens.js';
5
5
  import { PrismaTransactionInterceptor } from './transaction.js';
6
6
  const PRISMA_NORMALIZED_OPTIONS = Symbol('fluo.prisma.normalized-options');
7
+ function isObjectLike(value) {
8
+ return typeof value === 'object' && value !== null || typeof value === 'function';
9
+ }
7
10
  function normalizePrismaRegistrationName(name) {
8
11
  if (name === undefined) {
9
12
  return undefined;
10
13
  }
14
+ if (typeof name !== 'string') {
15
+ throw new Error('PrismaModule name must be a string when provided.');
16
+ }
11
17
  const normalizedName = name.trim();
12
18
  if (normalizedName.length === 0) {
13
19
  throw new Error('PrismaModule name must be a non-empty string when provided.');
@@ -19,6 +25,9 @@ function getPrismaNormalizedOptionsToken(name) {
19
25
  return normalizedName === undefined ? PRISMA_NORMALIZED_OPTIONS : Symbol.for(`fluo.prisma.normalized-options:${normalizedName}`);
20
26
  }
21
27
  function normalizePrismaModuleOptions(options) {
28
+ if (!isObjectLike(options.client)) {
29
+ throw new Error('PrismaModule requires a client option.');
30
+ }
22
31
  return {
23
32
  name: normalizePrismaRegistrationName(options.name),
24
33
  client: options.client,
package/dist/service.d.ts CHANGED
@@ -80,6 +80,7 @@ export declare class PrismaService<TClient extends PrismaClientLike<TTransaction
80
80
  private runWithRequestTransactionClient;
81
81
  private runNestedRequestTransaction;
82
82
  private assertRequestTransactionsAvailable;
83
+ private assertTransactionContextAvailable;
83
84
  private throwIfRequestAborted;
84
85
  private runRequestTransactionWithAbortSignal;
85
86
  private canAttemptTransactionAbortSignalOption;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AASA,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;AAMpB,UAAU,oBAAoB;IAC5B,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAoBD;;;;;;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,CAAmE;IAChG,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;IAuChC,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;YAkCpG,+BAA+B;YAuB/B,2BAA2B;IAqCzC,OAAO,CAAC,kCAAkC;IAM1C,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":"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"}
package/dist/service.js CHANGED
@@ -4,14 +4,49 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
4
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
5
  function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
6
6
  function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
7
- import { AsyncLocalStorage } from 'node:async_hooks';
8
7
  import { createAbortError, createRequestAbortContext, raceWithAbort, trackActiveRequestTransaction, untrackActiveRequestTransaction } from '@fluojs/runtime';
9
8
  import { Inject } from '@fluojs/core';
10
9
  import { createPrismaPlatformStatusSnapshot } from './status.js';
11
10
  import { PRISMA_CLIENT, PRISMA_OPTIONS } from './tokens.js';
12
11
  const NESTED_TRANSACTION_OPTIONS_NOT_SUPPORTED_ERROR = 'Nested Prisma transaction options are not supported because the active transaction context is reused.';
13
12
  const REQUEST_TRANSACTION_UNAVAILABLE_ERROR = 'Prisma request transactions are not available during shutdown.';
14
- let _PrismaService;
13
+ const TRANSACTION_CONTEXT_UNAVAILABLE_ERROR = 'Prisma transaction context requires AsyncLocalStorage support from the host runtime.';
14
+ class AsyncLocalStorageTransactionContextStore {
15
+ kind = 'als';
16
+ storage;
17
+ constructor(AsyncLocalStorage) {
18
+ this.storage = new AsyncLocalStorage();
19
+ }
20
+ getStore() {
21
+ return this.storage.getStore();
22
+ }
23
+ run(context, callback) {
24
+ return this.storage.run(context, callback);
25
+ }
26
+ }
27
+ class UnavailableTransactionContextStore {
28
+ kind = 'unavailable';
29
+ getStore() {
30
+ return undefined;
31
+ }
32
+ run(_context, _callback) {
33
+ throw new Error(TRANSACTION_CONTEXT_UNAVAILABLE_ERROR);
34
+ }
35
+ }
36
+ function resolveAsyncLocalStorageConstructor(host = globalThis) {
37
+ if (typeof host.AsyncLocalStorage === 'function') {
38
+ return host.AsyncLocalStorage;
39
+ }
40
+ return host.process?.getBuiltinModule?.('node:async_hooks').AsyncLocalStorage;
41
+ }
42
+ function createTransactionContextStore() {
43
+ const AsyncLocalStorage = resolveAsyncLocalStorageConstructor();
44
+ if (typeof AsyncLocalStorage === 'function') {
45
+ return new AsyncLocalStorageTransactionContextStore(AsyncLocalStorage);
46
+ }
47
+ return new UnavailableTransactionContextStore();
48
+ }
49
+
15
50
  /**
16
51
  * Prisma runtime facade that owns lifecycle hooks and transaction context access.
17
52
  *
@@ -19,11 +54,12 @@ let _PrismaService;
19
54
  * @typeParam TTransactionClient Transaction-scoped client resolved inside `$transaction(...)` callbacks.
20
55
  * @typeParam TTransactionOptions Options forwarded to Prisma interactive transactions.
21
56
  */
57
+ let _PrismaService;
22
58
  class PrismaService {
23
59
  static {
24
60
  [_PrismaService, _initClass] = _applyDecs(this, [Inject(PRISMA_CLIENT, PRISMA_OPTIONS)], []).c;
25
61
  }
26
- transactions = new AsyncLocalStorage();
62
+ transactions = createTransactionContextStore();
27
63
  activeRequestTransactions = new Set();
28
64
  transactionAbortSignalSupport = 'unknown';
29
65
  lifecycleState = 'created';
@@ -60,6 +96,7 @@ class PrismaService {
60
96
  }
61
97
  return fn();
62
98
  }
99
+ this.assertTransactionContextAvailable();
63
100
  const deferredRequestTransactionHandles = new Set();
64
101
  try {
65
102
  return await run(transactionClient => this.transactions.run({
@@ -103,7 +140,8 @@ class PrismaService {
103
140
  supportsConnect: typeof this.client.$connect === 'function',
104
141
  supportsDisconnect: typeof this.client.$disconnect === 'function',
105
142
  supportsTransaction: typeof this.client.$transaction === 'function',
106
- transactionAbortSignalSupport: this.transactionAbortSignalSupport
143
+ transactionAbortSignalSupport: this.transactionAbortSignalSupport,
144
+ transactionContext: this.transactions.kind
107
145
  });
108
146
  }
109
147
 
@@ -175,6 +213,7 @@ class PrismaService {
175
213
  }
176
214
  return fn();
177
215
  }
216
+ this.assertTransactionContextAvailable();
178
217
  return run(transactionClient => this.transactions.run({
179
218
  client: transactionClient,
180
219
  requestAbortSignal: signal
@@ -210,6 +249,11 @@ class PrismaService {
210
249
  throw new Error(REQUEST_TRANSACTION_UNAVAILABLE_ERROR);
211
250
  }
212
251
  }
252
+ assertTransactionContextAvailable() {
253
+ if (this.transactions.kind === 'unavailable') {
254
+ throw new Error(TRANSACTION_CONTEXT_UNAVAILABLE_ERROR);
255
+ }
256
+ }
213
257
  throwIfRequestAborted(signal) {
214
258
  if (signal.aborted) {
215
259
  throw createAbortError(signal.reason);
package/dist/status.d.ts CHANGED
@@ -8,6 +8,7 @@ type PrismaPlatformStatusSnapshotInput = {
8
8
  supportsDisconnect: boolean;
9
9
  supportsTransaction: boolean;
10
10
  transactionAbortSignalSupport: 'unknown' | 'supported' | 'unsupported';
11
+ transactionContext?: 'als' | 'unavailable';
11
12
  };
12
13
  /**
13
14
  * Create prisma platform status snapshot.
@@ -1 +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;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,iCAAiC,GACvC,iCAAiC,CAmBnC"}
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;IACvE,kBAAkB,CAAC,EAAE,KAAK,GAAG,aAAa,CAAC;CAC5C,CAAC;AAqEF;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,iCAAiC,GACvC,iCAAiC,CAqBnC"}
package/dist/status.js CHANGED
@@ -1,4 +1,11 @@
1
1
  function createReadiness(input) {
2
+ if (input.lifecycleState === 'created') {
3
+ return {
4
+ critical: true,
5
+ reason: 'Prisma integration has not connected yet.',
6
+ status: 'not-ready'
7
+ };
8
+ }
2
9
  if (input.lifecycleState === 'shutting-down') {
3
10
  return {
4
11
  critical: true,
@@ -20,6 +27,13 @@ function createReadiness(input) {
20
27
  status: 'not-ready'
21
28
  };
22
29
  }
30
+ if (input.supportsTransaction && input.transactionContext === 'unavailable') {
31
+ return {
32
+ critical: true,
33
+ reason: 'Prisma transaction context requires AsyncLocalStorage support from the host runtime.',
34
+ status: 'not-ready'
35
+ };
36
+ }
23
37
  return {
24
38
  critical: true,
25
39
  status: 'ready'
@@ -50,6 +64,7 @@ function createHealth(input) {
50
64
  * @returns The create prisma platform status snapshot result.
51
65
  */
52
66
  export function createPrismaPlatformStatusSnapshot(input) {
67
+ const transactionContext = input.transactionContext ?? 'als';
53
68
  return {
54
69
  details: {
55
70
  activeRequestTransactions: input.activeRequestTransactions,
@@ -59,7 +74,7 @@ export function createPrismaPlatformStatusSnapshot(input) {
59
74
  supportsDisconnect: input.supportsDisconnect,
60
75
  supportsTransaction: input.supportsTransaction,
61
76
  transactionAbortSignalSupport: input.transactionAbortSignalSupport,
62
- transactionContext: 'als'
77
+ transactionContext
63
78
  },
64
79
  health: createHealth(input),
65
80
  ownership: {
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "transaction",
10
10
  "als"
11
11
  ],
12
- "version": "1.0.0",
12
+ "version": "1.0.2",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -36,11 +36,10 @@
36
36
  "dist"
37
37
  ],
38
38
  "dependencies": {
39
- "@fluojs/core": "^1.0.0",
40
- "@fluojs/validation": "^1.0.0",
41
- "@fluojs/http": "^1.0.0",
42
- "@fluojs/di": "^1.0.0",
43
- "@fluojs/runtime": "^1.0.0"
39
+ "@fluojs/core": "^1.0.3",
40
+ "@fluojs/http": "^1.1.0",
41
+ "@fluojs/di": "^1.0.3",
42
+ "@fluojs/runtime": "^1.1.2"
44
43
  },
45
44
  "peerDependencies": {
46
45
  "@prisma/client": ">=5.0.0"
@@ -51,7 +50,8 @@
51
50
  }
52
51
  },
53
52
  "devDependencies": {
54
- "vitest": "^3.2.4"
53
+ "vitest": "^3.2.4",
54
+ "@fluojs/validation": "^1.0.5"
55
55
  },
56
56
  "scripts": {
57
57
  "prebuild": "node ../../tooling/scripts/clean-dist.mjs",