@nestjs-transactional/typeorm 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.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +361 -0
  3. package/dist/adapter/typeorm.adapter.d.ts +39 -0
  4. package/dist/adapter/typeorm.adapter.js +79 -0
  5. package/dist/adapter/typeorm.adapter.js.map +1 -0
  6. package/dist/helpers/get-entity-manager.d.ts +27 -0
  7. package/dist/helpers/get-entity-manager.js +52 -0
  8. package/dist/helpers/get-entity-manager.js.map +1 -0
  9. package/dist/index.d.ts +5 -0
  10. package/dist/index.js +21 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/module/typeorm-transactional.module.d.ts +157 -0
  13. package/dist/module/typeorm-transactional.module.js +289 -0
  14. package/dist/module/typeorm-transactional.module.js.map +1 -0
  15. package/dist/patching/data-source-patches.d.ts +48 -0
  16. package/dist/patching/data-source-patches.js +139 -0
  17. package/dist/patching/data-source-patches.js.map +1 -0
  18. package/dist/patching/entity-manager-patches.d.ts +31 -0
  19. package/dist/patching/entity-manager-patches.js +86 -0
  20. package/dist/patching/entity-manager-patches.js.map +1 -0
  21. package/dist/patching/index.d.ts +49 -0
  22. package/dist/patching/index.js +75 -0
  23. package/dist/patching/index.js.map +1 -0
  24. package/dist/patching/managed-registry.d.ts +59 -0
  25. package/dist/patching/managed-registry.js +112 -0
  26. package/dist/patching/managed-registry.js.map +1 -0
  27. package/dist/patching/repository-patches.d.ts +56 -0
  28. package/dist/patching/repository-patches.js +150 -0
  29. package/dist/patching/repository-patches.js.map +1 -0
  30. package/dist/patching/symbols.d.ts +53 -0
  31. package/dist/patching/symbols.js +56 -0
  32. package/dist/patching/symbols.js.map +1 -0
  33. package/dist/types/typeorm-transaction-handle.d.ts +17 -0
  34. package/dist/types/typeorm-transaction-handle.js +3 -0
  35. package/dist/types/typeorm-transaction-handle.js.map +1 -0
  36. package/package.json +79 -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,361 @@
1
+ # @nestjs-transactional/typeorm
2
+
3
+ [![npm version](https://img.shields.io/npm/v/%40nestjs-transactional%2Ftypeorm/alpha?style=flat-square&label=npm)](https://www.npmjs.com/package/@nestjs-transactional/typeorm)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/igorgolovanov/nestjs-transactional/blob/main/LICENSE)
5
+
6
+ TypeORM adapter for [`@nestjs-transactional/core`](../core).
7
+
8
+ ## Overview
9
+
10
+ - `TypeOrmTransactionAdapter` — implements the core
11
+ `TransactionAdapter` SPI over TypeORM's `DataSource`. Handles
12
+ BEGIN / COMMIT / ROLLBACK via `DataSource.transaction(...)` and
13
+ issues raw `SAVEPOINT` / `ROLLBACK TO SAVEPOINT` /
14
+ `RELEASE SAVEPOINT` SQL for nested transactions.
15
+ - **Transparent transactional repositories** —
16
+ `@InjectRepository(Entity)` instances,
17
+ `@InjectEntityManager() em.getRepository(E)`,
18
+ `@InjectDataSource() ds.manager.save(...)`, and
19
+ `ds.getRepository(E).save(...)` automatically dispatch through the
20
+ active `@Transactional()` scope's `EntityManager`. No
21
+ `getCurrentEntityManager()` boilerplate. Custom repositories via
22
+ `Repository.extend(...)` and `TreeRepository` work transparently.
23
+ See [Transparent transactional behaviour](#transparent-transactional-behaviour)
24
+ below.
25
+ - `getCurrentEntityManager(dataSource?, fallback?)` — escape-hatch
26
+ helper that returns the transaction-aware `EntityManager` from the
27
+ current async context (or falls back to `dataSource.manager`
28
+ outside a transaction). Mostly needed for the documented
29
+ limitations below; standard injection paths cover everything else.
30
+ - `isInTransaction(dataSource?)` — predicate for the current
31
+ context.
32
+ - `TypeOrmTransactionalModule.forRoot({ dataSource?, isDefault? })` —
33
+ NestJS dynamic module that activates the transparent patches and
34
+ registers an adapter with the core `AdapterRegistry`. The
35
+ `DataSource` itself resolves from DI under
36
+ `getDataSourceToken(dataSource)` — the same convention
37
+ `@nestjs/typeorm` uses for `@InjectRepository(E, dataSource)`.
38
+ - `TypeOrmTransactionalModule.forRootAsync({ useFactory, inject?, imports? })`
39
+ — async variant for `ConfigService`-driven setups. Registers via
40
+ `OnModuleInit` to defer DataSource resolution past `@nestjs/typeorm`'s
41
+ async DataSource provider settling
42
+ ([Convention #22](../../docs/status/conventions.md)).
43
+ - Multi-DataSource: call `forRoot` once per dataSource. Mirrors
44
+ [`OutboxModule`](../outbox) (ADR-019) and
45
+ [`TransactionalModule`](../core).
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ pnpm add @nestjs-transactional/typeorm @nestjs-transactional/core typeorm @nestjs/typeorm reflect-metadata
51
+ ```
52
+
53
+ ## Quick start
54
+
55
+ Minimal single-DataSource setup:
56
+
57
+ ```ts
58
+ import { Module } from '@nestjs/common';
59
+ import { TypeOrmModule } from '@nestjs/typeorm';
60
+ import { TransactionalModule } from '@nestjs-transactional/core';
61
+ import { TypeOrmTransactionalModule } from '@nestjs-transactional/typeorm';
62
+
63
+ @Module({
64
+ imports: [
65
+ TypeOrmModule.forRoot({
66
+ type: 'postgres',
67
+ url: process.env.DATABASE_URL,
68
+ entities: [User],
69
+ synchronize: false,
70
+ }),
71
+ TypeOrmModule.forFeature([User]),
72
+
73
+ TransactionalModule.forRoot({ isGlobal: true }),
74
+ TypeOrmTransactionalModule.forRoot(),
75
+ ],
76
+ })
77
+ export class AppModule {}
78
+ ```
79
+
80
+ **Import order matters** — `TransactionalModule.forRoot({ isGlobal: true })`
81
+ must be present (with `isGlobal`) so the `AdapterRegistry` is visible
82
+ inside `TypeOrmTransactionalModule`'s DI scope. The actual
83
+ `DataSource` is resolved via `@nestjs/typeorm`'s
84
+ `getDataSourceToken(name)` — `TypeOrmModule.forRoot(...)` registers
85
+ it globally, so `TypeOrmTransactionalModule.forRoot` finds it
86
+ automatically.
87
+
88
+ ## Transparent transactional behaviour
89
+
90
+ Once the module is imported, every Repository reachable through the
91
+ standard `@nestjs/typeorm` injection paths automatically dispatches
92
+ through the active `@Transactional()` scope. No
93
+ `getCurrentEntityManager()` calls in user code:
94
+
95
+ ```ts
96
+ import { Injectable } from '@nestjs/common';
97
+ import { InjectRepository } from '@nestjs/typeorm';
98
+ import { Transactional } from '@nestjs-transactional/core';
99
+ import { Repository } from 'typeorm';
100
+ import { Order } from './order.entity';
101
+
102
+ @Injectable()
103
+ export class OrderService {
104
+ constructor(
105
+ @InjectRepository(Order)
106
+ private readonly orderRepo: Repository<Order>,
107
+ ) {}
108
+
109
+ @Transactional()
110
+ async placeOrder(dto: PlaceOrderDto): Promise<Order> {
111
+ // `orderRepo.save(...)` automatically uses the transactional
112
+ // EntityManager. If the method throws, the save rolls back.
113
+ // Outside a @Transactional scope, the same call autocommits.
114
+ return this.orderRepo.save(dto);
115
+ }
116
+ }
117
+ ```
118
+
119
+ Supported transparent patterns:
120
+
121
+ - `@InjectRepository(Entity) repo` — the headline case.
122
+ - `@InjectEntityManager() em.getRepository(E).save(...)`.
123
+ - `@InjectDataSource() ds.getRepository(E).save(...)`.
124
+ - `@InjectDataSource() ds.manager.save(Entity, ...)` (the patched
125
+ DataSource manager getter routes through the active EM).
126
+ - Custom repositories via `Repository.extend(...)`.
127
+ - `TreeRepository` and `MongoRepository` (inherit from `Repository`).
128
+
129
+ The mechanism is prototype-level patching modelled on the
130
+ `typeorm-transactional` library; patches install at module-load
131
+ time so they cover Repositories constructed by any DI factory,
132
+ regardless of resolution order.
133
+
134
+ ### Documented limitations
135
+
136
+ Two patterns are NOT covered by the patches and require an escape
137
+ hatch:
138
+
139
+ 1. **`@InjectEntityManager() em.save(Entity, ...)` direct call** is
140
+ NOT transactional. The patches cover `em.getRepository(E).save(...)`
141
+ (the typical pattern) but not direct method calls on the injected
142
+ `EntityManager`. Use the Repository pattern instead, or call
143
+ `getCurrentEntityManager()`:
144
+
145
+ ```ts
146
+ @Transactional()
147
+ async createUser(name: string) {
148
+ // Option A — Repository pattern (recommended).
149
+ return this.em.getRepository(User).save({ name });
150
+
151
+ // Option B — escape hatch.
152
+ // const em = getCurrentEntityManager();
153
+ // return em.save(User, { name });
154
+ }
155
+ ```
156
+
157
+ 2. **`BaseEntity` static methods** (`User.save(...)` etc.) are NOT
158
+ supported. The `BaseEntity.useDataSource(...)` API stores a
159
+ captured DataSource reference that bypasses the patches. Use the
160
+ Repository pattern.
161
+
162
+ The escape hatch:
163
+
164
+ ```ts
165
+ import { getCurrentEntityManager } from '@nestjs-transactional/typeorm';
166
+
167
+ @Injectable()
168
+ export class RawSqlService {
169
+ constructor(@InjectDataSource() private readonly ds: DataSource) {}
170
+
171
+ @Transactional()
172
+ async runRawSql() {
173
+ // Pass `ds` as fallback so the helper returns ds.manager when
174
+ // no transaction is active (autocommit). Inside a tx, returns
175
+ // the transactional EM.
176
+ const em = getCurrentEntityManager('default', this.ds);
177
+ await em.query(
178
+ 'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
179
+ [100, 1],
180
+ );
181
+ }
182
+ }
183
+ ```
184
+
185
+ ## Multi-DataSource
186
+
187
+ ```ts
188
+ @Module({
189
+ imports: [
190
+ TypeOrmModule.forRoot({ name: 'default', /* ... */ }),
191
+ TypeOrmModule.forRoot({ name: 'billing', /* ... */ }),
192
+
193
+ TransactionalModule.forRoot({ isGlobal: true }),
194
+ TypeOrmTransactionalModule.forRoot({ isDefault: true }), // 'default'
195
+ TypeOrmTransactionalModule.forRoot({ dataSource: 'billing' }), // 'billing'
196
+ ],
197
+ })
198
+ export class AppModule {}
199
+ ```
200
+
201
+ Target a specific dataSource in a transactional method:
202
+
203
+ ```ts
204
+ import { Transactional } from '@nestjs-transactional/core';
205
+
206
+ @Injectable()
207
+ export class BillingService {
208
+ constructor(
209
+ @InjectRepository(Invoice, 'billing')
210
+ private readonly invoiceRepo: Repository<Invoice>,
211
+ ) {}
212
+
213
+ @Transactional({ dataSource: 'billing' })
214
+ async chargeCard(/* ... */) {
215
+ // Repository is bound to 'billing' DS — saves go to billing.
216
+ return this.invoiceRepo.save(/* ... */);
217
+ }
218
+ }
219
+ ```
220
+
221
+ **Cross-DS isolation (DD-023)**: a Repository bound to dataSource A
222
+ inside a `@Transactional({ dataSource: 'B' })` method autocommits —
223
+ its patched `manager` getter looks up the active transaction for
224
+ dataSource A, finds none, and falls back to its captured original
225
+ manager. Distributed transactions across dataSources are explicitly
226
+ NOT supported; cross-DS atomicity goes through the outbox.
227
+
228
+ Each `forRoot` call registers its adapter under
229
+ `typeorm:${dataSource}` in the core `AdapterRegistry`.
230
+ `TransactionManager` routes based on `options.dataSource`.
231
+
232
+ ## Async configuration
233
+
234
+ ```ts
235
+ @Module({
236
+ imports: [
237
+ ConfigModule,
238
+ TypeOrmModule.forRootAsync({
239
+ inject: [ConfigService],
240
+ useFactory: (cfg: ConfigService) => ({
241
+ type: 'postgres',
242
+ url: cfg.get('DATABASE_URL'),
243
+ entities: [User],
244
+ }),
245
+ }),
246
+
247
+ TransactionalModule.forRoot({ isGlobal: true }),
248
+ TypeOrmTransactionalModule.forRootAsync({
249
+ imports: [ConfigModule],
250
+ inject: [ConfigService],
251
+ useFactory: (cfg: ConfigService) => ({
252
+ dataSource: cfg.get('DATA_SOURCE_NAME', 'default'),
253
+ isDefault: true,
254
+ }),
255
+ }),
256
+ ],
257
+ })
258
+ export class AppModule {}
259
+ ```
260
+
261
+ `forRootAsync` defers resolution of the dataSource name until the
262
+ factory runs. Per-DS DI tokens (`getTransactionalAdapterToken(ds)`)
263
+ are NOT registered in the async path because NestJS provider tokens
264
+ must be declared statically — if you need direct adapter injection
265
+ by per-DS token, use sync `forRoot({ dataSource })` instead.
266
+
267
+ The async path uses an `OnModuleInit`-driven registration class so
268
+ the DataSource resolves correctly even when paired with
269
+ `TypeOrmModule.forRootAsync` (whose own DataSource provider is
270
+ async). Pinned by
271
+ [`packages/typeorm/test/integration/forrootasync.integration.spec.ts`](test/integration/forrootasync.integration.spec.ts).
272
+
273
+ ## Testing
274
+
275
+ ### Unit tests — in-memory SQLite
276
+
277
+ For fast unit tests that don't need a real database, use TypeORM's
278
+ `sqljs` driver:
279
+
280
+ ```ts
281
+ import { DataSource } from 'typeorm';
282
+ import { TypeOrmTransactionAdapter } from '@nestjs-transactional/typeorm';
283
+
284
+ const ds = new DataSource({
285
+ type: 'sqljs',
286
+ synchronize: true,
287
+ entities: [YourEntity],
288
+ });
289
+ await ds.initialize();
290
+
291
+ const adapter = new TypeOrmTransactionAdapter(ds, 'default');
292
+ ```
293
+
294
+ ### Integration tests — testcontainers-node + real Postgres
295
+
296
+ Bundled helper for real Postgres integration:
297
+
298
+ ```ts
299
+ import {
300
+ startPostgresContainer,
301
+ stopPostgresContainer,
302
+ createAdditionalDatabase,
303
+ } from '@nestjs-transactional/typeorm/test/setup-testcontainers';
304
+
305
+ let ctx;
306
+ beforeAll(async () => {
307
+ ctx = await startPostgresContainer({ entities: [User], synchronize: true });
308
+ });
309
+ afterAll(async () => {
310
+ await stopPostgresContainer(ctx);
311
+ });
312
+
313
+ // Multi-DS: a second database inside the same container.
314
+ const secondary = await createAdditionalDatabase(ctx, 'billing_test', {
315
+ entities: [User],
316
+ synchronize: true,
317
+ });
318
+ ```
319
+
320
+ Run integration tests:
321
+
322
+ ```bash
323
+ pnpm --filter @nestjs-transactional/typeorm test:integration
324
+ ```
325
+
326
+ The bundled `docker-compose.yml` is for manual local use (`psql`
327
+ against a persistent instance). Testcontainers manages its own
328
+ containers and does not require compose.
329
+
330
+ ## Savepoints and NESTED propagation
331
+
332
+ When a method uses `PropagationMode.NESTED` from inside an existing
333
+ TypeORM transaction, the adapter issues a `SAVEPOINT sp_<uuid-30>`
334
+ statement. Rollback rolls back to the savepoint; the outer
335
+ transaction continues. Savepoint names are at most 33 characters
336
+ long — valid on Postgres, MySQL, MariaDB, SQLite, and Oracle's
337
+ identifier limit.
338
+
339
+ ## Worked examples
340
+
341
+ - [`basic-transactional`](../../examples/basic-transactional) —
342
+ `@Transactional()` on `@InjectRepository`, single DataSource.
343
+ Transparent repository showcase.
344
+ - [`multi-datasource-basic`](../../examples/multi-datasource-basic)
345
+ — two DataSources with `@Transactional({ dataSource })`, no
346
+ outbox.
347
+ - [`read-write-separation`](../../examples/read-write-separation) —
348
+ master + replica, only the master gets
349
+ `TypeOrmTransactionalModule`.
350
+ - [`async-config-from-environment`](../../examples/async-config-from-environment)
351
+ — `TypeOrmTransactionalModule.forRootAsync` end-to-end with
352
+ `ConfigService` + Joi profiles.
353
+ - [`e-commerce-orders`](../../examples/e-commerce-orders) —
354
+ three-DataSource flagship combining transparent repositories +
355
+ per-DS outbox + CQRS + REST + Kafka externalization.
356
+
357
+ Full catalogue: [examples/README.md](../../examples/README.md).
358
+
359
+ ## Status
360
+
361
+ Alpha. Public API may change between 0.x releases.
@@ -0,0 +1,39 @@
1
+ import type { TransactionAdapter, TransactionOptions } from '@nestjs-transactional/core';
2
+ import type { DataSource } from 'typeorm';
3
+ import type { TypeOrmTransactionHandle } from '../types/typeorm-transaction-handle';
4
+ /**
5
+ * TypeORM implementation of {@link TransactionAdapter}. Delegates begin /
6
+ * commit / rollback to `DataSource.transaction` and emits raw `SAVEPOINT`
7
+ * SQL for nested transactions through the transactional
8
+ * {@link EntityManager}.
9
+ *
10
+ * Only `isolation` from {@link TransactionOptions} is forwarded today.
11
+ * `readOnly` and `timeout` are accepted for forward compatibility but do
12
+ * not yet map to per-dialect statements.
13
+ */
14
+ export declare class TypeOrmTransactionAdapter implements TransactionAdapter<TypeOrmTransactionHandle> {
15
+ private readonly dataSource;
16
+ /**
17
+ * Adapter instance name this adapter was created for (e.g.
18
+ * `'primary'`, `'billing'`). Exposed so observability / diagnostics
19
+ * can correlate events back to the registered instance.
20
+ */
21
+ readonly instanceName: string;
22
+ readonly name = "typeorm";
23
+ constructor(dataSource: DataSource,
24
+ /**
25
+ * Adapter instance name this adapter was created for (e.g.
26
+ * `'primary'`, `'billing'`). Exposed so observability / diagnostics
27
+ * can correlate events back to the registered instance.
28
+ */
29
+ instanceName: string);
30
+ /**
31
+ * Public dataSource name (DD-021). For TypeORM the dataSource name
32
+ * IS the adapter instance name — the constructor's `instanceName`
33
+ * argument is the single user-supplied identifier.
34
+ */
35
+ get dataSourceName(): string;
36
+ runInTransaction<T>(options: TransactionOptions, fn: (handle: TypeOrmTransactionHandle) => Promise<T>): Promise<T>;
37
+ runInSavepoint<T>(parent: TypeOrmTransactionHandle, fn: (handle: TypeOrmTransactionHandle) => Promise<T>): Promise<T>;
38
+ }
39
+ //# sourceMappingURL=typeorm.adapter.d.ts.map
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypeOrmTransactionAdapter = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ /**
6
+ * TypeORM implementation of {@link TransactionAdapter}. Delegates begin /
7
+ * commit / rollback to `DataSource.transaction` and emits raw `SAVEPOINT`
8
+ * SQL for nested transactions through the transactional
9
+ * {@link EntityManager}.
10
+ *
11
+ * Only `isolation` from {@link TransactionOptions} is forwarded today.
12
+ * `readOnly` and `timeout` are accepted for forward compatibility but do
13
+ * not yet map to per-dialect statements.
14
+ */
15
+ class TypeOrmTransactionAdapter {
16
+ dataSource;
17
+ instanceName;
18
+ name = 'typeorm';
19
+ constructor(dataSource,
20
+ /**
21
+ * Adapter instance name this adapter was created for (e.g.
22
+ * `'primary'`, `'billing'`). Exposed so observability / diagnostics
23
+ * can correlate events back to the registered instance.
24
+ */
25
+ instanceName) {
26
+ this.dataSource = dataSource;
27
+ this.instanceName = instanceName;
28
+ }
29
+ /**
30
+ * Public dataSource name (DD-021). For TypeORM the dataSource name
31
+ * IS the adapter instance name — the constructor's `instanceName`
32
+ * argument is the single user-supplied identifier.
33
+ */
34
+ get dataSourceName() {
35
+ return this.instanceName;
36
+ }
37
+ async runInTransaction(options, fn) {
38
+ const isolation = mapIsolation(options.isolation);
39
+ const runner = async (entityManager) => {
40
+ const handle = {
41
+ id: (0, node_crypto_1.randomUUID)(),
42
+ adapterName: this.name,
43
+ entityManager,
44
+ };
45
+ return fn(handle);
46
+ };
47
+ if (isolation !== undefined) {
48
+ return this.dataSource.transaction(isolation, runner);
49
+ }
50
+ return this.dataSource.transaction(runner);
51
+ }
52
+ async runInSavepoint(parent, fn) {
53
+ const savepointName = `sp_${(0, node_crypto_1.randomUUID)().replace(/-/g, '_').substring(0, 30)}`;
54
+ await parent.entityManager.query(`SAVEPOINT ${savepointName}`);
55
+ try {
56
+ const result = await fn(parent);
57
+ await parent.entityManager.query(`RELEASE SAVEPOINT ${savepointName}`);
58
+ return result;
59
+ }
60
+ catch (err) {
61
+ await parent.entityManager.query(`ROLLBACK TO SAVEPOINT ${savepointName}`);
62
+ throw err;
63
+ }
64
+ }
65
+ }
66
+ exports.TypeOrmTransactionAdapter = TypeOrmTransactionAdapter;
67
+ /**
68
+ * Map the core's underscore-style {@link IsolationLevel} to TypeORM's
69
+ * space-separated string. Returns `undefined` when no level is set so the
70
+ * caller can invoke the `DataSource.transaction` overload that leaves the
71
+ * database default in place.
72
+ */
73
+ function mapIsolation(level) {
74
+ if (level === undefined) {
75
+ return undefined;
76
+ }
77
+ return level.replace(/_/g, ' ');
78
+ }
79
+ //# sourceMappingURL=typeorm.adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeorm.adapter.js","sourceRoot":"","sources":["../../src/adapter/typeorm.adapter.ts"],"names":[],"mappings":";;;AAAA,6CAAyC;AAsBzC;;;;;;;;;GASG;AACH,MAAa,yBAAyB;IAIjB;IAMR;IATF,IAAI,GAAG,SAAS,CAAC;IAE1B,YACmB,UAAsB;IACvC;;;;OAIG;IACM,YAAoB;QANZ,eAAU,GAAV,UAAU,CAAY;QAM9B,iBAAY,GAAZ,YAAY,CAAQ;IAC5B,CAAC;IAEJ;;;;OAIG;IACH,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,gBAAgB,CACpB,OAA2B,EAC3B,EAAoD;QAEpD,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAElD,MAAM,MAAM,GAAG,KAAK,EAAE,aAA4B,EAAc,EAAE;YAChE,MAAM,MAAM,GAA6B;gBACvC,EAAE,EAAE,IAAA,wBAAU,GAAE;gBAChB,WAAW,EAAE,IAAI,CAAC,IAAI;gBACtB,aAAa;aACd,CAAC;YACF,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC,CAAC;QAEF,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,MAAgC,EAChC,EAAoD;QAEpD,MAAM,aAAa,GAAG,MAAM,IAAA,wBAAU,GAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QAE/E,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,aAAa,aAAa,EAAE,CAAC,CAAC;QAE/D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YAChC,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,qBAAqB,aAAa,EAAE,CAAC,CAAC;YACvE,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,yBAAyB,aAAa,EAAE,CAAC,CAAC;YAC3E,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AA5DD,8DA4DC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,KAAiC;IACrD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAA0B,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { DataSource, EntityManager } from 'typeorm';
2
+ /**
3
+ * Return the TypeORM {@link EntityManager} bound to the currently active
4
+ * transaction on the given adapter instance. Repositories and services
5
+ * call this to stay inside the surrounding `@Transactional` scope without
6
+ * having to thread the EntityManager through their arguments.
7
+ *
8
+ * Resolution order:
9
+ * 1. If a transaction is active on `typeorm:${adapterInstance}`, return
10
+ * its EntityManager — every write goes through the transaction.
11
+ * 2. Otherwise, if `fallback` is provided, return `fallback.manager` —
12
+ * writes execute autocommit.
13
+ * 3. Otherwise throw {@link IllegalTransactionStateError}. Passing no
14
+ * fallback is a deliberate assertion that the caller MUST be inside
15
+ * a transaction.
16
+ *
17
+ * @param adapterInstance - Adapter instance name. Defaults to `'default'`.
18
+ * @param fallback - DataSource used when no transaction is active.
19
+ */
20
+ export declare function getCurrentEntityManager(adapterInstance?: string, fallback?: DataSource): EntityManager;
21
+ /**
22
+ * Predicate: is there an active TypeORM transaction on this adapter
23
+ * instance? Useful to guard side effects that must only fire inside a
24
+ * transaction.
25
+ */
26
+ export declare function isInTransaction(adapterInstance?: string): boolean;
27
+ //# sourceMappingURL=get-entity-manager.d.ts.map
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCurrentEntityManager = getCurrentEntityManager;
4
+ exports.isInTransaction = isInTransaction;
5
+ const core_1 = require("@nestjs-transactional/core");
6
+ /**
7
+ * Compose the `TransactionContext` lookup key for the TypeORM adapter.
8
+ * Must match the `${adapterName}:${instanceName}` format that
9
+ * `TransactionManager` in core writes under. Keeping it private forces
10
+ * all helper call-sites to go through the same builder.
11
+ */
12
+ function typeOrmContextKey(adapterInstance) {
13
+ return `typeorm:${adapterInstance}`;
14
+ }
15
+ /**
16
+ * Return the TypeORM {@link EntityManager} bound to the currently active
17
+ * transaction on the given adapter instance. Repositories and services
18
+ * call this to stay inside the surrounding `@Transactional` scope without
19
+ * having to thread the EntityManager through their arguments.
20
+ *
21
+ * Resolution order:
22
+ * 1. If a transaction is active on `typeorm:${adapterInstance}`, return
23
+ * its EntityManager — every write goes through the transaction.
24
+ * 2. Otherwise, if `fallback` is provided, return `fallback.manager` —
25
+ * writes execute autocommit.
26
+ * 3. Otherwise throw {@link IllegalTransactionStateError}. Passing no
27
+ * fallback is a deliberate assertion that the caller MUST be inside
28
+ * a transaction.
29
+ *
30
+ * @param adapterInstance - Adapter instance name. Defaults to `'default'`.
31
+ * @param fallback - DataSource used when no transaction is active.
32
+ */
33
+ function getCurrentEntityManager(adapterInstance = 'default', fallback) {
34
+ const active = core_1.TransactionContext.getActiveTransaction(typeOrmContextKey(adapterInstance));
35
+ if (active !== undefined) {
36
+ return active.handle.entityManager;
37
+ }
38
+ if (fallback !== undefined) {
39
+ return fallback.manager;
40
+ }
41
+ throw new core_1.IllegalTransactionStateError(`No active transaction for 'typeorm:${adapterInstance}' and no fallback DataSource ` +
42
+ `provided. Either wrap the call with @Transactional() or pass a DataSource as fallback.`);
43
+ }
44
+ /**
45
+ * Predicate: is there an active TypeORM transaction on this adapter
46
+ * instance? Useful to guard side effects that must only fire inside a
47
+ * transaction.
48
+ */
49
+ function isInTransaction(adapterInstance = 'default') {
50
+ return core_1.TransactionContext.getActiveTransaction(typeOrmContextKey(adapterInstance)) !== undefined;
51
+ }
52
+ //# sourceMappingURL=get-entity-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-entity-manager.js","sourceRoot":"","sources":["../../src/helpers/get-entity-manager.ts"],"names":[],"mappings":";;AAiCA,0DAkBC;AAOD,0CAEC;AA5DD,qDAA8F;AAK9F;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,eAAuB;IAChD,OAAO,WAAW,eAAe,EAAE,CAAC;AACtC,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,SAAgB,uBAAuB,CACrC,eAAe,GAAG,SAAS,EAC3B,QAAqB;IAErB,MAAM,MAAM,GAAG,yBAAkB,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC,CAAC;IAE3F,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAQ,MAAM,CAAC,MAAmC,CAAC,aAAa,CAAC;IACnE,CAAC;IAED,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,QAAQ,CAAC,OAAO,CAAC;IAC1B,CAAC;IAED,MAAM,IAAI,mCAA4B,CACpC,sCAAsC,eAAe,+BAA+B;QAClF,wFAAwF,CAC3F,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,eAAe,GAAG,SAAS;IACzD,OAAO,yBAAkB,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC,KAAK,SAAS,CAAC;AACnG,CAAC"}
@@ -0,0 +1,5 @@
1
+ export * from './types/typeorm-transaction-handle';
2
+ export * from './adapter/typeorm.adapter';
3
+ export * from './helpers/get-entity-manager';
4
+ export * from './module/typeorm-transactional.module';
5
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types/typeorm-transaction-handle"), exports);
18
+ __exportStar(require("./adapter/typeorm.adapter"), exports);
19
+ __exportStar(require("./helpers/get-entity-manager"), exports);
20
+ __exportStar(require("./module/typeorm-transactional.module"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qEAAmD;AACnD,4DAA0C;AAC1C,+DAA6C;AAC7C,wEAAsD"}