@nestjs-transactional/typeorm 1.0.0-alpha.3 → 1.0.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/README.md CHANGED
@@ -1,76 +1,41 @@
1
1
  # @nestjs-transactional/typeorm
2
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)
3
+ [![npm version](https://img.shields.io/npm/v/%40nestjs-transactional%2Ftypeorm?style=flat-square&label=npm)](https://www.npmjs.com/package/@nestjs-transactional/typeorm)
4
4
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/igorgolovanov/nestjs-transactional/blob/main/LICENSE)
5
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
6
+ TypeORM adapter for
7
+ [`@nestjs-transactional/core`](https://www.npmjs.com/package/@nestjs-transactional/core).
48
8
 
49
- ```bash
50
- pnpm add @nestjs-transactional/typeorm @nestjs-transactional/core typeorm @nestjs/typeorm reflect-metadata
9
+ Two things come with it. The adapter itself, which maps `@Transactional()`
10
+ onto TypeORM's transactions and savepoints and **transparent
11
+ transactional repositories**: your existing `@InjectRepository(Order)`
12
+ instances start honouring the active transaction on their own, with no
13
+ change to the code that uses them.
14
+
15
+ ```ts
16
+ @Injectable()
17
+ export class OrderService {
18
+ constructor(@InjectRepository(Order) private readonly orders: Repository<Order>) {}
19
+
20
+ @Transactional()
21
+ async place(dto: PlaceOrderDto) {
22
+ // Runs in the transaction. Rolls back if anything below throws.
23
+ // Outside a @Transactional method, the same call autocommits.
24
+ return this.orders.save(dto);
25
+ }
26
+ }
51
27
  ```
52
28
 
53
- ## Compatibility
29
+ No `getCurrentEntityManager()`, no passing an `EntityManager` down
30
+ through service layers, no separate "transactional" repository type.
54
31
 
55
- | Peer | Supported range |
56
- | ----------------------------------- | -------------------------- |
57
- | Node.js | `>=22.13.0` |
58
- | `typeorm` | `^0.3.0 \|\| ^1.0.0` |
59
- | `@nestjs/typeorm` | `^10.0.0 \|\| ^11.0.0` |
60
- | `@nestjs/common` / `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` |
61
- | `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` |
62
- | `rxjs` | `^7.0.0` |
63
-
64
- The TypeORM range covers both stable `0.3.x` and stable `1.x`
65
- releases. CI runs the full unit and integration matrix
66
- (testcontainers Postgres) against both TypeORM majors, so the
67
- adapter is exercised end-to-end on every supported peer. TypeORM
68
- nightly / beta builds are not in the declared range; install them
69
- explicitly via `pnpm.overrides` if you need to pin to one.
32
+ ## Install
70
33
 
71
- ## Quick start
34
+ ```bash
35
+ pnpm add @nestjs-transactional/typeorm @nestjs-transactional/core typeorm @nestjs/typeorm reflect-metadata
36
+ ```
72
37
 
73
- Minimal single-DataSource setup:
38
+ ## Quick start
74
39
 
75
40
  ```ts
76
41
  import { Module } from '@nestjs/common';
@@ -80,13 +45,8 @@ import { TypeOrmTransactionalModule } from '@nestjs-transactional/typeorm';
80
45
 
81
46
  @Module({
82
47
  imports: [
83
- TypeOrmModule.forRoot({
84
- type: 'postgres',
85
- url: process.env.DATABASE_URL,
86
- entities: [User],
87
- synchronize: false,
88
- }),
89
- TypeOrmModule.forFeature([User]),
48
+ TypeOrmModule.forRoot({ type: 'postgres', entities: [Order] }),
49
+ TypeOrmModule.forFeature([Order]),
90
50
 
91
51
  TransactionalModule.forRoot({ isGlobal: true }),
92
52
  TypeOrmTransactionalModule.forRoot(),
@@ -95,285 +55,153 @@ import { TypeOrmTransactionalModule } from '@nestjs-transactional/typeorm';
95
55
  export class AppModule {}
96
56
  ```
97
57
 
98
- **Import order matters** — `TransactionalModule.forRoot({ isGlobal: true })`
99
- must be present (with `isGlobal`) so the `AdapterRegistry` is visible
100
- inside `TypeOrmTransactionalModule`'s DI scope. The actual
101
- `DataSource` is resolved via `@nestjs/typeorm`'s
102
- `getDataSourceToken(name)` `TypeOrmModule.forRoot(...)` registers
103
- it globally, so `TypeOrmTransactionalModule.forRoot` finds it
104
- automatically.
58
+ `TransactionalModule.forRoot({ isGlobal: true })` has to be there, with
59
+ `isGlobal` that is how this module sees the core registry from its own
60
+ DI scope. The `DataSource` is found through `@nestjs/typeorm`'s
61
+ `getDataSourceToken(name)`, the same convention
62
+ `@InjectRepository(E, dataSource)` uses, so nothing else needs wiring.
105
63
 
106
- ## Transparent transactional behaviour
64
+ ## What becomes transparent
107
65
 
108
- Once the module is imported, every Repository reachable through the
109
- standard `@nestjs/typeorm` injection paths automatically dispatches
110
- through the active `@Transactional()` scope. No
111
- `getCurrentEntityManager()` calls in user code:
66
+ These all dispatch through the active transaction:
112
67
 
113
- ```ts
114
- import { Injectable } from '@nestjs/common';
115
- import { InjectRepository } from '@nestjs/typeorm';
116
- import { Transactional } from '@nestjs-transactional/core';
117
- import { Repository } from 'typeorm';
118
- import { Order } from './order.entity';
68
+ - `@InjectRepository(Entity)` — the common case.
69
+ - `@InjectEntityManager() em.getRepository(E)`.
70
+ - `@InjectDataSource() ds.getRepository(E)` and `ds.manager`.
71
+ - Custom repositories built with `Repository.extend(...)`.
72
+ - `TreeRepository` and `MongoRepository`, which inherit from `Repository`.
119
73
 
120
- @Injectable()
121
- export class OrderService {
122
- constructor(
123
- @InjectRepository(Order)
124
- private readonly orderRepo: Repository<Order>,
125
- ) {}
74
+ Two patterns are **not** covered, and need an escape hatch:
126
75
 
127
- @Transactional()
128
- async placeOrder(dto: PlaceOrderDto): Promise<Order> {
129
- // `orderRepo.save(...)` automatically uses the transactional
130
- // EntityManager. If the method throws, the save rolls back.
131
- // Outside a @Transactional scope, the same call autocommits.
132
- return this.orderRepo.save(dto);
133
- }
134
- }
135
- ```
136
-
137
- Supported transparent patterns:
138
-
139
- - `@InjectRepository(Entity) repo` — the headline case.
140
- - `@InjectEntityManager() em.getRepository(E).save(...)`.
141
- - `@InjectDataSource() ds.getRepository(E).save(...)`.
142
- - `@InjectDataSource() ds.manager.save(Entity, ...)` (the patched
143
- DataSource manager getter routes through the active EM).
144
- - Custom repositories via `Repository.extend(...)`.
145
- - `TreeRepository` and `MongoRepository` (inherit from `Repository`).
146
-
147
- The mechanism is prototype-level patching modelled on the
148
- `typeorm-transactional` library; patches install at module-load
149
- time so they cover Repositories constructed by any DI factory,
150
- regardless of resolution order.
151
-
152
- ### Documented limitations
153
-
154
- Two patterns are NOT covered by the patches and require an escape
155
- hatch:
76
+ 1. **`em.save(Entity, ...)` called directly** on an injected
77
+ `EntityManager`. The patch covers `em.getRepository(E).save(...)`,
78
+ not the manager's own data methods — patching all ~14 of them would
79
+ require per-method recursion guards, which was judged not worth the
80
+ surface area.
81
+ 2. **`BaseEntity` static methods** (`User.save(...)`).
82
+ `BaseEntity.useDataSource(...)` captures a `DataSource` reference
83
+ that bypasses the patch. The library `typeorm-transactional` has the
84
+ same limitation.
156
85
 
157
- 1. **`@InjectEntityManager() em.save(Entity, ...)` direct call** is
158
- NOT transactional. The patches cover `em.getRepository(E).save(...)`
159
- (the typical pattern) but not direct method calls on the injected
160
- `EntityManager`. Use the Repository pattern instead, or call
161
- `getCurrentEntityManager()`:
162
-
163
- ```ts
164
- @Transactional()
165
- async createUser(name: string) {
166
- // Option A — Repository pattern (recommended).
167
- return this.em.getRepository(User).save({ name });
168
-
169
- // Option B — escape hatch.
170
- // const em = getCurrentEntityManager();
171
- // return em.save(User, { name });
172
- }
173
- ```
174
-
175
- 2. **`BaseEntity` static methods** (`User.save(...)` etc.) are NOT
176
- supported. The `BaseEntity.useDataSource(...)` API stores a
177
- captured DataSource reference that bypasses the patches. Use the
178
- Repository pattern.
179
-
180
- The escape hatch:
86
+ For both, either use a repository or reach for the escape hatch:
181
87
 
182
88
  ```ts
183
89
  import { getCurrentEntityManager } from '@nestjs-transactional/typeorm';
184
90
 
185
- @Injectable()
186
- export class RawSqlService {
187
- constructor(@InjectDataSource() private readonly ds: DataSource) {}
188
-
189
- @Transactional()
190
- async runRawSql() {
191
- // Pass `ds` as fallback so the helper returns ds.manager when
192
- // no transaction is active (autocommit). Inside a tx, returns
193
- // the transactional EM.
194
- const em = getCurrentEntityManager('default', this.ds);
195
- await em.query(
196
- 'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
197
- [100, 1],
198
- );
199
- }
91
+ @Transactional()
92
+ async runRawSql() {
93
+ // Pass the DataSource as fallback so this also works outside a
94
+ // transaction, where it returns ds.manager.
95
+ const em = getCurrentEntityManager('default', this.ds);
96
+ await em.query('UPDATE accounts SET balance = balance - $1', [100]);
200
97
  }
201
98
  ```
202
99
 
203
- ## Multi-DataSource
100
+ ## Dialect-dependent behaviour
204
101
 
205
- ```ts
206
- @Module({
207
- imports: [
208
- TypeOrmModule.forRoot({ name: 'default', /* ... */ }),
209
- TypeOrmModule.forRoot({ name: 'billing', /* ... */ }),
102
+ Two options behave differently per database, and both fail loudly or
103
+ harmlessly rather than surprisingly:
210
104
 
211
- TransactionalModule.forRoot({ isGlobal: true }),
212
- TypeOrmTransactionalModule.forRoot({ isDefault: true }), // 'default'
213
- TypeOrmTransactionalModule.forRoot({ dataSource: 'billing' }), // 'billing'
214
- ],
215
- })
216
- export class AppModule {}
217
- ```
105
+ - **`readOnly`** is enforced on `postgres`, `cockroachdb` and
106
+ `aurora-postgres`, where the adapter issues `SET TRANSACTION READ ONLY`
107
+ as the transaction's first statement and the database refuses a write.
108
+ On other dialects it is a silent no-op. MySQL is not merely
109
+ unimplemented but unimplementable: `SET TRANSACTION` there applies to
110
+ the *next* transaction and errors inside a started one. Worth knowing
111
+ if you develop on SQLite and deploy to Postgres — the constraint
112
+ appears in production for the first time.
113
+ ([DD-027](https://github.com/igorgolovanov/nestjs-transactional/blob/main/docs/dd/027-readonly-and-timeout-semantics.md))
114
+ - **`PropagationMode.NESTED`** needs savepoints. The adapter checks
115
+ TypeORM's own `driver.transactionSupport` flag and throws
116
+ `IllegalTransactionStateError` naming the driver and the alternatives,
117
+ instead of running your "nested" transaction as part of the outer one.
118
+
119
+ ## Multiple dataSources
218
120
 
219
- Target a specific dataSource in a transactional method:
121
+ One `forRoot` call per dataSource:
220
122
 
221
123
  ```ts
222
- import { Transactional } from '@nestjs-transactional/core';
124
+ TypeOrmTransactionalModule.forRoot({ isDefault: true }), // 'default'
125
+ TypeOrmTransactionalModule.forRoot({ dataSource: 'billing' }), // 'billing'
126
+ ```
223
127
 
224
- @Injectable()
225
- export class BillingService {
226
- constructor(
227
- @InjectRepository(Invoice, 'billing')
228
- private readonly invoiceRepo: Repository<Invoice>,
229
- ) {}
230
-
231
- @Transactional({ dataSource: 'billing' })
232
- async chargeCard(/* ... */) {
233
- // Repository is bound to 'billing' DS — saves go to billing.
234
- return this.invoiceRepo.save(/* ... */);
235
- }
128
+ ```ts
129
+ @Transactional({ dataSource: 'billing' })
130
+ async chargeCard() {
131
+ return this.invoiceRepo.save(/* ... */); // repo bound to 'billing'
236
132
  }
237
133
  ```
238
134
 
239
- **Cross-DS isolation (DD-023)**: a Repository bound to dataSource A
240
- inside a `@Transactional({ dataSource: 'B' })` method autocommits
241
- its patched `manager` getter looks up the active transaction for
242
- dataSource A, finds none, and falls back to its captured original
243
- manager. Distributed transactions across dataSources are explicitly
244
- NOT supported; cross-DS atomicity goes through the outbox.
245
-
246
- Each `forRoot` call registers its adapter under
247
- `typeorm:${dataSource}` in the core `AdapterRegistry`.
248
- `TransactionManager` routes based on `options.dataSource`.
135
+ A repository bound to dataSource A, used inside a
136
+ `@Transactional({ dataSource: 'B' })` method, autocommits: its patched
137
+ manager looks for an active transaction on A, finds none, and falls back
138
+ to its original manager. **Distributed transactions across dataSources
139
+ are not supported** that is deliberate, and cross-dataSource atomicity
140
+ is what the outbox is for.
249
141
 
250
142
  ## Async configuration
251
143
 
252
144
  ```ts
253
- @Module({
254
- imports: [
255
- ConfigModule,
256
- TypeOrmModule.forRootAsync({
257
- inject: [ConfigService],
258
- useFactory: (cfg: ConfigService) => ({
259
- type: 'postgres',
260
- url: cfg.get('DATABASE_URL'),
261
- entities: [User],
262
- }),
263
- }),
264
-
265
- TransactionalModule.forRoot({ isGlobal: true }),
266
- TypeOrmTransactionalModule.forRootAsync({
267
- imports: [ConfigModule],
268
- inject: [ConfigService],
269
- useFactory: (cfg: ConfigService) => ({
270
- dataSource: cfg.get('DATA_SOURCE_NAME', 'default'),
271
- isDefault: true,
272
- }),
273
- }),
274
- ],
275
- })
276
- export class AppModule {}
145
+ TypeOrmTransactionalModule.forRootAsync({
146
+ imports: [ConfigModule],
147
+ inject: [ConfigService],
148
+ useFactory: (cfg: ConfigService) => ({
149
+ dataSource: cfg.get('DATA_SOURCE_NAME', 'default'),
150
+ isDefault: true,
151
+ }),
152
+ });
277
153
  ```
278
154
 
279
- `forRootAsync` defers resolution of the dataSource name until the
280
- factory runs. Per-DS DI tokens (`getTransactionalAdapterToken(ds)`)
281
- are NOT registered in the async path because NestJS provider tokens
282
- must be declared statically if you need direct adapter injection
283
- by per-DS token, use sync `forRoot({ dataSource })` instead.
155
+ Registration is deferred to `OnModuleInit` so the dataSource resolves
156
+ correctly even when paired with `TypeOrmModule.forRootAsync`. Per-dataSource
157
+ adapter tokens are not registered on this path, because NestJS needs
158
+ provider tokens at module-definition time; use sync `forRoot({ dataSource })`
159
+ if you inject adapters by token.
284
160
 
285
- The async path uses an `OnModuleInit`-driven registration class so
286
- the DataSource resolves correctly even when paired with
287
- `TypeOrmModule.forRootAsync` (whose own DataSource provider is
288
- async). Pinned by
289
- [`packages/typeorm/test/integration/forrootasync.integration.spec.ts`](test/integration/forrootasync.integration.spec.ts).
161
+ ## Compatibility
290
162
 
291
- ## Testing
163
+ | Peer | Supported range |
164
+ | --- | --- |
165
+ | Node.js | `>=22.13.0` |
166
+ | `typeorm` | `^0.3.0 \|\| ^1.0.0` |
167
+ | `@nestjs/typeorm` | `^10.0.0 \|\| ^11.0.0` |
168
+ | `@nestjs/common` / `@nestjs/core` | `^10.0.0 \|\| ^11.0.0` |
169
+ | `reflect-metadata` | `^0.1.13 \|\| ^0.2.0` |
170
+ | `rxjs` | `^7.0.0` |
292
171
 
293
- ### Unit tests in-memory SQLite
172
+ Both stable TypeORM lines are supported. CI runs the full unit and
173
+ integration matrix — including savepoints and isolation against a real
174
+ Postgres — at three points of that range: `0.3.31`, `1.0.0` and `1.1.0`.
294
175
 
295
- For fast unit tests that don't need a real database, use TypeORM's
296
- `sqljs` driver:
176
+ ## Testing
297
177
 
298
- ```ts
299
- import { DataSource } from 'typeorm';
300
- import { TypeOrmTransactionAdapter } from '@nestjs-transactional/typeorm';
178
+ For unit tests, TypeORM's in-memory `sqljs` driver is enough:
301
179
 
302
- const ds = new DataSource({
303
- type: 'sqljs',
304
- synchronize: true,
305
- entities: [YourEntity],
306
- });
180
+ ```ts
181
+ const ds = new DataSource({ type: 'sqljs', synchronize: true, entities: [Order] });
307
182
  await ds.initialize();
308
-
309
183
  const adapter = new TypeOrmTransactionAdapter(ds, 'default');
310
184
  ```
311
185
 
312
- ### Integration tests testcontainers-node + real Postgres
186
+ Note that `readOnly` is not enforced on `sqljs`, so a read-only
187
+ violation your tests miss can still surface on Postgres. For tests that
188
+ need real dialect behaviour, run Postgres through
189
+ [testcontainers](https://node.testcontainers.org/); the
190
+ [`testing-patterns`](https://github.com/igorgolovanov/nestjs-transactional/tree/main/examples/testing-patterns)
191
+ example shows both layers.
313
192
 
314
- Bundled helper for real Postgres integration:
193
+ ## Documentation
315
194
 
316
- ```ts
317
- import {
318
- startPostgresContainer,
319
- stopPostgresContainer,
320
- createAdditionalDatabase,
321
- } from '@nestjs-transactional/typeorm/test/setup-testcontainers';
322
-
323
- let ctx;
324
- beforeAll(async () => {
325
- ctx = await startPostgresContainer({ entities: [User], synchronize: true });
326
- });
327
- afterAll(async () => {
328
- await stopPostgresContainer(ctx);
329
- });
195
+ - [Getting started and full docs](https://github.com/igorgolovanov/nestjs-transactional#readme)
196
+ - [`readOnly` and `timeout` semantics (DD-027)](https://github.com/igorgolovanov/nestjs-transactional/blob/main/docs/dd/027-readonly-and-timeout-semantics.md)
197
+ - [Multi-adapter architecture (ADR-018)](https://github.com/igorgolovanov/nestjs-transactional/blob/main/docs/adr/018-multi-adapter-architecture.md)
198
+ - [Known limitations](https://github.com/igorgolovanov/nestjs-transactional/blob/main/docs/known-limitations.md)
199
+ - Runnable examples:
200
+ [`basic-transactional`](https://github.com/igorgolovanov/nestjs-transactional/tree/main/examples/basic-transactional),
201
+ [`multi-datasource-basic`](https://github.com/igorgolovanov/nestjs-transactional/tree/main/examples/multi-datasource-basic),
202
+ [`read-write-separation`](https://github.com/igorgolovanov/nestjs-transactional/tree/main/examples/read-write-separation),
203
+ [`e-commerce-orders`](https://github.com/igorgolovanov/nestjs-transactional/tree/main/examples/e-commerce-orders)
330
204
 
331
- // Multi-DS: a second database inside the same container.
332
- const secondary = await createAdditionalDatabase(ctx, 'billing_test', {
333
- entities: [User],
334
- synchronize: true,
335
- });
336
- ```
337
-
338
- Run integration tests:
339
-
340
- ```bash
341
- pnpm --filter @nestjs-transactional/typeorm test:integration
342
- ```
205
+ ## License
343
206
 
344
- The bundled `docker-compose.yml` is for manual local use (`psql`
345
- against a persistent instance). Testcontainers manages its own
346
- containers and does not require compose.
347
-
348
- ## Savepoints and NESTED propagation
349
-
350
- When a method uses `PropagationMode.NESTED` from inside an existing
351
- TypeORM transaction, the adapter issues a `SAVEPOINT sp_<uuid-30>`
352
- statement. Rollback rolls back to the savepoint; the outer
353
- transaction continues. Savepoint names are at most 33 characters
354
- long — valid on Postgres, MySQL, MariaDB, SQLite, and Oracle's
355
- identifier limit.
356
-
357
- ## Worked examples
358
-
359
- - [`basic-transactional`](../../examples/basic-transactional) —
360
- `@Transactional()` on `@InjectRepository`, single DataSource.
361
- Transparent repository showcase.
362
- - [`multi-datasource-basic`](../../examples/multi-datasource-basic)
363
- — two DataSources with `@Transactional({ dataSource })`, no
364
- outbox.
365
- - [`read-write-separation`](../../examples/read-write-separation) —
366
- master + replica, only the master gets
367
- `TypeOrmTransactionalModule`.
368
- - [`async-config-from-environment`](../../examples/async-config-from-environment)
369
- — `TypeOrmTransactionalModule.forRootAsync` end-to-end with
370
- `ConfigService` + Joi profiles.
371
- - [`e-commerce-orders`](../../examples/e-commerce-orders) —
372
- three-DataSource flagship combining transparent repositories +
373
- per-DS outbox + CQRS + REST + Kafka externalization.
374
-
375
- Full catalogue: [examples/README.md](../../examples/README.md).
376
-
377
- ## Status
378
-
379
- Alpha. Public API may change between 0.x releases.
207
+ MIT
@@ -1,4 +1,4 @@
1
- import type { TransactionAdapter, TransactionOptions } from '@nestjs-transactional/core';
1
+ import { type TransactionAdapter, type TransactionOptions } from '@nestjs-transactional/core';
2
2
  import type { DataSource } from 'typeorm';
3
3
  import type { TypeOrmTransactionHandle } from '../types/typeorm-transaction-handle';
4
4
  /**
@@ -10,6 +10,12 @@ import type { TypeOrmTransactionHandle } from '../types/typeorm-transaction-hand
10
10
  * Only `isolation` from {@link TransactionOptions} is forwarded today.
11
11
  * `readOnly` and `timeout` are accepted for forward compatibility but do
12
12
  * not yet map to per-dialect statements.
13
+ *
14
+ * `PropagationMode.NESTED` needs savepoints, so `runInSavepoint` first
15
+ * checks the driver's own capability flag and rejects with
16
+ * `IllegalTransactionStateError` on drivers that cannot do them (SQL
17
+ * Server, SAP HANA, MongoDB, Spanner, Cordova) instead of emitting SQL
18
+ * they will refuse.
13
19
  */
14
20
  export declare class TypeOrmTransactionAdapter implements TransactionAdapter<TypeOrmTransactionHandle> {
15
21
  private readonly dataSource;
@@ -35,5 +41,23 @@ export declare class TypeOrmTransactionAdapter implements TransactionAdapter<Typ
35
41
  get dataSourceName(): string;
36
42
  runInTransaction<T>(options: TransactionOptions, fn: (handle: TypeOrmTransactionHandle) => Promise<T>): Promise<T>;
37
43
  runInSavepoint<T>(parent: TypeOrmTransactionHandle, fn: (handle: TypeOrmTransactionHandle) => Promise<T>): Promise<T>;
44
+ /**
45
+ * Fail fast when the configured driver cannot do savepoints, rather
46
+ * than emitting `SAVEPOINT` SQL it will reject with an opaque
47
+ * driver-level error that says nothing about `NESTED` propagation.
48
+ *
49
+ * TypeORM reports the capability itself — `driver.transactionSupport`
50
+ * is `'nested'` for the savepoint-capable drivers (Postgres, MySQL,
51
+ * Oracle, SQLite, CockroachDB, ...), `'simple'` for SQL Server and
52
+ * SAP HANA, and `'none'` for MongoDB, Spanner and Cordova. Reading the
53
+ * flag beats maintaining our own dialect allowlist, which would rot
54
+ * with every driver TypeORM adds.
55
+ *
56
+ * Deliberately permissive when the flag is missing: a TypeORM version
57
+ * that renames or drops it must not turn every `NESTED` call into a
58
+ * hard failure, since absence of the signal is not evidence of absent
59
+ * support.
60
+ */
61
+ private assertSavepointsSupported;
38
62
  }
39
63
  //# sourceMappingURL=typeorm.adapter.d.ts.map
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TypeOrmTransactionAdapter = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
+ const core_1 = require("@nestjs-transactional/core");
5
6
  /**
6
7
  * TypeORM implementation of {@link TransactionAdapter}. Delegates begin /
7
8
  * commit / rollback to `DataSource.transaction` and emits raw `SAVEPOINT`
@@ -11,6 +12,12 @@ const node_crypto_1 = require("node:crypto");
11
12
  * Only `isolation` from {@link TransactionOptions} is forwarded today.
12
13
  * `readOnly` and `timeout` are accepted for forward compatibility but do
13
14
  * not yet map to per-dialect statements.
15
+ *
16
+ * `PropagationMode.NESTED` needs savepoints, so `runInSavepoint` first
17
+ * checks the driver's own capability flag and rejects with
18
+ * `IllegalTransactionStateError` on drivers that cannot do them (SQL
19
+ * Server, SAP HANA, MongoDB, Spanner, Cordova) instead of emitting SQL
20
+ * they will refuse.
14
21
  */
15
22
  class TypeOrmTransactionAdapter {
16
23
  dataSource;
@@ -37,6 +44,9 @@ class TypeOrmTransactionAdapter {
37
44
  async runInTransaction(options, fn) {
38
45
  const isolation = mapIsolation(options.isolation);
39
46
  const runner = async (entityManager) => {
47
+ if (options.readOnly === true) {
48
+ await applyReadOnly(entityManager, this.dataSource.options.type);
49
+ }
40
50
  const handle = {
41
51
  id: (0, node_crypto_1.randomUUID)(),
42
52
  adapterName: this.name,
@@ -50,6 +60,7 @@ class TypeOrmTransactionAdapter {
50
60
  return this.dataSource.transaction(runner);
51
61
  }
52
62
  async runInSavepoint(parent, fn) {
63
+ this.assertSavepointsSupported();
53
64
  const savepointName = `sp_${(0, node_crypto_1.randomUUID)().replace(/-/g, '_').substring(0, 30)}`;
54
65
  await parent.entityManager.query(`SAVEPOINT ${savepointName}`);
55
66
  try {
@@ -62,8 +73,74 @@ class TypeOrmTransactionAdapter {
62
73
  throw err;
63
74
  }
64
75
  }
76
+ /**
77
+ * Fail fast when the configured driver cannot do savepoints, rather
78
+ * than emitting `SAVEPOINT` SQL it will reject with an opaque
79
+ * driver-level error that says nothing about `NESTED` propagation.
80
+ *
81
+ * TypeORM reports the capability itself — `driver.transactionSupport`
82
+ * is `'nested'` for the savepoint-capable drivers (Postgres, MySQL,
83
+ * Oracle, SQLite, CockroachDB, ...), `'simple'` for SQL Server and
84
+ * SAP HANA, and `'none'` for MongoDB, Spanner and Cordova. Reading the
85
+ * flag beats maintaining our own dialect allowlist, which would rot
86
+ * with every driver TypeORM adds.
87
+ *
88
+ * Deliberately permissive when the flag is missing: a TypeORM version
89
+ * that renames or drops it must not turn every `NESTED` call into a
90
+ * hard failure, since absence of the signal is not evidence of absent
91
+ * support.
92
+ */
93
+ assertSavepointsSupported() {
94
+ const support = this.dataSource.driver
95
+ ?.transactionSupport;
96
+ if (support !== undefined && support !== 'nested') {
97
+ throw new core_1.IllegalTransactionStateError(`PropagationMode.NESTED needs savepoints, which the '${this.dataSource.options.type}' ` +
98
+ `driver does not support (TypeORM reports transactionSupport: '${support}'). ` +
99
+ "Use PropagationMode.REQUIRED to join the caller's transaction, or " +
100
+ 'REQUIRES_NEW for an independent one.');
101
+ }
102
+ }
65
103
  }
66
104
  exports.TypeOrmTransactionAdapter = TypeOrmTransactionAdapter;
105
+ /**
106
+ * Dialects where `SET TRANSACTION READ ONLY` is valid after `BEGIN`,
107
+ * provided it precedes the transaction's first query.
108
+ *
109
+ * An explicit allowlist, unlike the savepoint check above: TypeORM
110
+ * publishes a capability flag for nested transactions
111
+ * (`driver.transactionSupport`) but none for transaction access mode, so
112
+ * there is nothing to read. **Review this list when a driver is added.**
113
+ *
114
+ * MySQL and MariaDB are absent on purpose rather than by omission —
115
+ * `SET TRANSACTION` there applies to the *next* transaction and raises
116
+ * `ERROR 1568` inside a started one, so read-only would have to be set
117
+ * at `START TRANSACTION` time, which TypeORM does not expose. See
118
+ * DD-027.
119
+ */
120
+ const READ_ONLY_DIALECTS = new Set([
121
+ 'postgres',
122
+ 'cockroachdb',
123
+ 'aurora-postgres',
124
+ ]);
125
+ /**
126
+ * Ask the database to reject writes for the remainder of this
127
+ * transaction, on the dialects that can do it.
128
+ *
129
+ * A silent no-op elsewhere, deliberately: `CqrsTransactionalModule`
130
+ * defaults every query handler to `readOnly: true`, so throwing here
131
+ * would break consumers on MySQL or SQLite over an option they never
132
+ * set. `readOnly` is a hint in Spring too — honoured where possible
133
+ * (DD-027).
134
+ *
135
+ * Must run before any user statement; Postgres rejects the statement
136
+ * once the transaction has executed its first query.
137
+ */
138
+ async function applyReadOnly(entityManager, dialect) {
139
+ if (!READ_ONLY_DIALECTS.has(dialect)) {
140
+ return;
141
+ }
142
+ await entityManager.query('SET TRANSACTION READ ONLY');
143
+ }
67
144
  /**
68
145
  * Map the core's underscore-style {@link IsolationLevel} to TypeORM's
69
146
  * space-separated string. Returns `undefined` when no level is set so the
@@ -1 +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"}
1
+ {"version":3,"file":"typeorm.adapter.js","sourceRoot":"","sources":["../../src/adapter/typeorm.adapter.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { type DynamicModule, type InjectionToken, type ModuleMetadata } from '@n
2
2
  /**
3
3
  * Options accepted by {@link TypeOrmTransactionalModule.forRoot}.
4
4
  *
5
- * Phase 14.20 reshape: this module now resolves the actual TypeORM
5
+ * Reshaped so this module now resolves the actual TypeORM
6
6
  * `DataSource` via DI (using `getDataSourceToken` from
7
7
  * `@nestjs/typeorm`) instead of taking it as a constructor argument.
8
8
  * The new contract is "TypeORM is configured by `@nestjs/typeorm`'s
@@ -41,7 +41,7 @@ export interface TypeOrmTransactionalOptions {
41
41
  * populates as a side effect.
42
42
  *
43
43
  * Mirrors the documented limitation on
44
- * `TransactionalModule.forRootAsync` (Phase 14.10).
44
+ * `TransactionalModule.forRootAsync`.
45
45
  */
46
46
  export interface TypeOrmTransactionalAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
47
47
  readonly useFactory: (...args: never[]) => Promise<TypeOrmTransactionalOptions> | TypeOrmTransactionalOptions;
@@ -50,7 +50,7 @@ export interface TypeOrmTransactionalAsyncOptions extends Pick<ModuleMetadata, '
50
50
  /**
51
51
  * NestJS module that binds a TypeORM {@link DataSource} to the core
52
52
  * {@link AdapterRegistry} as a transactional adapter AND activates
53
- * the transparent transactional patching machinery (Phase 14.20).
53
+ * the transparent transactional patching machinery.
54
54
  * Once registered:
55
55
  *
56
56
  * - Every `Repository` reachable via `@InjectRepository`,
@@ -46,7 +46,7 @@ const ASYNC_OPTIONS_TOKEN = (id) => Symbol(`TYPEORM_TRANSACTIONAL_ASYNC_OPTIONS[
46
46
  /**
47
47
  * NestJS module that binds a TypeORM {@link DataSource} to the core
48
48
  * {@link AdapterRegistry} as a transactional adapter AND activates
49
- * the transparent transactional patching machinery (Phase 14.20).
49
+ * the transparent transactional patching machinery.
50
50
  * Once registered:
51
51
  *
52
52
  * - Every `Repository` reachable via `@InjectRepository`,
@@ -190,7 +190,7 @@ let TypeOrmTransactionalModule = class TypeOrmTransactionalModule {
190
190
  // (or even `moduleRef.get`) cascaded into a hard-to-diagnose
191
191
  // `Invalid value used in weak set` followed by
192
192
  // `this.postgres.Pool is not a constructor` when paired with
193
- // `TypeOrmModule.forRootAsync` (Phase 14.8e Convention #22).
193
+ // `TypeOrmModule.forRootAsync` (Convention #22).
194
194
  //
195
195
  // The robust pattern is `OnModuleInit`: by the time the hook
196
196
  // runs, every provider in the module tree has been instantiated
@@ -1 +1 @@
1
- {"version":3,"file":"typeorm-transactional.module.js","sourceRoot":"","sources":["../../src/module/typeorm-transactional.module.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"typeorm-transactional.module.js","sourceRoot":"","sources":["../../src/module/typeorm-transactional.module.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * This wrap matters specifically for the
10
10
  * `@InjectEntityManager() em.getRepository(Entity).save(...)` user
11
- * pattern (Phase 14.20 Q1 Option A coverage proof): the injected
11
+ * pattern (coverage proof in the integration tests): the injected
12
12
  * `EntityManager` is the DataSource's default (non-transactional)
13
13
  * manager. Calling `em.getRepository(Entity)` would, without this
14
14
  * wrap, return a Repository whose only `manager` reference is `em`
@@ -22,7 +22,7 @@
22
22
  *
23
23
  * `@InjectEntityManager` + direct method call (`em.save(Entity, ...)`)
24
24
  * is NOT covered by this patch — that is the documented limitation
25
- * (Phase 14.20 Q5). Use `getCurrentEntityManager()` as the escape
25
+ *. Use `getCurrentEntityManager()` as the escape
26
26
  * hatch.
27
27
  */
28
28
  export declare function applyEntityManagerPatches(): void;
@@ -35,7 +35,7 @@ let originalGetRepository;
35
35
  *
36
36
  * This wrap matters specifically for the
37
37
  * `@InjectEntityManager() em.getRepository(Entity).save(...)` user
38
- * pattern (Phase 14.20 Q1 Option A coverage proof): the injected
38
+ * pattern (coverage proof in the integration tests): the injected
39
39
  * `EntityManager` is the DataSource's default (non-transactional)
40
40
  * manager. Calling `em.getRepository(Entity)` would, without this
41
41
  * wrap, return a Repository whose only `manager` reference is `em`
@@ -49,7 +49,7 @@ let originalGetRepository;
49
49
  *
50
50
  * `@InjectEntityManager` + direct method call (`em.save(Entity, ...)`)
51
51
  * is NOT covered by this patch — that is the documented limitation
52
- * (Phase 14.20 Q5). Use `getCurrentEntityManager()` as the escape
52
+ *. Use `getCurrentEntityManager()` as the escape
53
53
  * hatch.
54
54
  */
55
55
  function applyEntityManagerPatches() {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Phase 14.20 transparent transactional repositories — patching
2
+ * Transparent transactional repositories — patching
3
3
  * machinery. Exported so the module layer can drive `applyAllPatches`
4
4
  * in `forRoot`, and so unit tests can probe state directly. None of
5
5
  * these symbols are intended for application code — public API stays
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * @internal
9
9
  */
10
- export { applyRepositoryPatches, areRepositoryPatchesApplied, } from './repository-patches';
10
+ export { applyRepositoryPatches, areRepositoryPatchesApplied } from './repository-patches';
11
11
  export { applyEntityManagerPatches, areEntityManagerPatchesApplied, } from './entity-manager-patches';
12
12
  export { patchDataSourceInstance } from './data-source-patches';
13
13
  export { getActiveEntityManager, getManagedDataSourceName, isManaged, markAsManaged, resetManagedRegistry, } from './managed-registry';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * Phase 14.20 transparent transactional repositories — patching
3
+ * Transparent transactional repositories — patching
4
4
  * machinery. Exported so the module layer can drive `applyAllPatches`
5
5
  * in `forRoot`, and so unit tests can probe state directly. None of
6
6
  * these symbols are intended for application code — public API stays
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/patching/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAsCH,0CAGC;AAwBD,0DAEC;AAjED,qEAAqE;AACrE,yDAA0D;AAC1D,6DAA8D;AAE9D,2DAG8B;AAF5B,4HAAA,sBAAsB,OAAA;AACtB,iIAAA,2BAA2B,OAAA;AAE7B,mEAGkC;AAFhC,mIAAA,yBAAyB,OAAA;AACzB,wIAAA,8BAA8B,OAAA;AAEhC,6DAAgE;AAAvD,8HAAA,uBAAuB,OAAA;AAChC,uDAM4B;AAL1B,0HAAA,sBAAsB,OAAA;AACtB,4HAAA,wBAAwB,OAAA;AACxB,6GAAA,SAAS,OAAA;AACT,iHAAA,aAAa,OAAA;AACb,wHAAA,oBAAoB,OAAA;AAEtB,qCAImB;AAHjB,mHAAA,wBAAwB,OAAA;AACxB,sHAAA,2BAA2B,OAAA;AAC3B,sHAAA,2BAA2B,OAAA;AAG7B;;;;;;;;;GASG;AACH,SAAgB,eAAe;IAC7B,IAAA,2CAAsB,GAAE,CAAC;IACzB,IAAA,kDAAyB,GAAE,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,uBAAuB;IACrC,IAAA,uCAAoB,GAAE,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/patching/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAmCH,0CAGC;AAwBD,0DAEC;AA9DD,qEAAqE;AACrE,yDAA0D;AAC1D,6DAA8D;AAE9D,2DAA2F;AAAlF,4HAAA,sBAAsB,OAAA;AAAE,iIAAA,2BAA2B,OAAA;AAC5D,mEAGkC;AAFhC,mIAAA,yBAAyB,OAAA;AACzB,wIAAA,8BAA8B,OAAA;AAEhC,6DAAgE;AAAvD,8HAAA,uBAAuB,OAAA;AAChC,uDAM4B;AAL1B,0HAAA,sBAAsB,OAAA;AACtB,4HAAA,wBAAwB,OAAA;AACxB,6GAAA,SAAS,OAAA;AACT,iHAAA,aAAa,OAAA;AACb,wHAAA,oBAAoB,OAAA;AAEtB,qCAImB;AAHjB,mHAAA,wBAAwB,OAAA;AACxB,sHAAA,2BAA2B,OAAA;AAC3B,sHAAA,2BAA2B,OAAA;AAG7B;;;;;;;;;GASG;AACH,SAAgB,eAAe;IAC7B,IAAA,2CAAsB,GAAE,CAAC;IACzB,IAAA,kDAAyB,GAAE,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,uBAAuB;IACrC,IAAA,uCAAoB,GAAE,CAAC;AACzB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Hidden property keys used by the transparent transactional patching
3
- * machinery (Phase 14.20).
3
+ * machinery.
4
4
  *
5
5
  * `Symbol.for(...)` is used (not module-local `Symbol(...)`) so the same
6
6
  * key resolves identically across realms and across multiple copies of
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
3
  * Hidden property keys used by the transparent transactional patching
4
- * machinery (Phase 14.20).
4
+ * machinery.
5
5
  *
6
6
  * `Symbol.for(...)` is used (not module-local `Symbol(...)`) so the same
7
7
  * key resolves identically across realms and across multiple copies of
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "@nestjs-transactional/typeorm",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0",
4
4
  "description": "TypeORM adapter for @nestjs-transactional/core — EntityManager propagation, savepoints, multi-datasource",
5
5
  "license": "MIT",
6
+ "type": "commonjs",
7
+ "sideEffects": [
8
+ "./dist/module/typeorm-transactional.module.js"
9
+ ],
6
10
  "author": "Igor Golovanov",
7
11
  "repository": {
8
12
  "type": "git",
9
- "url": "https://github.com/igorgolovanov/nestjs-transactional.git",
13
+ "url": "git+https://github.com/igorgolovanov/nestjs-transactional.git",
10
14
  "directory": "packages/typeorm"
11
15
  },
12
16
  "bugs": {
@@ -41,7 +45,6 @@
41
45
  },
42
46
  "publishConfig": {
43
47
  "access": "public",
44
- "tag": "alpha",
45
48
  "provenance": true
46
49
  },
47
50
  "peerDependencies": {
@@ -51,7 +54,7 @@
51
54
  "reflect-metadata": "^0.1.13 || ^0.2.0",
52
55
  "rxjs": "^7.0.0",
53
56
  "typeorm": "^0.3.0 || ^1.0.0",
54
- "@nestjs-transactional/core": "^1.0.0-alpha.3"
57
+ "@nestjs-transactional/core": "^1.0.0"
55
58
  },
56
59
  "devDependencies": {
57
60
  "@nestjs/common": "^11.0.0",
@@ -64,8 +67,8 @@
64
67
  "rxjs": "^7.8.1",
65
68
  "sql.js": "^1.11.0",
66
69
  "testcontainers": "^10.13.0",
67
- "typeorm": "^0.3.0 || ^1.0.0",
68
- "@nestjs-transactional/core": "1.0.0-alpha.3"
70
+ "typeorm": "^1.1.0",
71
+ "@nestjs-transactional/core": "1.0.0"
69
72
  },
70
73
  "scripts": {
71
74
  "build": "tsc -p tsconfig.build.json",
@@ -75,6 +78,9 @@
75
78
  "test:cov": "jest --coverage",
76
79
  "test:integration": "jest --config jest.integration.config.js",
77
80
  "type-check": "tsc --noEmit",
78
- "lint": "eslint \"src/**/*.ts\""
81
+ "lint": "eslint \"src/**/*.ts\"",
82
+ "api:check": "api-extractor run",
83
+ "api:update": "api-extractor run --local",
84
+ "publish:check": "publint && attw --pack ."
79
85
  }
80
86
  }