@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
|
@@ -0,0 +1,412 @@
|
|
|
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 __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var TransactionManager_1;
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.TransactionManager = void 0;
|
|
17
|
+
const node_crypto_1 = require("node:crypto");
|
|
18
|
+
const common_1 = require("@nestjs/common");
|
|
19
|
+
const transaction_context_1 = require("../context/transaction.context");
|
|
20
|
+
const transaction_observer_1 = require("../observability/transaction-observer");
|
|
21
|
+
const errors_1 = require("../types/errors");
|
|
22
|
+
const propagation_1 = require("../types/propagation");
|
|
23
|
+
const adapter_registry_1 = require("./adapter.registry");
|
|
24
|
+
/**
|
|
25
|
+
* Runtime that executes a callback inside a transaction, following the
|
|
26
|
+
* requested {@link PropagationMode}. Exposes registration points for
|
|
27
|
+
* before/after-commit and after-rollback hooks that the surrounding
|
|
28
|
+
* transaction fires at the appropriate phase.
|
|
29
|
+
*
|
|
30
|
+
* All seven Spring-compatible propagation modes are supported:
|
|
31
|
+
* `REQUIRED`, `REQUIRES_NEW`, `NESTED`, `SUPPORTS`, `NOT_SUPPORTED`,
|
|
32
|
+
* `NEVER`, `MANDATORY`. See {@link TransactionManager.run} for the
|
|
33
|
+
* per-mode behaviour.
|
|
34
|
+
*/
|
|
35
|
+
let TransactionManager = TransactionManager_1 = class TransactionManager {
|
|
36
|
+
registry;
|
|
37
|
+
observers;
|
|
38
|
+
logger = new common_1.Logger(TransactionManager_1.name);
|
|
39
|
+
constructor(registry, observers = []) {
|
|
40
|
+
this.registry = registry;
|
|
41
|
+
this.observers = observers;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Execute `fn` inside a transaction managed by the adapter resolved from
|
|
45
|
+
* `options` (or defaults from {@link AdapterRegistry}).
|
|
46
|
+
*
|
|
47
|
+
* Behaviour by propagation mode:
|
|
48
|
+
* - `REQUIRED` (default): join the active transaction for this adapter
|
|
49
|
+
* instance if one exists; otherwise start a new transaction.
|
|
50
|
+
* - `REQUIRES_NEW`: always start a new, independent transaction. If an
|
|
51
|
+
* outer transaction is active, its {@link ActiveTransaction} entry is
|
|
52
|
+
* suspended out of the context Map for the duration of the inner call
|
|
53
|
+
* and restored afterwards.
|
|
54
|
+
* - `NESTED`: if an outer transaction is active, run inside a savepoint
|
|
55
|
+
* on that transaction (via {@link TransactionAdapter.runInSavepoint}).
|
|
56
|
+
* A savepoint rollback leaves the outer transaction intact. Lifecycle
|
|
57
|
+
* hooks registered inside a `NESTED` block attach to the outer
|
|
58
|
+
* transaction and fire on the outer commit/rollback. If no outer
|
|
59
|
+
* transaction is active, `NESTED` degrades to `REQUIRED`.
|
|
60
|
+
* - `SUPPORTS`: run `fn` in the outer transaction if present; otherwise
|
|
61
|
+
* run `fn` directly with no transaction and no lifecycle hooks.
|
|
62
|
+
* - `NOT_SUPPORTED`: suspend the outer transaction (remove its entry
|
|
63
|
+
* from the context Map) and run `fn` without transactional context.
|
|
64
|
+
* Restore on return. Note that the adapter-level connection/query
|
|
65
|
+
* runner is NOT actually suspended — this is a context-level opt-out.
|
|
66
|
+
* - `NEVER`: throw {@link IllegalTransactionStateError} if an outer
|
|
67
|
+
* transaction is active; otherwise run `fn` directly.
|
|
68
|
+
* - `MANDATORY`: throw {@link IllegalTransactionStateError} if no outer
|
|
69
|
+
* transaction is active; otherwise join it.
|
|
70
|
+
*
|
|
71
|
+
* Both inner and outer transactions share the surrounding
|
|
72
|
+
* {@link TransactionContext} store — same `correlationId`, same Map —
|
|
73
|
+
* only the Map slot at `instanceName` is swapped as propagation requires.
|
|
74
|
+
*/
|
|
75
|
+
async run(options, fn) {
|
|
76
|
+
// dataSource takes precedence over adapter/adapterInstance (DD-020).
|
|
77
|
+
// Resolved up front so the rest of run() works in a single shape.
|
|
78
|
+
const { adapterName, instanceName } = this.resolveAdapterIdentifiers(options);
|
|
79
|
+
const propagation = options.propagation ?? propagation_1.PropagationMode.REQUIRED;
|
|
80
|
+
const key = TransactionManager_1.contextKey(adapterName, instanceName);
|
|
81
|
+
const existing = transaction_context_1.TransactionContext.getActiveTransaction(key);
|
|
82
|
+
switch (propagation) {
|
|
83
|
+
case propagation_1.PropagationMode.REQUIRED: {
|
|
84
|
+
if (existing !== undefined) {
|
|
85
|
+
return fn();
|
|
86
|
+
}
|
|
87
|
+
const adapter = this.registry.get(adapterName, instanceName);
|
|
88
|
+
return this.startNew(adapter, adapterName, instanceName, options, fn);
|
|
89
|
+
}
|
|
90
|
+
case propagation_1.PropagationMode.REQUIRES_NEW: {
|
|
91
|
+
const adapter = this.registry.get(adapterName, instanceName);
|
|
92
|
+
if (existing === undefined) {
|
|
93
|
+
return this.startNew(adapter, adapterName, instanceName, options, fn);
|
|
94
|
+
}
|
|
95
|
+
transaction_context_1.TransactionContext.removeActiveTransaction(key);
|
|
96
|
+
try {
|
|
97
|
+
return await this.startNew(adapter, adapterName, instanceName, options, fn);
|
|
98
|
+
}
|
|
99
|
+
finally {
|
|
100
|
+
transaction_context_1.TransactionContext.setActiveTransaction(key, existing);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
case propagation_1.PropagationMode.NESTED: {
|
|
104
|
+
const adapter = this.registry.get(adapterName, instanceName);
|
|
105
|
+
if (existing === undefined) {
|
|
106
|
+
return this.startNew(adapter, adapterName, instanceName, options, fn);
|
|
107
|
+
}
|
|
108
|
+
return this.runNestedSavepoint(adapter, existing, options, fn);
|
|
109
|
+
}
|
|
110
|
+
case propagation_1.PropagationMode.SUPPORTS: {
|
|
111
|
+
return fn();
|
|
112
|
+
}
|
|
113
|
+
case propagation_1.PropagationMode.NOT_SUPPORTED: {
|
|
114
|
+
if (existing === undefined) {
|
|
115
|
+
return fn();
|
|
116
|
+
}
|
|
117
|
+
transaction_context_1.TransactionContext.removeActiveTransaction(key);
|
|
118
|
+
try {
|
|
119
|
+
return await fn();
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
transaction_context_1.TransactionContext.setActiveTransaction(key, existing);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
case propagation_1.PropagationMode.NEVER: {
|
|
126
|
+
if (existing !== undefined) {
|
|
127
|
+
throw new errors_1.IllegalTransactionStateError(`Propagation NEVER cannot be invoked inside an active transaction ` +
|
|
128
|
+
`(adapter: '${adapterName}', instance: '${instanceName}')`);
|
|
129
|
+
}
|
|
130
|
+
return fn();
|
|
131
|
+
}
|
|
132
|
+
case propagation_1.PropagationMode.MANDATORY: {
|
|
133
|
+
if (existing === undefined) {
|
|
134
|
+
throw new errors_1.IllegalTransactionStateError(`Propagation MANDATORY requires an active transaction, but none is ` +
|
|
135
|
+
`active (adapter: '${adapterName}', instance: '${instanceName}')`);
|
|
136
|
+
}
|
|
137
|
+
return fn();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Compose the {@link TransactionContext} key for a given adapter
|
|
143
|
+
* (type name) + instance pair. Manager and helper packages must agree
|
|
144
|
+
* on this format so that `@nestjs-transactional/typeorm`'s
|
|
145
|
+
* `getCurrentEntityManager` can find the transaction registered here.
|
|
146
|
+
*/
|
|
147
|
+
static contextKey(adapterName, instanceName) {
|
|
148
|
+
return `${adapterName}:${instanceName}`;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Translate the user-facing options into the internal `(adapterName,
|
|
152
|
+
* instanceName)` pair used to build the active-transaction Map key.
|
|
153
|
+
*
|
|
154
|
+
* Resolution order (DD-020):
|
|
155
|
+
* 1. `options.dataSource` set: lookup the unique adapter via the
|
|
156
|
+
* registry by dataSource name. The dataSource name becomes the
|
|
157
|
+
* `instanceName`; the registry hands back the adapter type.
|
|
158
|
+
* 2. Else: legacy path — `options.adapter` / `options.adapterInstance`
|
|
159
|
+
* fall back to the registry's defaults.
|
|
160
|
+
*/
|
|
161
|
+
resolveAdapterIdentifiers(options) {
|
|
162
|
+
if (options.dataSource !== undefined) {
|
|
163
|
+
return {
|
|
164
|
+
adapterName: this.registry.getAdapterNameByDataSource(options.dataSource),
|
|
165
|
+
instanceName: options.dataSource,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
adapterName: options.adapter ?? this.registry.getDefaultAdapterName(),
|
|
170
|
+
instanceName: options.adapterInstance ?? this.registry.getDefaultInstanceName(),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Register a hook to fire after the current transaction commits
|
|
175
|
+
* successfully. Attaches to the first active transaction on the current
|
|
176
|
+
* async context — sufficient for single-adapter setups. Hook errors are
|
|
177
|
+
* swallowed with a warning and do not reject `run()`.
|
|
178
|
+
*
|
|
179
|
+
* @throws {IllegalTransactionStateError} If called outside an active transaction.
|
|
180
|
+
*/
|
|
181
|
+
registerAfterCommit(hook) {
|
|
182
|
+
this.currentTransaction().afterCommitHooks.push(hook);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Register a hook to fire after the current transaction rolls back. The
|
|
186
|
+
* hook receives the error that caused the rollback.
|
|
187
|
+
*
|
|
188
|
+
* @throws {IllegalTransactionStateError} If called outside an active transaction.
|
|
189
|
+
*/
|
|
190
|
+
registerAfterRollback(hook) {
|
|
191
|
+
this.currentTransaction().afterRollbackHooks.push(hook);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Register a hook to fire just before the transaction commits. A throwing
|
|
195
|
+
* hook triggers the adapter's rollback — the transaction does not commit.
|
|
196
|
+
*
|
|
197
|
+
* @throws {IllegalTransactionStateError} If called outside an active transaction.
|
|
198
|
+
*/
|
|
199
|
+
registerBeforeCommit(hook) {
|
|
200
|
+
this.currentTransaction().beforeCommitHooks.push(hook);
|
|
201
|
+
}
|
|
202
|
+
async startNew(adapter, adapterName, instanceName, options, fn) {
|
|
203
|
+
const outerStore = transaction_context_1.TransactionContext.getStore();
|
|
204
|
+
const correlationId = outerStore?.correlationId ?? (0, node_crypto_1.randomUUID)();
|
|
205
|
+
let activeTx;
|
|
206
|
+
const startTime = Date.now();
|
|
207
|
+
const key = TransactionManager_1.contextKey(adapterName, instanceName);
|
|
208
|
+
const body = async () => {
|
|
209
|
+
let result;
|
|
210
|
+
try {
|
|
211
|
+
result = await adapter.runInTransaction(options, async (handle) => {
|
|
212
|
+
activeTx = {
|
|
213
|
+
handle,
|
|
214
|
+
adapterName,
|
|
215
|
+
adapterInstanceName: instanceName,
|
|
216
|
+
options,
|
|
217
|
+
startedAt: new Date(),
|
|
218
|
+
afterCommitHooks: [],
|
|
219
|
+
afterRollbackHooks: [],
|
|
220
|
+
beforeCommitHooks: [],
|
|
221
|
+
correlationId,
|
|
222
|
+
};
|
|
223
|
+
transaction_context_1.TransactionContext.setActiveTransaction(key, activeTx);
|
|
224
|
+
this.notifyStart({
|
|
225
|
+
transactionId: handle.id,
|
|
226
|
+
adapterName,
|
|
227
|
+
adapterInstanceName: instanceName,
|
|
228
|
+
correlationId,
|
|
229
|
+
options,
|
|
230
|
+
});
|
|
231
|
+
try {
|
|
232
|
+
try {
|
|
233
|
+
const innerValue = await fn();
|
|
234
|
+
// Before-commit hooks run inside the adapter callback so
|
|
235
|
+
// that a throwing hook still triggers the adapter's
|
|
236
|
+
// rollback path.
|
|
237
|
+
for (const hook of activeTx.beforeCommitHooks) {
|
|
238
|
+
await hook();
|
|
239
|
+
}
|
|
240
|
+
return { ok: true, value: innerValue };
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
if (this.shouldRollback(err, options)) {
|
|
244
|
+
throw err; // let the adapter roll back
|
|
245
|
+
}
|
|
246
|
+
// Commit despite the error — the manager will re-raise it
|
|
247
|
+
// to the caller after the adapter's commit succeeds.
|
|
248
|
+
return { ok: false, error: err };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
finally {
|
|
252
|
+
transaction_context_1.TransactionContext.removeActiveTransaction(key);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
catch (rollbackError) {
|
|
257
|
+
if (activeTx !== undefined) {
|
|
258
|
+
this.notifyRollback({
|
|
259
|
+
transactionId: activeTx.handle.id,
|
|
260
|
+
adapterName,
|
|
261
|
+
adapterInstanceName: instanceName,
|
|
262
|
+
correlationId,
|
|
263
|
+
options,
|
|
264
|
+
durationMs: Date.now() - startTime,
|
|
265
|
+
error: rollbackError,
|
|
266
|
+
rollbackCount: activeTx.afterRollbackHooks.length,
|
|
267
|
+
});
|
|
268
|
+
await this.runHooks(activeTx.afterRollbackHooks, rollbackError);
|
|
269
|
+
}
|
|
270
|
+
throw rollbackError;
|
|
271
|
+
}
|
|
272
|
+
// Adapter has committed. Fire the observer event and afterCommit
|
|
273
|
+
// hooks regardless of whether we are about to re-raise a business
|
|
274
|
+
// error — the database state is what the subscribers care about,
|
|
275
|
+
// and it has been persisted.
|
|
276
|
+
if (activeTx !== undefined) {
|
|
277
|
+
this.notifyCommit({
|
|
278
|
+
transactionId: activeTx.handle.id,
|
|
279
|
+
adapterName,
|
|
280
|
+
adapterInstanceName: instanceName,
|
|
281
|
+
correlationId,
|
|
282
|
+
options,
|
|
283
|
+
durationMs: Date.now() - startTime,
|
|
284
|
+
commitCount: activeTx.afterCommitHooks.length,
|
|
285
|
+
});
|
|
286
|
+
await this.runHooks(activeTx.afterCommitHooks);
|
|
287
|
+
}
|
|
288
|
+
if (result.ok) {
|
|
289
|
+
return result.value;
|
|
290
|
+
}
|
|
291
|
+
throw result.error;
|
|
292
|
+
};
|
|
293
|
+
if (outerStore === undefined) {
|
|
294
|
+
return transaction_context_1.TransactionContext.run(correlationId, body);
|
|
295
|
+
}
|
|
296
|
+
return body();
|
|
297
|
+
}
|
|
298
|
+
notifyStart(ctx) {
|
|
299
|
+
for (const observer of this.observers) {
|
|
300
|
+
try {
|
|
301
|
+
observer.onTransactionStart?.(ctx);
|
|
302
|
+
}
|
|
303
|
+
catch (err) {
|
|
304
|
+
this.logger.warn(`TransactionObserver.onTransactionStart failed: ${String(err)}`, err instanceof Error ? err.stack : undefined);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
notifyCommit(ctx) {
|
|
309
|
+
for (const observer of this.observers) {
|
|
310
|
+
try {
|
|
311
|
+
observer.onTransactionCommit?.(ctx);
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
this.logger.warn(`TransactionObserver.onTransactionCommit failed: ${String(err)}`, err instanceof Error ? err.stack : undefined);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
notifyRollback(ctx) {
|
|
319
|
+
for (const observer of this.observers) {
|
|
320
|
+
try {
|
|
321
|
+
observer.onTransactionRollback?.(ctx);
|
|
322
|
+
}
|
|
323
|
+
catch (err) {
|
|
324
|
+
this.logger.warn(`TransactionObserver.onTransactionRollback failed: ${String(err)}`, err instanceof Error ? err.stack : undefined);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Run `fn` inside a savepoint on `parent.handle`. Used by
|
|
330
|
+
* {@link PropagationMode.NESTED} when there is an active outer transaction.
|
|
331
|
+
*
|
|
332
|
+
* The manager intentionally does NOT create a new
|
|
333
|
+
* {@link ActiveTransaction} for the savepoint — hook registrations inside
|
|
334
|
+
* `fn` fall through {@link currentTransaction} to the outer transaction,
|
|
335
|
+
* which is the Spring-style semantic: nested events "promote" to the
|
|
336
|
+
* enclosing transaction and fire when that transaction commits.
|
|
337
|
+
*
|
|
338
|
+
* Rollback semantics follow `shouldRollback`: errors that match the
|
|
339
|
+
* rollback rules cause the adapter to roll back to the savepoint (the
|
|
340
|
+
* outer transaction is unaffected); errors that do NOT match lead to a
|
|
341
|
+
* savepoint release (commit-inside-savepoint) and the error is re-raised
|
|
342
|
+
* to the caller after the release.
|
|
343
|
+
*/
|
|
344
|
+
async runNestedSavepoint(adapter, parent, options, fn) {
|
|
345
|
+
const result = await adapter.runInSavepoint(parent.handle, async () => {
|
|
346
|
+
try {
|
|
347
|
+
const value = await fn();
|
|
348
|
+
return { ok: true, value };
|
|
349
|
+
}
|
|
350
|
+
catch (err) {
|
|
351
|
+
if (this.shouldRollback(err, options)) {
|
|
352
|
+
throw err; // adapter rolls back to savepoint
|
|
353
|
+
}
|
|
354
|
+
return { ok: false, error: err };
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
if (result.ok) {
|
|
358
|
+
return result.value;
|
|
359
|
+
}
|
|
360
|
+
throw result.error;
|
|
361
|
+
}
|
|
362
|
+
currentTransaction() {
|
|
363
|
+
const store = transaction_context_1.TransactionContext.getStore();
|
|
364
|
+
if (store === undefined) {
|
|
365
|
+
throw new errors_1.IllegalTransactionStateError('Cannot register a transactional hook outside of TransactionManager.run()');
|
|
366
|
+
}
|
|
367
|
+
for (const tx of store.activeTransactions.values()) {
|
|
368
|
+
return tx;
|
|
369
|
+
}
|
|
370
|
+
throw new errors_1.IllegalTransactionStateError('Cannot register a transactional hook: no active transaction on the current context');
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Spring-style decision on whether a thrown error should trigger rollback:
|
|
374
|
+
*
|
|
375
|
+
* 1. If `noRollbackFor` is set and matches, commit anyway (precedence).
|
|
376
|
+
* 2. Else if `rollbackFor` is set, roll back only when the error matches
|
|
377
|
+
* an entry — a non-match commits (explicit allow-list semantics).
|
|
378
|
+
* 3. Else (no rules set), any error triggers rollback — the default
|
|
379
|
+
* transactional behaviour.
|
|
380
|
+
*/
|
|
381
|
+
shouldRollback(error, options) {
|
|
382
|
+
const { noRollbackFor, rollbackFor } = options;
|
|
383
|
+
if (noRollbackFor !== undefined && noRollbackFor.length > 0) {
|
|
384
|
+
if (noRollbackFor.some((cls) => error instanceof cls)) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (rollbackFor !== undefined && rollbackFor.length > 0) {
|
|
389
|
+
return rollbackFor.some((cls) => error instanceof cls);
|
|
390
|
+
}
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
async runHooks(hooks, error) {
|
|
394
|
+
for (const hook of hooks) {
|
|
395
|
+
try {
|
|
396
|
+
await hook(error);
|
|
397
|
+
}
|
|
398
|
+
catch (hookError) {
|
|
399
|
+
this.logger.warn(`Transaction lifecycle hook failed: ${String(hookError)}`, hookError instanceof Error ? hookError.stack : undefined);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
exports.TransactionManager = TransactionManager;
|
|
405
|
+
exports.TransactionManager = TransactionManager = TransactionManager_1 = __decorate([
|
|
406
|
+
(0, common_1.Injectable)(),
|
|
407
|
+
__param(0, (0, common_1.Inject)(adapter_registry_1.ADAPTER_REGISTRY)),
|
|
408
|
+
__param(1, (0, common_1.Optional)()),
|
|
409
|
+
__param(1, (0, common_1.Inject)(transaction_observer_1.TRANSACTION_OBSERVERS)),
|
|
410
|
+
__metadata("design:paramtypes", [adapter_registry_1.AdapterRegistry, Array])
|
|
411
|
+
], TransactionManager);
|
|
412
|
+
//# sourceMappingURL=transaction.manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction.manager.js","sourceRoot":"","sources":["../../src/manager/transaction.manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { type DynamicModule, type InjectionToken, type ModuleMetadata } from '@nestjs/common';
|
|
2
|
+
import { type TransactionObserver } from '../observability/transaction-observer';
|
|
3
|
+
import type { TransactionAdapter } from '../types/transaction-adapter';
|
|
4
|
+
/**
|
|
5
|
+
* Synchronous options for {@link TransactionalModule.forRoot}.
|
|
6
|
+
*
|
|
7
|
+
* Multi-dataSource deployments call `forRoot` once per dataSource:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* TransactionalModule.forRoot({ adapter: defaultAdapter }), // default
|
|
11
|
+
* TransactionalModule.forRoot({ adapter: billingAdapter }), // billing
|
|
12
|
+
* TransactionalModule.forRoot({ adapter: inventoryAdapter }), // inventory
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Matches NestJS conventions (`TypeOrmModule`, `MongooseModule`,
|
|
16
|
+
* `ClientsModule`) and aligns with `OutboxModule.forRoot` from
|
|
17
|
+
* Phase 14.3.2 (ADR-019). Cross-call coordination of singletons
|
|
18
|
+
* (`AdapterRegistry`, `TransactionManager`, `APP_INTERCEPTOR`,
|
|
19
|
+
* `TransactionalMethodsBootstrap`, `TRANSACTION_OBSERVERS`) lives in
|
|
20
|
+
* static class storage on {@link TransactionalModule}, mirroring
|
|
21
|
+
* `@nestjs/typeorm`'s `EntitiesMetadataStorage` pattern.
|
|
22
|
+
*
|
|
23
|
+
* The `infrastructure-only` shorthand `TransactionalModule.forRoot({})`
|
|
24
|
+
* (no adapter) is preserved for setups where the adapter is contributed
|
|
25
|
+
* by an integration package's `forFeature` (e.g.
|
|
26
|
+
* `TypeOrmTransactionalModule.forFeature` calls `AdapterRegistry.register`
|
|
27
|
+
* imperatively at module-init time).
|
|
28
|
+
*
|
|
29
|
+
* Q5 invariants on multi-call:
|
|
30
|
+
* - Two calls with the same `adapter.dataSourceName` throw at module-
|
|
31
|
+
* definition time.
|
|
32
|
+
* - Two `forRoot({})` calls (both infrastructure-only) — the second
|
|
33
|
+
* throws because infrastructure has already been registered.
|
|
34
|
+
* - `forRoot({})` then `forRoot({ adapter })` works.
|
|
35
|
+
* - `forRoot({ adapter A })` then `forRoot({ adapter B })` (different
|
|
36
|
+
* dataSource) works.
|
|
37
|
+
* - `forRoot({ adapter })` then `forRoot({})` throws (infrastructure
|
|
38
|
+
* already registered).
|
|
39
|
+
*
|
|
40
|
+
* Tests that build multiple modules sequentially MUST call
|
|
41
|
+
* {@link TransactionalModule.resetForTesting} in `beforeEach`.
|
|
42
|
+
*/
|
|
43
|
+
export interface TransactionalModuleOptions {
|
|
44
|
+
/**
|
|
45
|
+
* Adapter instance to register. The adapter's `dataSourceName`
|
|
46
|
+
* keys this registration; duplicate dataSource names across
|
|
47
|
+
* `forRoot` calls throw. When omitted, this call only registers
|
|
48
|
+
* the process-wide infrastructure — adapters are then expected
|
|
49
|
+
* from an integration package (e.g.
|
|
50
|
+
* `TypeOrmTransactionalModule.forFeature`).
|
|
51
|
+
*/
|
|
52
|
+
readonly adapter?: TransactionAdapter;
|
|
53
|
+
/**
|
|
54
|
+
* When `true` (default — Phase 14.10), the module is registered as
|
|
55
|
+
* `@Global()` — its exports are available app-wide without being
|
|
56
|
+
* re-imported. Honored per call (each `forRoot` builds its own
|
|
57
|
+
* `DynamicModule` with its own `global` flag). Multi-call setups
|
|
58
|
+
* effectively require `isGlobal: true` on at least the first call
|
|
59
|
+
* for `TransactionManager` and `AdapterRegistry` to be visible
|
|
60
|
+
* across sibling DynamicModules; the default flip aligns the API
|
|
61
|
+
* with `OutboxModule` (also default-global) and removes a
|
|
62
|
+
* common-case footgun.
|
|
63
|
+
*/
|
|
64
|
+
readonly isGlobal?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* When `true` (default), the FIRST `forRoot` call registers
|
|
67
|
+
* {@link TransactionalInterceptor} as `APP_INTERCEPTOR`. Honored
|
|
68
|
+
* only on the first call — the value passed to subsequent calls is
|
|
69
|
+
* ignored (the interceptor is process-wide).
|
|
70
|
+
*/
|
|
71
|
+
readonly registerInterceptor?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* When `true` (default), the FIRST `forRoot` call registers
|
|
74
|
+
* {@link TransactionalMethodsBootstrap} — an
|
|
75
|
+
* `OnApplicationBootstrap` service that wraps every `@Transactional()`
|
|
76
|
+
* method on plain `@Injectable()` providers with
|
|
77
|
+
* `TransactionManager.run(...)`. Honored only on the first call;
|
|
78
|
+
* subsequent calls' value is ignored.
|
|
79
|
+
*/
|
|
80
|
+
readonly registerMethodsBootstrap?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Transaction observers registered under {@link TRANSACTION_OBSERVERS}.
|
|
83
|
+
* Honored only on the FIRST `forRoot` call (Q2 invariant) — passing
|
|
84
|
+
* `observers` to a subsequent call throws at module-definition time.
|
|
85
|
+
* Provide your own `TRANSACTION_OBSERVERS` provider via standard DI
|
|
86
|
+
* if your observers need DI resolution.
|
|
87
|
+
*/
|
|
88
|
+
readonly observers?: readonly TransactionObserver[];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Result shape resolved by {@link TransactionalModuleAsyncOptions.useFactory}.
|
|
92
|
+
*
|
|
93
|
+
* Per-DS DI tokens (`getTransactionalAdapterToken(ds)`,
|
|
94
|
+
* `getTransactionContextToken(ds)`,
|
|
95
|
+
* `getTransactionManagerToken(ds)`) are NOT registered for
|
|
96
|
+
* `forRootAsync` because the dataSource name is only known after the
|
|
97
|
+
* async factory runs, while NestJS provider tokens must be declared
|
|
98
|
+
* statically. If per-DS injection matters, use sync
|
|
99
|
+
* `forRoot({ adapter })` instead — build the adapter configuration
|
|
100
|
+
* through your own async logic before reaching the module imports.
|
|
101
|
+
*/
|
|
102
|
+
export interface TransactionalModuleAsyncFactoryResult {
|
|
103
|
+
readonly adapter?: TransactionAdapter;
|
|
104
|
+
readonly observers?: readonly TransactionObserver[];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Asynchronous options for {@link TransactionalModule.forRootAsync}.
|
|
108
|
+
* Mirrors the multi-call pattern of `forRoot`: each call registers one
|
|
109
|
+
* dataSource's adapter (resolved asynchronously). See
|
|
110
|
+
* {@link TransactionalModuleAsyncFactoryResult} for the per-DS-token
|
|
111
|
+
* limitation.
|
|
112
|
+
*/
|
|
113
|
+
export interface TransactionalModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
|
114
|
+
readonly isGlobal?: boolean;
|
|
115
|
+
readonly registerInterceptor?: boolean;
|
|
116
|
+
readonly registerMethodsBootstrap?: boolean;
|
|
117
|
+
readonly useFactory: (...args: never[]) => Promise<TransactionalModuleAsyncFactoryResult> | TransactionalModuleAsyncFactoryResult;
|
|
118
|
+
readonly inject?: readonly InjectionToken[];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* NestJS module that wires the core transactional runtime:
|
|
122
|
+
* {@link AdapterRegistry}, {@link TransactionManager}, and (by
|
|
123
|
+
* default) the global {@link TransactionalInterceptor}. ADR-018
|
|
124
|
+
* shape — multi-dataSource deployments call {@link forRoot} once per
|
|
125
|
+
* dataSource. Static class storage coordinates singletons across
|
|
126
|
+
* calls (mirrors Phase 14.3.2 `OutboxModule` per ADR-019).
|
|
127
|
+
*
|
|
128
|
+
* The first call registers the process-wide infrastructure; subsequent
|
|
129
|
+
* calls only contribute per-dataSource providers. Adapter-specific
|
|
130
|
+
* registration (TypeORM, Prisma, ...) is handled by the integration
|
|
131
|
+
* package's `forFeature` helper — this module only provides the
|
|
132
|
+
* adapter-agnostic infrastructure.
|
|
133
|
+
*/
|
|
134
|
+
export declare class TransactionalModule {
|
|
135
|
+
/**
|
|
136
|
+
* @internal
|
|
137
|
+
* Process-wide map of adapter-bearing `forRoot` calls, keyed by
|
|
138
|
+
* `adapter.dataSourceName`. Used for dedup of duplicate dataSource
|
|
139
|
+
* registrations at module-definition time. The {@link AdapterRegistry}
|
|
140
|
+
* itself is populated imperatively by per-DS adapter providers
|
|
141
|
+
* (factory side effect) — this Map exists for `forRoot`-call-time
|
|
142
|
+
* coordination only.
|
|
143
|
+
*
|
|
144
|
+
* Tests that build multiple modules sequentially MUST call
|
|
145
|
+
* {@link resetForTesting} between cases.
|
|
146
|
+
*/
|
|
147
|
+
private static readonly registrations;
|
|
148
|
+
/**
|
|
149
|
+
* @internal
|
|
150
|
+
* `true` once any `forRoot` (or `forRootAsync`) call has run.
|
|
151
|
+
* First-call-special providers (singletons, interceptor, methods
|
|
152
|
+
* bootstrap, observers) are only registered when this is `false` at
|
|
153
|
+
* the start of a call.
|
|
154
|
+
*
|
|
155
|
+
* Tracked separately from {@link registrations} because the
|
|
156
|
+
* shorthand `forRoot({})` (no adapter) still registers
|
|
157
|
+
* infrastructure but contributes nothing to the Map. Q5 invariant:
|
|
158
|
+
* a second `forRoot({})` after infrastructure is already registered
|
|
159
|
+
* throws — the first call already wired everything; the second has
|
|
160
|
+
* nothing to add.
|
|
161
|
+
*/
|
|
162
|
+
private static infrastructureRegistered;
|
|
163
|
+
/**
|
|
164
|
+
* @internal
|
|
165
|
+
* Counter for `forRootAsync`-only token uniqueness. Not strictly
|
|
166
|
+
* needed for `Symbol()` (each call returns a unique symbol
|
|
167
|
+
* regardless of description), but keeping a numeric id makes
|
|
168
|
+
* provider names deterministic in error messages.
|
|
169
|
+
*/
|
|
170
|
+
private static asyncCounter;
|
|
171
|
+
/**
|
|
172
|
+
* Test-only — drop every registration so a subsequent `forRoot`
|
|
173
|
+
* starts from a clean slate. Mirrors the pattern used with
|
|
174
|
+
* `OutboxModule.resetForTesting` and `EntitiesMetadataStorage` in
|
|
175
|
+
* `@nestjs/typeorm` test suites.
|
|
176
|
+
*
|
|
177
|
+
* Production code should never call this. Calling at runtime after
|
|
178
|
+
* the module has been initialised does NOT clear the provider tree
|
|
179
|
+
* NestJS already built — it only affects subsequent `forRoot` calls.
|
|
180
|
+
*
|
|
181
|
+
* @internal
|
|
182
|
+
*/
|
|
183
|
+
static resetForTesting(): void;
|
|
184
|
+
/**
|
|
185
|
+
* Synchronous registration. Each call registers one dataSource's
|
|
186
|
+
* adapter (or, with `adapter` omitted, only the process-wide
|
|
187
|
+
* infrastructure for an integration package's `forFeature` to write
|
|
188
|
+
* into).
|
|
189
|
+
*
|
|
190
|
+
* @example Single-adapter
|
|
191
|
+
* ```ts
|
|
192
|
+
* TransactionalModule.forRoot({ isGlobal: true, adapter })
|
|
193
|
+
* ```
|
|
194
|
+
*
|
|
195
|
+
* @example Multi-adapter (multiple calls)
|
|
196
|
+
* ```ts
|
|
197
|
+
* TransactionalModule.forRoot({ isGlobal: true, adapter: defaultAdapter }),
|
|
198
|
+
* TransactionalModule.forRoot({ adapter: billingAdapter }),
|
|
199
|
+
* TransactionalModule.forRoot({ adapter: inventoryAdapter }),
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* @example Infrastructure-only (TypeORM forFeature handles adapters)
|
|
203
|
+
* ```ts
|
|
204
|
+
* TransactionalModule.forRoot({ isGlobal: true }),
|
|
205
|
+
* TypeOrmTransactionalModule.forFeature({ dataSource }),
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
static forRoot(options?: TransactionalModuleOptions): DynamicModule;
|
|
209
|
+
/**
|
|
210
|
+
* Asynchronous registration. Each call registers one dataSource's
|
|
211
|
+
* adapter (resolved asynchronously via `useFactory`). Multi-DS
|
|
212
|
+
* deployments call `forRootAsync` once per dataSource.
|
|
213
|
+
*
|
|
214
|
+
* **Per-DS DI token limitation**: per-DS tokens
|
|
215
|
+
* (`getTransactionalAdapterToken(ds)`, etc.) are NOT registered
|
|
216
|
+
* for `forRootAsync` calls because the dataSource identifier is
|
|
217
|
+
* only known after the async factory runs, while NestJS provider
|
|
218
|
+
* tokens must be declared statically. If per-DS injection matters,
|
|
219
|
+
* use sync `forRoot({ adapter })` instead — build the adapter
|
|
220
|
+
* configuration through your own async logic before reaching the
|
|
221
|
+
* module imports.
|
|
222
|
+
*
|
|
223
|
+
* `forRootAsync` still works for `AdapterRegistry`-based access
|
|
224
|
+
* (`@Transactional({ dataSource })`,
|
|
225
|
+
* `getCurrentEntityManager(dataSource)`,
|
|
226
|
+
* `manager.run({ dataSource })`) — those route through the registry,
|
|
227
|
+
* which is populated by the async factory's side effect via
|
|
228
|
+
* `AdapterRegistry.register(...)`.
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```ts
|
|
232
|
+
* TransactionalModule.forRootAsync({
|
|
233
|
+
* inject: [ConfigService],
|
|
234
|
+
* useFactory: (config: ConfigService) => ({
|
|
235
|
+
* adapter: buildAdapterFromConfig(config),
|
|
236
|
+
* }),
|
|
237
|
+
* });
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
static forRootAsync(options: TransactionalModuleAsyncOptions): DynamicModule;
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=transactional.module.d.ts.map
|