@nestjs-transactional/core 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +265 -0
- package/dist/bootstrap/transactional-methods.bootstrap.d.ts +42 -0
- package/dist/bootstrap/transactional-methods.bootstrap.js +129 -0
- package/dist/bootstrap/transactional-methods.bootstrap.js.map +1 -0
- package/dist/context/transaction-context-view.d.ts +38 -0
- package/dist/context/transaction-context-view.js +48 -0
- package/dist/context/transaction-context-view.js.map +1 -0
- package/dist/context/transaction.context.d.ts +114 -0
- package/dist/context/transaction.context.js +112 -0
- package/dist/context/transaction.context.js.map +1 -0
- package/dist/decorators/inject-decorators.d.ts +36 -0
- package/dist/decorators/inject-decorators.js +45 -0
- package/dist/decorators/inject-decorators.js.map +1 -0
- package/dist/decorators/transactional.decorator.d.ts +64 -0
- package/dist/decorators/transactional.decorator.js +82 -0
- package/dist/decorators/transactional.decorator.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptor/transactional.interceptor.d.ts +28 -0
- package/dist/interceptor/transactional.interceptor.js +56 -0
- package/dist/interceptor/transactional.interceptor.js.map +1 -0
- package/dist/internal/markers.d.ts +15 -0
- package/dist/internal/markers.js +18 -0
- package/dist/internal/markers.js.map +1 -0
- package/dist/manager/adapter.registry.d.ts +103 -0
- package/dist/manager/adapter.registry.js +179 -0
- package/dist/manager/adapter.registry.js.map +1 -0
- package/dist/manager/transaction.manager.d.ts +129 -0
- package/dist/manager/transaction.manager.js +412 -0
- package/dist/manager/transaction.manager.js.map +1 -0
- package/dist/module/transactional.module.d.ts +242 -0
- package/dist/module/transactional.module.js +374 -0
- package/dist/module/transactional.module.js.map +1 -0
- package/dist/observability/transaction-observer.d.ts +78 -0
- package/dist/observability/transaction-observer.js +18 -0
- package/dist/observability/transaction-observer.js.map +1 -0
- package/dist/testing/in-memory.adapter.d.ts +66 -0
- package/dist/testing/in-memory.adapter.js +83 -0
- package/dist/testing/in-memory.adapter.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +18 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tokens/constants.d.ts +14 -0
- package/dist/tokens/constants.js +17 -0
- package/dist/tokens/constants.js.map +1 -0
- package/dist/tokens/index.d.ts +3 -0
- package/dist/tokens/index.js +11 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/tokens/token-utils.d.ts +52 -0
- package/dist/tokens/token-utils.js +67 -0
- package/dist/tokens/token-utils.js.map +1 -0
- package/dist/types/domain-event.d.ts +18 -0
- package/dist/types/domain-event.js +3 -0
- package/dist/types/domain-event.js.map +1 -0
- package/dist/types/errors.d.ts +50 -0
- package/dist/types/errors.js +61 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/isolation.d.ts +11 -0
- package/dist/types/isolation.js +3 -0
- package/dist/types/isolation.js.map +1 -0
- package/dist/types/propagation.d.ts +63 -0
- package/dist/types/propagation.js +67 -0
- package/dist/types/propagation.js.map +1 -0
- package/dist/types/transaction-adapter.d.ts +72 -0
- package/dist/types/transaction-adapter.js +3 -0
- package/dist/types/transaction-adapter.js.map +1 -0
- package/dist/types/transaction-handle.d.ts +23 -0
- package/dist/types/transaction-handle.js +3 -0
- package/dist/types/transaction-handle.js.map +1 -0
- package/dist/types/transaction-options.d.ts +88 -0
- package/dist/types/transaction-options.js +3 -0
- package/dist/types/transaction-options.js.map +1 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Igor Golovanov
|
|
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.md
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# @nestjs-transactional/core
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@nestjs-transactional/core)
|
|
4
|
+
[](https://github.com/igorgolovanov/nestjs-transactional/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
Core primitives for declarative Spring-style transaction management
|
|
7
|
+
in NestJS.
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
The adapter-agnostic foundation of the `@nestjs-transactional` family:
|
|
12
|
+
|
|
13
|
+
- `TransactionContext` — `AsyncLocalStorage`-backed carrier that
|
|
14
|
+
propagates the active transaction across `await` boundaries.
|
|
15
|
+
- `TransactionManager` — runtime with the full Spring propagation
|
|
16
|
+
semantics (`REQUIRED`, `REQUIRES_NEW`, `NESTED`, `SUPPORTS`,
|
|
17
|
+
`NOT_SUPPORTED`, `NEVER`, `MANDATORY`) plus `rollbackFor` /
|
|
18
|
+
`noRollbackFor` rules and before / after commit / rollback hooks.
|
|
19
|
+
- `@Transactional()`, `@ReadOnly()`, `@TransactionalOn(instance)`
|
|
20
|
+
decorators — metadata-only; runtime wrapping is performed by the
|
|
21
|
+
three coordinated mechanisms documented in
|
|
22
|
+
[ADR-005](../../docs/adr/005-method-wrapping-strategy.md).
|
|
23
|
+
- `TransactionalInterceptor` — wires `@Transactional` on controllers,
|
|
24
|
+
resolvers, gateways, and microservice handlers via `APP_INTERCEPTOR`.
|
|
25
|
+
- `TransactionalModule.forRoot` / `forRootAsync` — module wiring,
|
|
26
|
+
one call per dataSource (multi-`forRoot` pattern, see
|
|
27
|
+
[ADR-019](../../docs/adr/019-outbox-multi-forroot-pattern.md)).
|
|
28
|
+
- `TransactionAdapter<THandle>` SPI — the port for ORM-specific
|
|
29
|
+
adapters.
|
|
30
|
+
- `InMemoryTransactionAdapter` (via the `@nestjs-transactional/core/testing`
|
|
31
|
+
subpath) — drop-in adapter for unit tests.
|
|
32
|
+
|
|
33
|
+
This package does not depend on any concrete ORM. Install
|
|
34
|
+
`@nestjs-transactional/typeorm` for TypeORM integration, or implement
|
|
35
|
+
your own adapter against the `TransactionAdapter` interface.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pnpm add @nestjs-transactional/core reflect-metadata
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Load `reflect-metadata` once at the application entry point (same as
|
|
44
|
+
for NestJS itself).
|
|
45
|
+
|
|
46
|
+
## Quick start
|
|
47
|
+
|
|
48
|
+
In typical use this package is imported via an integration package
|
|
49
|
+
(like `@nestjs-transactional/typeorm`) which registers the adapter
|
|
50
|
+
into the `AdapterRegistry` automatically. The minimal application
|
|
51
|
+
shape is:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { Module } from '@nestjs/common';
|
|
55
|
+
import { TransactionalModule } from '@nestjs-transactional/core';
|
|
56
|
+
import { TypeOrmTransactionalModule } from '@nestjs-transactional/typeorm';
|
|
57
|
+
// ...your TypeORM config
|
|
58
|
+
|
|
59
|
+
@Module({
|
|
60
|
+
imports: [
|
|
61
|
+
TypeOrmModule.forRoot({ /* ... */ }),
|
|
62
|
+
|
|
63
|
+
// Infrastructure-only forRoot — registers TransactionManager,
|
|
64
|
+
// AdapterRegistry, and the interceptor. No `adapter` here; the
|
|
65
|
+
// integration package below registers it.
|
|
66
|
+
TransactionalModule.forRoot({ isGlobal: true }),
|
|
67
|
+
|
|
68
|
+
// Integration package registers `TypeOrmTransactionAdapter`
|
|
69
|
+
// for the default dataSource.
|
|
70
|
+
TypeOrmTransactionalModule.forRoot(),
|
|
71
|
+
],
|
|
72
|
+
})
|
|
73
|
+
export class AppModule {}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`@Transactional()` on any controller handler, query handler, or
|
|
77
|
+
service method is then wrapped in a transaction automatically:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { Controller, Get, Param } from '@nestjs/common';
|
|
81
|
+
import { Transactional } from '@nestjs-transactional/core';
|
|
82
|
+
|
|
83
|
+
@Controller('orders')
|
|
84
|
+
export class OrdersController {
|
|
85
|
+
constructor(private readonly orders: OrdersService) {}
|
|
86
|
+
|
|
87
|
+
@Get(':id')
|
|
88
|
+
@Transactional()
|
|
89
|
+
async findOne(@Param('id') id: string) {
|
|
90
|
+
return this.orders.findById(id);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Direct adapter registration (custom backends)
|
|
96
|
+
|
|
97
|
+
When implementing a new `TransactionAdapter` (Prisma, Mongoose, ...),
|
|
98
|
+
pass it to `forRoot` directly:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { TransactionalModule, type TransactionAdapter } from '@nestjs-transactional/core';
|
|
102
|
+
|
|
103
|
+
const myAdapter: TransactionAdapter = /* ... */;
|
|
104
|
+
|
|
105
|
+
@Module({
|
|
106
|
+
imports: [
|
|
107
|
+
TransactionalModule.forRoot({
|
|
108
|
+
isGlobal: true,
|
|
109
|
+
adapter: myAdapter,
|
|
110
|
+
}),
|
|
111
|
+
],
|
|
112
|
+
})
|
|
113
|
+
export class AppModule {}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
For multi-dataSource setups, call `forRoot` once per dataSource —
|
|
117
|
+
each call registers exactly one adapter under its dataSource name.
|
|
118
|
+
|
|
119
|
+
## Decorator options
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
import {
|
|
123
|
+
Transactional,
|
|
124
|
+
ReadOnly,
|
|
125
|
+
TransactionalOn,
|
|
126
|
+
PropagationMode,
|
|
127
|
+
} from '@nestjs-transactional/core';
|
|
128
|
+
|
|
129
|
+
class ReportsService {
|
|
130
|
+
// Explicit propagation + isolation.
|
|
131
|
+
@Transactional({
|
|
132
|
+
propagation: PropagationMode.REQUIRES_NEW,
|
|
133
|
+
isolation: 'SERIALIZABLE',
|
|
134
|
+
timeout: 10_000,
|
|
135
|
+
})
|
|
136
|
+
async rebuildReport() { /* ... */ }
|
|
137
|
+
|
|
138
|
+
// Shorthand for { readOnly: true }.
|
|
139
|
+
@ReadOnly()
|
|
140
|
+
async exportCsv() { /* ... */ }
|
|
141
|
+
|
|
142
|
+
// Rollback rules — commit on `ValidationError`, roll back on others.
|
|
143
|
+
@Transactional({ noRollbackFor: [ValidationError] })
|
|
144
|
+
async processBatch() { /* ... */ }
|
|
145
|
+
|
|
146
|
+
// Target a specific dataSource in multi-DataSource setups.
|
|
147
|
+
@TransactionalOn('billing')
|
|
148
|
+
async chargeCard() { /* ... */ }
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Propagation semantics:
|
|
153
|
+
|
|
154
|
+
| Mode | Active outer transaction | No outer transaction |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| `REQUIRED` (default) | join | start new |
|
|
157
|
+
| `REQUIRES_NEW` | suspend + start new, then resume | start new |
|
|
158
|
+
| `NESTED` | run inside a savepoint | start new |
|
|
159
|
+
| `SUPPORTS` | join | run without transaction |
|
|
160
|
+
| `NOT_SUPPORTED` | suspend + run without transaction, then resume | run without transaction |
|
|
161
|
+
| `NEVER` | throw `IllegalTransactionStateError` | run without transaction |
|
|
162
|
+
| `MANDATORY` | join | throw `IllegalTransactionStateError` |
|
|
163
|
+
|
|
164
|
+
## Async module configuration
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import { TransactionalModule } from '@nestjs-transactional/core';
|
|
168
|
+
|
|
169
|
+
@Module({
|
|
170
|
+
imports: [
|
|
171
|
+
TransactionalModule.forRootAsync({
|
|
172
|
+
imports: [ConfigModule],
|
|
173
|
+
inject: [ConfigService],
|
|
174
|
+
useFactory: (config: ConfigService) => ({
|
|
175
|
+
adapter: buildAdapterFromConfig(config),
|
|
176
|
+
}),
|
|
177
|
+
}),
|
|
178
|
+
],
|
|
179
|
+
})
|
|
180
|
+
export class AppModule {}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`isGlobal` and `registerInterceptor` remain static top-level flags —
|
|
184
|
+
they must be known at module definition time. The async factory
|
|
185
|
+
returns the per-call configuration (`adapter` and any other
|
|
186
|
+
runtime-resolved options).
|
|
187
|
+
|
|
188
|
+
## Lifecycle hooks
|
|
189
|
+
|
|
190
|
+
Register hooks from inside a transactional method — they fire on the
|
|
191
|
+
current transaction:
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import { TransactionManager } from '@nestjs-transactional/core';
|
|
195
|
+
|
|
196
|
+
export class OrdersService {
|
|
197
|
+
constructor(private readonly manager: TransactionManager) {}
|
|
198
|
+
|
|
199
|
+
@Transactional()
|
|
200
|
+
async placeOrder(payload: PlaceOrderDto) {
|
|
201
|
+
const order = await this.orders.insert(payload);
|
|
202
|
+
|
|
203
|
+
this.manager.registerAfterCommit(async () => {
|
|
204
|
+
// Fires only after the adapter commits. Never on rollback.
|
|
205
|
+
await this.analytics.trackOrderPlaced(order.id);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
this.manager.registerAfterRollback(async (error) => {
|
|
209
|
+
// Receives the error that caused the rollback.
|
|
210
|
+
await this.metrics.recordFailedOrder(order.id, error);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return order;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Hook errors are caught and logged via NestJS `Logger` — they do not
|
|
219
|
+
affect the transaction outcome or prevent sibling hooks from running.
|
|
220
|
+
|
|
221
|
+
## Testing
|
|
222
|
+
|
|
223
|
+
`InMemoryTransactionAdapter` from the `/testing` subpath gives
|
|
224
|
+
adapter-level observability without a real database:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import { InMemoryTransactionAdapter } from '@nestjs-transactional/core/testing';
|
|
228
|
+
import { TransactionalModule } from '@nestjs-transactional/core';
|
|
229
|
+
|
|
230
|
+
const adapter = new InMemoryTransactionAdapter();
|
|
231
|
+
|
|
232
|
+
const moduleRef = await Test.createTestingModule({
|
|
233
|
+
imports: [
|
|
234
|
+
TransactionalModule.forRoot({ isGlobal: true, adapter }),
|
|
235
|
+
],
|
|
236
|
+
}).compile();
|
|
237
|
+
|
|
238
|
+
// After exercising the code under test:
|
|
239
|
+
expect(adapter.committedTransactions).toHaveLength(1);
|
|
240
|
+
expect(adapter.rolledBackTransactions).toHaveLength(0);
|
|
241
|
+
expect(adapter.savepointsReleased).toHaveLength(0);
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`adapter.reset()` clears all observation arrays between tests when
|
|
245
|
+
you keep a single adapter instance across cases. For multi-DS test
|
|
246
|
+
setups, pass distinct dataSource names to the constructor:
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
const billing = new InMemoryTransactionAdapter('billing');
|
|
250
|
+
const inventory = new InMemoryTransactionAdapter('inventory');
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Worked examples
|
|
254
|
+
|
|
255
|
+
- [`basic-transactional`](../../examples/basic-transactional) —
|
|
256
|
+
`@Transactional()` on a plain service.
|
|
257
|
+
- [`testing-patterns`](../../examples/testing-patterns) —
|
|
258
|
+
`InMemoryTransactionAdapter` from `core/testing` plus the outbox /
|
|
259
|
+
integration test layers.
|
|
260
|
+
|
|
261
|
+
Full catalogue: [examples/README.md](../../examples/README.md).
|
|
262
|
+
|
|
263
|
+
## Status
|
|
264
|
+
|
|
265
|
+
Alpha. Public API may change between 0.x releases.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { type OnApplicationBootstrap } from '@nestjs/common';
|
|
3
|
+
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
|
|
4
|
+
import { TransactionManager } from '../manager/transaction.manager';
|
|
5
|
+
/**
|
|
6
|
+
* `OnApplicationBootstrap` service that wraps every `@Transactional()`
|
|
7
|
+
* method on a plain `@Injectable()` provider with
|
|
8
|
+
* `TransactionManager.run(...)`.
|
|
9
|
+
*
|
|
10
|
+
* This is the second of the three coordinated wrapping mechanisms
|
|
11
|
+
* described in ADR-005:
|
|
12
|
+
*
|
|
13
|
+
* 1. {@link import('../interceptor/transactional.interceptor').TransactionalInterceptor}
|
|
14
|
+
* — for controller / resolver / gateway request-boundary handlers.
|
|
15
|
+
* 2. This class — for regular `@Injectable` services.
|
|
16
|
+
* 3. `CqrsHandlerWrapper` (in `@nestjs-transactional/cqrs`) — for CQRS
|
|
17
|
+
* command / query / event handlers.
|
|
18
|
+
*
|
|
19
|
+
* Skip rules:
|
|
20
|
+
* - CQRS handler classes (detected via `@nestjs/cqrs` metadata keys) are
|
|
21
|
+
* left alone so their wrapping goes through `CqrsHandlerWrapper`.
|
|
22
|
+
* - Methods already marked with {@link WRAPPED_MARKER} are left alone
|
|
23
|
+
* (safety net against double-wrap).
|
|
24
|
+
*
|
|
25
|
+
* Metadata lookup priority: method-level `@Transactional` overrides
|
|
26
|
+
* class-level. Classes with only class-level `@Transactional` apply the
|
|
27
|
+
* same options to every method that doesn't carry its own metadata.
|
|
28
|
+
*
|
|
29
|
+
* Registered by `TransactionalModule.forRoot` unless the caller sets
|
|
30
|
+
* `registerMethodsBootstrap: false`.
|
|
31
|
+
*/
|
|
32
|
+
export declare class TransactionalMethodsBootstrap implements OnApplicationBootstrap {
|
|
33
|
+
private readonly discovery;
|
|
34
|
+
private readonly metadataScanner;
|
|
35
|
+
private readonly manager;
|
|
36
|
+
private readonly logger;
|
|
37
|
+
constructor(discovery: DiscoveryService, metadataScanner: MetadataScanner, manager: TransactionManager);
|
|
38
|
+
onApplicationBootstrap(): void;
|
|
39
|
+
private isCqrsHandler;
|
|
40
|
+
private wrapMethod;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=transactional-methods.bootstrap.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var TransactionalMethodsBootstrap_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.TransactionalMethodsBootstrap = void 0;
|
|
14
|
+
require("reflect-metadata");
|
|
15
|
+
const common_1 = require("@nestjs/common");
|
|
16
|
+
const core_1 = require("@nestjs/core");
|
|
17
|
+
const transactional_decorator_1 = require("../decorators/transactional.decorator");
|
|
18
|
+
const markers_1 = require("../internal/markers");
|
|
19
|
+
const transaction_manager_1 = require("../manager/transaction.manager");
|
|
20
|
+
/**
|
|
21
|
+
* Metadata keys set by `@nestjs/cqrs`'s handler decorators. Mirrored here
|
|
22
|
+
* so the bootstrap skips CQRS handler classes — they are wrapped by
|
|
23
|
+
* `CqrsHandlerWrapper` in `@nestjs-transactional/cqrs`, which knows how
|
|
24
|
+
* to apply per-kind defaults (read-only queries, etc.). Double-wrapping
|
|
25
|
+
* is additionally guarded via `WRAPPED_MARKER`.
|
|
26
|
+
*/
|
|
27
|
+
const CQRS_HANDLER_KEYS = [
|
|
28
|
+
'__commandHandler__',
|
|
29
|
+
'__queryHandler__',
|
|
30
|
+
'__eventsHandler__',
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* `OnApplicationBootstrap` service that wraps every `@Transactional()`
|
|
34
|
+
* method on a plain `@Injectable()` provider with
|
|
35
|
+
* `TransactionManager.run(...)`.
|
|
36
|
+
*
|
|
37
|
+
* This is the second of the three coordinated wrapping mechanisms
|
|
38
|
+
* described in ADR-005:
|
|
39
|
+
*
|
|
40
|
+
* 1. {@link import('../interceptor/transactional.interceptor').TransactionalInterceptor}
|
|
41
|
+
* — for controller / resolver / gateway request-boundary handlers.
|
|
42
|
+
* 2. This class — for regular `@Injectable` services.
|
|
43
|
+
* 3. `CqrsHandlerWrapper` (in `@nestjs-transactional/cqrs`) — for CQRS
|
|
44
|
+
* command / query / event handlers.
|
|
45
|
+
*
|
|
46
|
+
* Skip rules:
|
|
47
|
+
* - CQRS handler classes (detected via `@nestjs/cqrs` metadata keys) are
|
|
48
|
+
* left alone so their wrapping goes through `CqrsHandlerWrapper`.
|
|
49
|
+
* - Methods already marked with {@link WRAPPED_MARKER} are left alone
|
|
50
|
+
* (safety net against double-wrap).
|
|
51
|
+
*
|
|
52
|
+
* Metadata lookup priority: method-level `@Transactional` overrides
|
|
53
|
+
* class-level. Classes with only class-level `@Transactional` apply the
|
|
54
|
+
* same options to every method that doesn't carry its own metadata.
|
|
55
|
+
*
|
|
56
|
+
* Registered by `TransactionalModule.forRoot` unless the caller sets
|
|
57
|
+
* `registerMethodsBootstrap: false`.
|
|
58
|
+
*/
|
|
59
|
+
let TransactionalMethodsBootstrap = TransactionalMethodsBootstrap_1 = class TransactionalMethodsBootstrap {
|
|
60
|
+
discovery;
|
|
61
|
+
metadataScanner;
|
|
62
|
+
manager;
|
|
63
|
+
logger = new common_1.Logger(TransactionalMethodsBootstrap_1.name);
|
|
64
|
+
constructor(discovery, metadataScanner, manager) {
|
|
65
|
+
this.discovery = discovery;
|
|
66
|
+
this.metadataScanner = metadataScanner;
|
|
67
|
+
this.manager = manager;
|
|
68
|
+
}
|
|
69
|
+
onApplicationBootstrap() {
|
|
70
|
+
const providers = this.discovery.getProviders();
|
|
71
|
+
let wrappedCount = 0;
|
|
72
|
+
for (const wrapper of providers) {
|
|
73
|
+
if (wrapper.instance === null || wrapper.instance === undefined)
|
|
74
|
+
continue;
|
|
75
|
+
if (typeof wrapper.metatype !== 'function')
|
|
76
|
+
continue;
|
|
77
|
+
const metatype = wrapper.metatype;
|
|
78
|
+
if (this.isCqrsHandler(metatype))
|
|
79
|
+
continue;
|
|
80
|
+
const instance = wrapper.instance;
|
|
81
|
+
const prototype = Object.getPrototypeOf(instance);
|
|
82
|
+
if (prototype === null)
|
|
83
|
+
continue;
|
|
84
|
+
const classMetadata = (0, transactional_decorator_1.getTransactionalMetadata)(metatype);
|
|
85
|
+
const methodNames = this.metadataScanner.getAllMethodNames(prototype);
|
|
86
|
+
const methods = prototype;
|
|
87
|
+
const host = instance;
|
|
88
|
+
for (const methodName of methodNames) {
|
|
89
|
+
const protoMethod = methods[methodName];
|
|
90
|
+
if (typeof protoMethod !== 'function')
|
|
91
|
+
continue;
|
|
92
|
+
const metadata = (0, transactional_decorator_1.getTransactionalMetadata)(protoMethod) ?? classMetadata;
|
|
93
|
+
if (metadata === undefined)
|
|
94
|
+
continue;
|
|
95
|
+
const currentMethod = host[methodName];
|
|
96
|
+
if (typeof currentMethod !== 'function')
|
|
97
|
+
continue;
|
|
98
|
+
if (Reflect.getMetadata(markers_1.WRAPPED_MARKER, currentMethod) === true)
|
|
99
|
+
continue;
|
|
100
|
+
if (this.wrapMethod(host, methodName, currentMethod, instance, metadata)) {
|
|
101
|
+
wrappedCount++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this.logger.log(`Wrapped ${wrappedCount} @Transactional method${wrappedCount === 1 ? '' : 's'}`);
|
|
106
|
+
}
|
|
107
|
+
isCqrsHandler(metatype) {
|
|
108
|
+
return CQRS_HANDLER_KEYS.some((key) => Reflect.hasMetadata(key, metatype));
|
|
109
|
+
}
|
|
110
|
+
wrapMethod(host, methodName, currentMethod, instance, metadata) {
|
|
111
|
+
const boundOriginal = currentMethod.bind(instance);
|
|
112
|
+
const manager = this.manager;
|
|
113
|
+
const wrapped = (...args) => manager.run(metadata, () => Promise.resolve(boundOriginal(...args)));
|
|
114
|
+
Reflect.defineMetadata(markers_1.WRAPPED_MARKER, true, wrapped);
|
|
115
|
+
Reflect.defineMetadata(transactional_decorator_1.TRANSACTIONAL_METADATA, metadata, wrapped);
|
|
116
|
+
host[methodName] = wrapped;
|
|
117
|
+
this.logger.debug(`Wrapped ${instance.constructor.name}.${methodName} ` +
|
|
118
|
+
`(propagation=${metadata.propagation ?? 'REQUIRED'})`);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
exports.TransactionalMethodsBootstrap = TransactionalMethodsBootstrap;
|
|
123
|
+
exports.TransactionalMethodsBootstrap = TransactionalMethodsBootstrap = TransactionalMethodsBootstrap_1 = __decorate([
|
|
124
|
+
(0, common_1.Injectable)(),
|
|
125
|
+
__metadata("design:paramtypes", [core_1.DiscoveryService,
|
|
126
|
+
core_1.MetadataScanner,
|
|
127
|
+
transaction_manager_1.TransactionManager])
|
|
128
|
+
], TransactionalMethodsBootstrap);
|
|
129
|
+
//# sourceMappingURL=transactional-methods.bootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional-methods.bootstrap.js","sourceRoot":"","sources":["../../src/bootstrap/transactional-methods.bootstrap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type ActiveTransaction } from './transaction.context';
|
|
2
|
+
/**
|
|
3
|
+
* Per-dataSource read-only view over {@link TransactionContext}'s active
|
|
4
|
+
* transaction Map (DD-022). Bound to a single dataSource name at
|
|
5
|
+
* construction; consumers ask "is there an active transaction for *my*
|
|
6
|
+
* dataSource?" without typing the name twice.
|
|
7
|
+
*
|
|
8
|
+
* Mutations (set / remove) intentionally do NOT live on this surface —
|
|
9
|
+
* they remain on `TransactionContext` and are the domain of
|
|
10
|
+
* {@link TransactionManager} alone. The view is a thin lookup helper
|
|
11
|
+
* to keep the per-dataSource inject-decorator surface (DD-022)
|
|
12
|
+
* meaningful without duplicating the lifecycle API.
|
|
13
|
+
*
|
|
14
|
+
* Wired by `TransactionalModule.forRoot` under
|
|
15
|
+
* `getTransactionContextToken(dataSource)`. Inject via
|
|
16
|
+
* `@InjectTransactionContext(dataSource?)`.
|
|
17
|
+
*/
|
|
18
|
+
export declare class TransactionContextView {
|
|
19
|
+
readonly dataSource: string;
|
|
20
|
+
/**
|
|
21
|
+
* @param dataSource - Public dataSource name this view is bound to.
|
|
22
|
+
* `'default'` for the single-adapter case.
|
|
23
|
+
*/
|
|
24
|
+
constructor(dataSource: string);
|
|
25
|
+
/**
|
|
26
|
+
* Return the active transaction for the bound dataSource, or
|
|
27
|
+
* `undefined` if none is active. Equivalent to calling
|
|
28
|
+
* `TransactionContext.getActiveTransactionByDataSource(this.dataSource)`.
|
|
29
|
+
*/
|
|
30
|
+
getActiveTransaction(): ActiveTransaction | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Predicate convenience over {@link getActiveTransaction}. Useful for
|
|
33
|
+
* smart-facade publishers (DD-024) that branch on whether the bound
|
|
34
|
+
* dataSource is the one currently running a transaction.
|
|
35
|
+
*/
|
|
36
|
+
hasActiveTransaction(): boolean;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=transaction-context-view.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransactionContextView = void 0;
|
|
4
|
+
const transaction_context_1 = require("./transaction.context");
|
|
5
|
+
/**
|
|
6
|
+
* Per-dataSource read-only view over {@link TransactionContext}'s active
|
|
7
|
+
* transaction Map (DD-022). Bound to a single dataSource name at
|
|
8
|
+
* construction; consumers ask "is there an active transaction for *my*
|
|
9
|
+
* dataSource?" without typing the name twice.
|
|
10
|
+
*
|
|
11
|
+
* Mutations (set / remove) intentionally do NOT live on this surface —
|
|
12
|
+
* they remain on `TransactionContext` and are the domain of
|
|
13
|
+
* {@link TransactionManager} alone. The view is a thin lookup helper
|
|
14
|
+
* to keep the per-dataSource inject-decorator surface (DD-022)
|
|
15
|
+
* meaningful without duplicating the lifecycle API.
|
|
16
|
+
*
|
|
17
|
+
* Wired by `TransactionalModule.forRoot` under
|
|
18
|
+
* `getTransactionContextToken(dataSource)`. Inject via
|
|
19
|
+
* `@InjectTransactionContext(dataSource?)`.
|
|
20
|
+
*/
|
|
21
|
+
class TransactionContextView {
|
|
22
|
+
dataSource;
|
|
23
|
+
/**
|
|
24
|
+
* @param dataSource - Public dataSource name this view is bound to.
|
|
25
|
+
* `'default'` for the single-adapter case.
|
|
26
|
+
*/
|
|
27
|
+
constructor(dataSource) {
|
|
28
|
+
this.dataSource = dataSource;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Return the active transaction for the bound dataSource, or
|
|
32
|
+
* `undefined` if none is active. Equivalent to calling
|
|
33
|
+
* `TransactionContext.getActiveTransactionByDataSource(this.dataSource)`.
|
|
34
|
+
*/
|
|
35
|
+
getActiveTransaction() {
|
|
36
|
+
return transaction_context_1.TransactionContext.getActiveTransactionByDataSource(this.dataSource);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Predicate convenience over {@link getActiveTransaction}. Useful for
|
|
40
|
+
* smart-facade publishers (DD-024) that branch on whether the bound
|
|
41
|
+
* dataSource is the one currently running a transaction.
|
|
42
|
+
*/
|
|
43
|
+
hasActiveTransaction() {
|
|
44
|
+
return this.getActiveTransaction() !== undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.TransactionContextView = TransactionContextView;
|
|
48
|
+
//# sourceMappingURL=transaction-context-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction-context-view.js","sourceRoot":"","sources":["../../src/context/transaction-context-view.ts"],"names":[],"mappings":";;;AAAA,+DAG+B;AAE/B;;;;;;;;;;;;;;;GAeG;AACH,MAAa,sBAAsB;IAKZ;IAJrB;;;OAGG;IACH,YAAqB,UAAkB;QAAlB,eAAU,GAAV,UAAU,CAAQ;IAAG,CAAC;IAE3C;;;;OAIG;IACH,oBAAoB;QAClB,OAAO,wCAAkB,CAAC,gCAAgC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC9E,CAAC;IAED;;;;OAIG;IACH,oBAAoB;QAClB,OAAO,IAAI,CAAC,oBAAoB,EAAE,KAAK,SAAS,CAAC;IACnD,CAAC;CACF;AAxBD,wDAwBC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { TransactionHandle } from '../types/transaction-handle';
|
|
2
|
+
import type { TransactionOptions } from '../types/transaction-options';
|
|
3
|
+
/**
|
|
4
|
+
* A single transaction currently live on the async context. Adapters register
|
|
5
|
+
* one of these under their `adapterInstanceName` when they begin a transaction
|
|
6
|
+
* and remove it when the transaction ends. Hooks registered on this object
|
|
7
|
+
* are fired by the manager during commit / rollback.
|
|
8
|
+
*/
|
|
9
|
+
export interface ActiveTransaction {
|
|
10
|
+
/** Adapter-specific opaque handle — cast by the adapter's own helpers. */
|
|
11
|
+
readonly handle: TransactionHandle;
|
|
12
|
+
/** Adapter type name (e.g. `'typeorm'`) — mirrors `handle.adapterName`. */
|
|
13
|
+
readonly adapterName: string;
|
|
14
|
+
/** Adapter instance name (e.g. `'primary'`, `'billing'`) under which it was registered. */
|
|
15
|
+
readonly adapterInstanceName: string;
|
|
16
|
+
/** Options the manager handed to the adapter when beginning this transaction. */
|
|
17
|
+
readonly options: TransactionOptions;
|
|
18
|
+
/** Wall-clock moment the transaction began, for observability. */
|
|
19
|
+
readonly startedAt: Date;
|
|
20
|
+
/** Hooks executed just before the adapter issues COMMIT. A throwing hook rolls the transaction back. */
|
|
21
|
+
readonly afterCommitHooks: (() => Promise<void>)[];
|
|
22
|
+
/** Hooks executed after a successful COMMIT. */
|
|
23
|
+
readonly afterRollbackHooks: ((error: unknown) => Promise<void>)[];
|
|
24
|
+
/** Hooks executed after a ROLLBACK; receive the error that caused the rollback. */
|
|
25
|
+
readonly beforeCommitHooks: (() => Promise<void>)[];
|
|
26
|
+
/** Correlation id inherited from the enclosing {@link TransactionContext} scope. */
|
|
27
|
+
readonly correlationId: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Per-scope state that lives on the async context. Created on the outermost
|
|
31
|
+
* {@link TransactionContext.run} call and reused by all nested ones, so that
|
|
32
|
+
* a single logical unit of work owns a single correlation id and a single
|
|
33
|
+
* registry of active transactions across adapters.
|
|
34
|
+
*/
|
|
35
|
+
export interface TransactionContextStore {
|
|
36
|
+
/** Active transactions keyed by `adapterInstanceName`. */
|
|
37
|
+
readonly activeTransactions: Map<string, ActiveTransaction>;
|
|
38
|
+
/** Stable correlation id for the whole scope — set by the outermost run(). */
|
|
39
|
+
readonly correlationId: string;
|
|
40
|
+
/** Wall-clock moment the scope began. */
|
|
41
|
+
readonly startedAt: Date;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Thin façade over `AsyncLocalStorage` that carries the active
|
|
45
|
+
* {@link TransactionContextStore} across async boundaries. This is the
|
|
46
|
+
* foundation of the module — every decorator, interceptor, and adapter helper
|
|
47
|
+
* ultimately asks this class whether a transaction is live on the current
|
|
48
|
+
* async chain.
|
|
49
|
+
*
|
|
50
|
+
* **Internal Map key format**: `${adapterName}:${instanceName}` (composite)
|
|
51
|
+
* for historical reasons and cross-package compatibility — typeorm
|
|
52
|
+
* helpers, the CQRS dispatcher, and the outbox publisher all consume
|
|
53
|
+
* this format directly via {@link getActiveTransaction}. The composite
|
|
54
|
+
* key is also the format `TransactionManager` writes under.
|
|
55
|
+
*
|
|
56
|
+
* **Public dataSource-name access**:
|
|
57
|
+
* {@link getActiveTransactionByDataSource} provides dataSource-name
|
|
58
|
+
* lookup for Phase 14.2+ multi-adapter consumers — it scans the Map
|
|
59
|
+
* for the entry whose `adapterInstanceName === dataSource`. Both
|
|
60
|
+
* access patterns coexist; future cleanup is possible once
|
|
61
|
+
* cross-package consumers migrate to the dataSource-name lookup and
|
|
62
|
+
* no backwards-compatibility constraints remain.
|
|
63
|
+
*/
|
|
64
|
+
export declare class TransactionContext {
|
|
65
|
+
/**
|
|
66
|
+
* Run `fn` inside a transaction context scope.
|
|
67
|
+
*
|
|
68
|
+
* - If there is no active store on the current async chain, a new store is
|
|
69
|
+
* created (empty active-transaction map, `correlationId`, current time)
|
|
70
|
+
* and installed for the duration of `fn`.
|
|
71
|
+
* - If there is already an active store, `fn` is executed directly — the
|
|
72
|
+
* existing store is reused. The `correlationId` argument is ignored in
|
|
73
|
+
* that case; the outermost scope owns the correlation id.
|
|
74
|
+
*
|
|
75
|
+
* Propagates the value resolved (or error thrown) by `fn`.
|
|
76
|
+
*/
|
|
77
|
+
static run<T>(correlationId: string, fn: () => Promise<T>): Promise<T>;
|
|
78
|
+
/** Return the active store, or `undefined` if called outside any run() scope. */
|
|
79
|
+
static getStore(): TransactionContextStore | undefined;
|
|
80
|
+
/** Return the active transaction registered under `adapterInstanceName`, or `undefined`. */
|
|
81
|
+
static getActiveTransaction(adapterInstanceName: string): ActiveTransaction | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Return the active transaction whose adapter instance was
|
|
84
|
+
* registered under the given dataSource name (DD-020 / DD-023).
|
|
85
|
+
* Scans the active-transactions Map for the entry whose
|
|
86
|
+
* `adapterInstanceName === dataSource`.
|
|
87
|
+
*
|
|
88
|
+
* Returns the first match — there is exactly one active transaction
|
|
89
|
+
* per dataSource by construction (multiple adapters sharing the
|
|
90
|
+
* same dataSource name is rejected at the registry level by
|
|
91
|
+
* {@link AdapterRegistry.getByDataSource}).
|
|
92
|
+
*
|
|
93
|
+
* Used by {@link TransactionContextView} and by smart-facade
|
|
94
|
+
* publishers to answer "is there an active transaction for *my*
|
|
95
|
+
* dataSource?" without needing to know the adapter type that owns
|
|
96
|
+
* the dataSource.
|
|
97
|
+
*/
|
|
98
|
+
static getActiveTransactionByDataSource(dataSource: string): ActiveTransaction | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Register `tx` under `adapterInstanceName` on the current store.
|
|
101
|
+
*
|
|
102
|
+
* @throws {IllegalTransactionStateError} If called outside of a run() scope.
|
|
103
|
+
* Adapters must not attempt to register a transaction without a
|
|
104
|
+
* surrounding context — doing so would leak the transaction state.
|
|
105
|
+
*/
|
|
106
|
+
static setActiveTransaction(adapterInstanceName: string, tx: ActiveTransaction): void;
|
|
107
|
+
/**
|
|
108
|
+
* Remove the active transaction registered under `adapterInstanceName`.
|
|
109
|
+
* Idempotent: a no-op when no store is active or the instance is not
|
|
110
|
+
* registered, so that adapter cleanup paths can call it unconditionally.
|
|
111
|
+
*/
|
|
112
|
+
static removeActiveTransaction(adapterInstanceName: string): void;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=transaction.context.d.ts.map
|