@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.ko.md +134 -54
- package/README.md +134 -53
- package/dist/module.d.ts +2 -2
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +9 -14
- package/dist/prisma-service-brand.d.ts +13 -0
- package/dist/prisma-service-brand.d.ts.map +1 -0
- package/dist/prisma-service-brand.js +23 -0
- package/dist/service.d.ts +31 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +114 -17
- package/dist/transaction-decorator-contract.notes.d.ts +19 -0
- package/dist/transaction-decorator-contract.notes.d.ts.map +1 -0
- package/dist/transaction-decorator-contract.notes.js +1 -0
- package/dist/transaction.d.ts +34 -10
- package/dist/transaction.d.ts.map +1 -1
- package/dist/transaction.js +86 -8
- package/package.json +6 -6
package/dist/service.js
CHANGED
|
@@ -4,13 +4,28 @@ 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 { createAbortError, createRequestAbortContext, raceWithAbort, trackActiveRequestTransaction, untrackActiveRequestTransaction } from '@fluojs/runtime';
|
|
8
7
|
import { Inject } from '@fluojs/core';
|
|
8
|
+
import { createAbortError, createRequestAbortContext, raceWithAbort, trackActiveRequestTransaction, untrackActiveRequestTransaction } from '@fluojs/runtime';
|
|
9
|
+
import { markPrismaServiceHandle } from './prisma-service-brand.js';
|
|
9
10
|
import { createPrismaPlatformStatusSnapshot } from './status.js';
|
|
10
11
|
import { PRISMA_CLIENT, PRISMA_OPTIONS } from './tokens.js';
|
|
11
12
|
const NESTED_TRANSACTION_OPTIONS_NOT_SUPPORTED_ERROR = 'Nested Prisma transaction options are not supported because the active transaction context is reused.';
|
|
12
13
|
const REQUEST_TRANSACTION_UNAVAILABLE_ERROR = 'Prisma request transactions are not available during shutdown.';
|
|
14
|
+
const TRANSACTION_BOUNDARY_UNAVAILABLE_ERROR = 'Prisma transaction boundaries are not available during shutdown.';
|
|
13
15
|
const TRANSACTION_CONTEXT_UNAVAILABLE_ERROR = 'Prisma transaction context requires AsyncLocalStorage support from the host runtime.';
|
|
16
|
+
function createCurrentClientPrismaFacade(target) {
|
|
17
|
+
markPrismaServiceHandle(target);
|
|
18
|
+
return markPrismaServiceHandle(new Proxy(target, {
|
|
19
|
+
get(service, prop, receiver) {
|
|
20
|
+
if (prop in service) {
|
|
21
|
+
return Reflect.get(service, prop, receiver);
|
|
22
|
+
}
|
|
23
|
+
const currentClient = service.current();
|
|
24
|
+
const value = Reflect.get(currentClient, prop, currentClient);
|
|
25
|
+
return typeof value === 'function' ? value.bind(currentClient) : value;
|
|
26
|
+
}
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
14
29
|
class AsyncLocalStorageTransactionContextStore {
|
|
15
30
|
kind = 'als';
|
|
16
31
|
storage;
|
|
@@ -37,7 +52,11 @@ function resolveAsyncLocalStorageConstructor(host = globalThis) {
|
|
|
37
52
|
if (typeof host.AsyncLocalStorage === 'function') {
|
|
38
53
|
return host.AsyncLocalStorage;
|
|
39
54
|
}
|
|
40
|
-
|
|
55
|
+
try {
|
|
56
|
+
return host.process?.getBuiltinModule?.('node:async_hooks')?.AsyncLocalStorage;
|
|
57
|
+
} catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
41
60
|
}
|
|
42
61
|
function createTransactionContextStore() {
|
|
43
62
|
const AsyncLocalStorage = resolveAsyncLocalStorageConstructor();
|
|
@@ -61,6 +80,7 @@ class PrismaService {
|
|
|
61
80
|
}
|
|
62
81
|
transactions = createTransactionContextStore();
|
|
63
82
|
activeRequestTransactions = new Set();
|
|
83
|
+
activeTransactionBoundaries = new Set();
|
|
64
84
|
transactionAbortSignalSupport = 'unknown';
|
|
65
85
|
lifecycleState = 'created';
|
|
66
86
|
constructor(client, serviceOptions = {
|
|
@@ -68,6 +88,41 @@ class PrismaService {
|
|
|
68
88
|
}) {
|
|
69
89
|
this.client = client;
|
|
70
90
|
this.serviceOptions = serviceOptions;
|
|
91
|
+
markPrismaServiceHandle(this);
|
|
92
|
+
this.installCurrentClientFacade();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Creates the low-level DI facade that forwards unknown Prisma API properties to the ambient `current()` client.
|
|
97
|
+
*
|
|
98
|
+
* @remarks
|
|
99
|
+
* This compatibility helper is used by `PrismaModule` provider wiring. Application code should prefer
|
|
100
|
+
* `PrismaModule.forRoot(...)` or `PrismaModule.forRootAsync(...)`, then type injected repository handles as
|
|
101
|
+
* `PrismaServiceFacade<TClient>` when direct generated Prisma delegates are needed.
|
|
102
|
+
*
|
|
103
|
+
* @param client Root Prisma client registered in the module.
|
|
104
|
+
* @param serviceOptions Runtime transaction options consumed by the Fluo wrapper.
|
|
105
|
+
* @returns A transaction-aware facade that exposes wrapper methods plus the root Prisma client surface.
|
|
106
|
+
*/
|
|
107
|
+
static createFacade(client, serviceOptions = {
|
|
108
|
+
strictTransactions: false
|
|
109
|
+
}) {
|
|
110
|
+
return createCurrentClientPrismaFacade(new _PrismaService(client, serviceOptions));
|
|
111
|
+
}
|
|
112
|
+
installCurrentClientFacade() {
|
|
113
|
+
for (const prop of Reflect.ownKeys(this.client)) {
|
|
114
|
+
if (prop in this) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
Object.defineProperty(this, prop, {
|
|
118
|
+
configurable: true,
|
|
119
|
+
get: () => {
|
|
120
|
+
const current = this.current();
|
|
121
|
+
const value = Reflect.get(current, prop);
|
|
122
|
+
return typeof value === 'function' ? value.bind(current) : value;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
71
126
|
}
|
|
72
127
|
|
|
73
128
|
/**
|
|
@@ -90,23 +145,29 @@ class PrismaService {
|
|
|
90
145
|
}
|
|
91
146
|
return fn();
|
|
92
147
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
throw new Error('Transaction not supported: Prisma client does not implement $transaction.');
|
|
96
|
-
}
|
|
97
|
-
return fn();
|
|
98
|
-
}
|
|
99
|
-
this.assertTransactionContextAvailable();
|
|
100
|
-
const deferredRequestTransactionHandles = new Set();
|
|
148
|
+
this.assertTransactionBoundariesAvailable();
|
|
149
|
+
const activeTransaction = this.trackActiveTransactionBoundary();
|
|
101
150
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
151
|
+
if (typeof this.client.$transaction !== 'function') {
|
|
152
|
+
if (this.serviceOptions.strictTransactions) {
|
|
153
|
+
throw new Error('Transaction not supported: Prisma client does not implement $transaction.');
|
|
154
|
+
}
|
|
155
|
+
return await fn();
|
|
156
|
+
}
|
|
157
|
+
this.assertTransactionContextAvailable();
|
|
158
|
+
const deferredRequestTransactionHandles = new Set();
|
|
159
|
+
try {
|
|
160
|
+
return await run(transactionClient => this.transactions.run({
|
|
161
|
+
client: transactionClient,
|
|
162
|
+
deferredRequestTransactionHandles
|
|
163
|
+
}, fn), options);
|
|
164
|
+
} finally {
|
|
165
|
+
for (const handle of deferredRequestTransactionHandles) {
|
|
166
|
+
this.untrackActiveRequestTransaction(handle);
|
|
167
|
+
}
|
|
109
168
|
}
|
|
169
|
+
} finally {
|
|
170
|
+
this.untrackActiveTransactionBoundary(activeTransaction);
|
|
110
171
|
}
|
|
111
172
|
}
|
|
112
173
|
async onModuleInit() {
|
|
@@ -121,6 +182,7 @@ class PrismaService {
|
|
|
121
182
|
transaction.abort(new Error('Application shutdown interrupted an open request transaction.'));
|
|
122
183
|
}
|
|
123
184
|
await Promise.allSettled(Array.from(this.activeRequestTransactions, transaction => transaction.settled));
|
|
185
|
+
await Promise.allSettled(Array.from(this.activeTransactionBoundaries, transaction => transaction.settled));
|
|
124
186
|
if (typeof this.client.$disconnect === 'function') {
|
|
125
187
|
await this.client.$disconnect();
|
|
126
188
|
}
|
|
@@ -249,6 +311,11 @@ class PrismaService {
|
|
|
249
311
|
throw new Error(REQUEST_TRANSACTION_UNAVAILABLE_ERROR);
|
|
250
312
|
}
|
|
251
313
|
}
|
|
314
|
+
assertTransactionBoundariesAvailable() {
|
|
315
|
+
if (this.lifecycleState === 'shutting-down' || this.lifecycleState === 'stopped') {
|
|
316
|
+
throw new Error(TRANSACTION_BOUNDARY_UNAVAILABLE_ERROR);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
252
319
|
assertTransactionContextAvailable() {
|
|
253
320
|
if (this.transactions.kind === 'unavailable') {
|
|
254
321
|
throw new Error(TRANSACTION_CONTEXT_UNAVAILABLE_ERROR);
|
|
@@ -322,8 +389,38 @@ class PrismaService {
|
|
|
322
389
|
untrackActiveRequestTransaction(handle) {
|
|
323
390
|
untrackActiveRequestTransaction(this.activeRequestTransactions, handle);
|
|
324
391
|
}
|
|
392
|
+
trackActiveTransactionBoundary() {
|
|
393
|
+
let settle;
|
|
394
|
+
const active = {
|
|
395
|
+
settled: new Promise(resolve => {
|
|
396
|
+
settle = resolve;
|
|
397
|
+
})
|
|
398
|
+
};
|
|
399
|
+
this.activeTransactionBoundaries.add(active);
|
|
400
|
+
return {
|
|
401
|
+
active,
|
|
402
|
+
settle
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
untrackActiveTransactionBoundary(handle) {
|
|
406
|
+
this.activeTransactionBoundaries.delete(handle.active);
|
|
407
|
+
handle.settle();
|
|
408
|
+
}
|
|
325
409
|
static {
|
|
326
410
|
_initClass();
|
|
327
411
|
}
|
|
328
412
|
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Injection-facing Prisma facade type that combines the Fluo wrapper methods with the registered Prisma client surface.
|
|
416
|
+
*
|
|
417
|
+
* @remarks
|
|
418
|
+
* `PrismaModule` resolves `PrismaService` to a facade that forwards unknown properties to `current()`. Use this type in
|
|
419
|
+
* repositories that call generated Prisma delegates directly, and use `PrismaService<TClient>` when only wrapper methods
|
|
420
|
+
* (`current()`, `transaction(...)`, `requestTransaction(...)`, and status snapshots) are needed.
|
|
421
|
+
*
|
|
422
|
+
* @typeParam TClient Root Prisma client shape registered in the module.
|
|
423
|
+
* @typeParam TTransactionClient Transaction-scoped client resolved inside `$transaction(...)` callbacks.
|
|
424
|
+
* @typeParam TTransactionOptions Options forwarded to Prisma interactive transactions.
|
|
425
|
+
*/
|
|
329
426
|
export { _PrismaService as PrismaService };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TC39 standard method decorator — 2023-11 (Babel @babel/plugin-proposal-decorators)
|
|
3
|
+
* NO reflect-metadata. NO experimentalDecorators.
|
|
4
|
+
*
|
|
5
|
+
* Factory signature:
|
|
6
|
+
* Transaction(accessor?) returns (value, context: ClassMethodDecoratorContext) => Function
|
|
7
|
+
*
|
|
8
|
+
* Default: @Transaction() — uses `this` as the service
|
|
9
|
+
* Explicit: @Transaction((self) => self.analytics) — uses returned service property
|
|
10
|
+
*
|
|
11
|
+
* Nested reuse: if active context exists, reuses it (no new transaction opened)
|
|
12
|
+
* Nested options: if active context exists AND options passed, THROWS
|
|
13
|
+
*/
|
|
14
|
+
export type TransactionAccessor<THost, TService> = (self: THost) => TService;
|
|
15
|
+
/**
|
|
16
|
+
* Standard method decorator factory signature for Prisma service transaction boundaries.
|
|
17
|
+
*/
|
|
18
|
+
export type TransactionDecorator = <TService>(accessor?: TransactionAccessor<unknown, TService>) => (value: Function, context: ClassMethodDecoratorContext) => Function;
|
|
19
|
+
//# sourceMappingURL=transaction-decorator-contract.notes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction-decorator-contract.notes.d.ts","sourceRoot":"","sources":["../src/transaction-decorator-contract.notes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,mBAAmB,CAAC,KAAK,EAAE,QAAQ,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,QAAQ,CAAC;AAE7E;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAC1C,QAAQ,CAAC,EAAE,mBAAmB,CAAC,OAAO,EAAE,QAAQ,CAAC,KAC9C,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,KAAK,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/transaction.d.ts
CHANGED
|
@@ -1,25 +1,49 @@
|
|
|
1
|
-
import type { Interceptor, InterceptorContext } from '@fluojs/http';
|
|
1
|
+
import type { CallHandler, Interceptor, InterceptorContext } from '@fluojs/http';
|
|
2
2
|
import { PrismaService } from './service.js';
|
|
3
3
|
import type { PrismaClientLike } from './types.js';
|
|
4
|
+
type TransactionalPrismaService<TOptions = unknown> = {
|
|
5
|
+
createPlatformStatusSnapshot(): unknown;
|
|
6
|
+
current(): unknown;
|
|
7
|
+
transaction<T>(fn: () => Promise<T>, options?: TOptions): Promise<T>;
|
|
8
|
+
};
|
|
9
|
+
type TransactionAccessor<THost, TOptions> = (self: THost) => TransactionalPrismaService<TOptions>;
|
|
10
|
+
type TransactionMethod<THost, TArgs extends unknown[], TResult> = (this: THost, ...args: TArgs) => Promise<TResult>;
|
|
4
11
|
/**
|
|
5
|
-
*
|
|
12
|
+
* Wraps a service method in a `PrismaService.transaction(...)` boundary.
|
|
6
13
|
*
|
|
7
14
|
* @remarks
|
|
8
|
-
*
|
|
9
|
-
*
|
|
15
|
+
* This is a TC39 standard method decorator (2023-11) and does not use legacy decorator metadata or `reflect-metadata`.
|
|
16
|
+
* `@Transaction()` resolves a Prisma service from the decorated instance, while
|
|
17
|
+
* `@Transaction((self) => self.prisma)` selects an explicit service for named or multi-client registrations.
|
|
18
|
+
* Calls made while a transaction context is already active reuse the existing Prisma transaction through `PrismaService`.
|
|
19
|
+
* Passing Prisma transaction options to a nested call is rejected by `PrismaService.transaction(...)` so option intent is not
|
|
20
|
+
* silently ignored.
|
|
21
|
+
*
|
|
22
|
+
* @param input Optional service accessor or Prisma interactive transaction options.
|
|
23
|
+
* @returns A standard method decorator that runs the original method inside a Prisma transaction boundary.
|
|
24
|
+
*/
|
|
25
|
+
export declare function Transaction<THost, TOptions = unknown>(input?: TransactionAccessor<THost, TOptions> | TOptions): <TArgs extends unknown[], TResult>(value: TransactionMethod<THost, TArgs, TResult>, context: ClassMethodDecoratorContext<THost, TransactionMethod<THost, TArgs, TResult>>) => TransactionMethod<THost, TArgs, TResult>;
|
|
26
|
+
/**
|
|
27
|
+
* Compatibility HTTP interceptor that opens a Prisma request transaction around a routed handler.
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* This deprecated 1.x bridge forwards the request `AbortSignal` to `PrismaService.requestTransaction(...)` and is
|
|
31
|
+
* registered only by the unnamed `PrismaModule` entrypoint. Prefer service-layer `@Transaction()` or an explicit
|
|
32
|
+
* request boundary for new code.
|
|
33
|
+
*
|
|
34
|
+
* @deprecated Prefer service-layer `@Transaction()` or explicit `PrismaService.requestTransaction(...)`.
|
|
10
35
|
*/
|
|
11
36
|
export declare class PrismaTransactionInterceptor implements Interceptor {
|
|
12
37
|
private readonly prisma;
|
|
13
38
|
constructor(prisma: PrismaService<PrismaClientLike>);
|
|
14
39
|
/**
|
|
15
|
-
* Runs the downstream handler inside
|
|
40
|
+
* Runs the downstream handler inside the compatibility request transaction.
|
|
16
41
|
*
|
|
17
|
-
* @param context Interceptor context
|
|
42
|
+
* @param context Interceptor context containing the request cancellation signal.
|
|
18
43
|
* @param next Downstream handler chain.
|
|
19
|
-
* @returns The downstream
|
|
44
|
+
* @returns The downstream result after the request transaction settles.
|
|
20
45
|
*/
|
|
21
|
-
intercept(context: InterceptorContext, next:
|
|
22
|
-
handle(): Promise<unknown>;
|
|
23
|
-
}): Promise<unknown>;
|
|
46
|
+
intercept(context: InterceptorContext, next: CallHandler): Promise<unknown>;
|
|
24
47
|
}
|
|
48
|
+
export {};
|
|
25
49
|
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGjF,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,KAAK,0BAA0B,CAAC,QAAQ,GAAG,OAAO,IAAI;IACpD,4BAA4B,IAAI,OAAO,CAAC;IACxC,OAAO,IAAI,OAAO,CAAC;IACnB,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACtE,CAAC;AAEF,KAAK,mBAAmB,CAAC,KAAK,EAAE,QAAQ,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,0BAA0B,CAAC,QAAQ,CAAC,CAAC;AAElG,KAAK,iBAAiB,CAAC,KAAK,EAAE,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,IAAI,CAChE,IAAI,EAAE,KAAK,EACX,GAAG,IAAI,EAAE,KAAK,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAuEtB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,EACnD,KAAK,CAAC,EAAE,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,GACtD,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EAClC,KAAK,EAAE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAC/C,OAAO,EAAE,2BAA2B,CAAC,KAAK,EAAE,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,KAClF,iBAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAiB5C;AAED;;;;;;;;;GASG;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,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;CAGlF"}
|
package/dist/transaction.js
CHANGED
|
@@ -5,15 +5,93 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
|
|
|
5
5
|
function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; }
|
|
6
6
|
function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; }
|
|
7
7
|
import { Inject } from '@fluojs/core';
|
|
8
|
+
import { isPrismaServiceHandle } from './prisma-service-brand.js';
|
|
8
9
|
import { PrismaService } from './service.js';
|
|
9
|
-
|
|
10
|
+
function isPrismaServiceLike(value) {
|
|
11
|
+
return typeof value === 'object' && value !== null && isPrismaServiceHandle(value) && 'createPlatformStatusSnapshot' in value && typeof value.createPlatformStatusSnapshot === 'function' && 'current' in value && typeof value.current === 'function' && 'transaction' in value && typeof value.transaction === 'function';
|
|
12
|
+
}
|
|
13
|
+
function readProperty(value, property) {
|
|
14
|
+
if (typeof value !== 'object' && typeof value !== 'function' || value === null) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
return Reflect.get(value, property);
|
|
18
|
+
}
|
|
19
|
+
function addPrismaServiceCandidate(candidates, value) {
|
|
20
|
+
if (!isPrismaServiceLike(value) || candidates.includes(value)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
candidates.push(value);
|
|
24
|
+
}
|
|
25
|
+
function resolveDefaultPrismaService(self) {
|
|
26
|
+
const candidates = [];
|
|
27
|
+
const directPrisma = readProperty(self, 'prisma');
|
|
28
|
+
addPrismaServiceCandidate(candidates, directPrisma);
|
|
29
|
+
addPrismaServiceCandidate(candidates, self);
|
|
30
|
+
for (const value of Object.values(Object(self))) {
|
|
31
|
+
addPrismaServiceCandidate(candidates, value);
|
|
32
|
+
const nestedPrisma = readProperty(value, 'prisma');
|
|
33
|
+
addPrismaServiceCandidate(candidates, nestedPrisma);
|
|
34
|
+
}
|
|
35
|
+
if (candidates.length === 1) {
|
|
36
|
+
return candidates[0];
|
|
37
|
+
}
|
|
38
|
+
if (candidates.length > 1) {
|
|
39
|
+
throw new Error('Ambiguous PrismaService resolution for @Transaction(). Provide an explicit accessor function.');
|
|
40
|
+
}
|
|
41
|
+
throw new Error('Unable to resolve PrismaService for @Transaction(). Provide an accessor function.');
|
|
42
|
+
}
|
|
43
|
+
function resolveTransactionInput(input) {
|
|
44
|
+
if (typeof input === 'function') {
|
|
45
|
+
return {
|
|
46
|
+
accessor: input
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
options: input
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
10
54
|
/**
|
|
11
|
-
*
|
|
55
|
+
* Wraps a service method in a `PrismaService.transaction(...)` boundary.
|
|
12
56
|
*
|
|
13
57
|
* @remarks
|
|
14
|
-
*
|
|
15
|
-
*
|
|
58
|
+
* This is a TC39 standard method decorator (2023-11) and does not use legacy decorator metadata or `reflect-metadata`.
|
|
59
|
+
* `@Transaction()` resolves a Prisma service from the decorated instance, while
|
|
60
|
+
* `@Transaction((self) => self.prisma)` selects an explicit service for named or multi-client registrations.
|
|
61
|
+
* Calls made while a transaction context is already active reuse the existing Prisma transaction through `PrismaService`.
|
|
62
|
+
* Passing Prisma transaction options to a nested call is rejected by `PrismaService.transaction(...)` so option intent is not
|
|
63
|
+
* silently ignored.
|
|
64
|
+
*
|
|
65
|
+
* @param input Optional service accessor or Prisma interactive transaction options.
|
|
66
|
+
* @returns A standard method decorator that runs the original method inside a Prisma transaction boundary.
|
|
16
67
|
*/
|
|
68
|
+
export function Transaction(input) {
|
|
69
|
+
const {
|
|
70
|
+
accessor,
|
|
71
|
+
options
|
|
72
|
+
} = resolveTransactionInput(input);
|
|
73
|
+
return function transactionDecorator(value, context) {
|
|
74
|
+
if (context.kind !== 'method') {
|
|
75
|
+
throw new Error('@Transaction() can only decorate methods.');
|
|
76
|
+
}
|
|
77
|
+
return async function wrappedTransactionMethod(...args) {
|
|
78
|
+
const prisma = accessor?.(this) ?? resolveDefaultPrismaService(this);
|
|
79
|
+
return prisma.transaction(() => value.apply(this, args), options);
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Compatibility HTTP interceptor that opens a Prisma request transaction around a routed handler.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* This deprecated 1.x bridge forwards the request `AbortSignal` to `PrismaService.requestTransaction(...)` and is
|
|
89
|
+
* registered only by the unnamed `PrismaModule` entrypoint. Prefer service-layer `@Transaction()` or an explicit
|
|
90
|
+
* request boundary for new code.
|
|
91
|
+
*
|
|
92
|
+
* @deprecated Prefer service-layer `@Transaction()` or explicit `PrismaService.requestTransaction(...)`.
|
|
93
|
+
*/
|
|
94
|
+
let _PrismaTransactionInt;
|
|
17
95
|
class PrismaTransactionInterceptor {
|
|
18
96
|
static {
|
|
19
97
|
[_PrismaTransactionInt, _initClass] = _applyDecs(this, [Inject(PrismaService)], []).c;
|
|
@@ -23,14 +101,14 @@ class PrismaTransactionInterceptor {
|
|
|
23
101
|
}
|
|
24
102
|
|
|
25
103
|
/**
|
|
26
|
-
* Runs the downstream handler inside
|
|
104
|
+
* Runs the downstream handler inside the compatibility request transaction.
|
|
27
105
|
*
|
|
28
|
-
* @param context Interceptor context
|
|
106
|
+
* @param context Interceptor context containing the request cancellation signal.
|
|
29
107
|
* @param next Downstream handler chain.
|
|
30
|
-
* @returns The downstream
|
|
108
|
+
* @returns The downstream result after the request transaction settles.
|
|
31
109
|
*/
|
|
32
110
|
async intercept(context, next) {
|
|
33
|
-
return this.prisma.requestTransaction(
|
|
111
|
+
return this.prisma.requestTransaction(() => next.handle(), context.requestContext.request.signal);
|
|
34
112
|
}
|
|
35
113
|
static {
|
|
36
114
|
_initClass();
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"transaction",
|
|
10
10
|
"als"
|
|
11
11
|
],
|
|
12
|
-
"version": "1.
|
|
12
|
+
"version": "1.1.1",
|
|
13
13
|
"private": false,
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"repository": {
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
"dist"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@fluojs/core": "^1.0
|
|
40
|
-
"@fluojs/http": "^
|
|
41
|
-
"@fluojs/di": "^
|
|
42
|
-
"@fluojs/runtime": "^
|
|
39
|
+
"@fluojs/core": "^1.1.0",
|
|
40
|
+
"@fluojs/http": "^2.0.1",
|
|
41
|
+
"@fluojs/di": "^2.0.0",
|
|
42
|
+
"@fluojs/runtime": "^2.0.1"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@prisma/client": ">=5.0.0"
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"vitest": "^3.2.4",
|
|
54
|
-
"@fluojs/validation": "^1.0.
|
|
54
|
+
"@fluojs/validation": "^1.0.6"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|