npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

typeorm-aop-transaction

v1.6.0

Published

Modules that allow transactions of TypeORM 0.3 version to be applied from an AOP perspective

Downloads

27

Readme

Outline

In Nest.js, the library allows for the establishment of TypeORM Transaction boundaries via AOP. This approach to transactional business logic takes inspiration from the Spring Framework's non-invasive methodology. There is a good library called typeorm-transactional-cls-hooked but it is not compatible with typeorm 0.3^ or higher.

We used @toss/aop for AOP implementation and it is compatible with custom AOP decoder implemented using that library. In addition, much of the code in the typeorm-transactional-cls-hooked library was referenced to implement the Spring Transaction Synchronization Pool. Internally, use Nest.js's AsyncStorage to manage resources in TypeORM during the request lifecycle.

Initialization

step 1

To use this library, you must register the Transaction Module with the App Module.

import { MiddlewareConsumer, Module } from '@nestjs/common';
import {
  TransactionMiddleware,
  TransactionModule,
} from 'typeorm-aop-transaction';

@Module({
  imports: [TransactionModule.regist()],
  controllers: [],
  providers: [],
})
export class AppModule {}
import { MiddlewareConsumer, Module } from '@nestjs/common';
import {
  TransactionMiddleware,
  TransactionModule,
} from 'typeorm-aop-transaction';

@Module({
  imports: [TransactionModule.regist()],
  controllers: [],
  providers: [],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(TransactionMiddleware).forRoutes('*'); // <-- add this line
  }
}

If you used the connection name when registering a TypeORM Module, you must enter it in the defaultConnectionName property. The defaultConnectionName is the name that you defined when you initialized the TypeORM module, DataSource.

// app.module.ts
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { TransactionModule } from 'typeorm-aop-transaction';
import { TransactionMiddleware } from 'typeorm-aop-transaction';

@Module({
  imports: [
    TransactionModule.regist({
      defaultConnectionName: 'POSTGRES_CONNECTION', // <-- set specific typeorm connection name
    }),
  ],
  controllers: [],
  providers: [],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(TransactionMiddleware).forRoutes('*');
  }
}
...

// (example) database.module.ts
@Module({
  imports: [
    // Postgres Database
    TypeOrmModule.forRootAsync({
      name: 'POSTGRES_CONNECTION', // <-- using this connection name
      inject: [ConfigService<IsPostgresDatabaseConfig>],
      useFactory: (
        configService: ConfigService<IsPostgresDatabaseConfig, true>,
      ) => ({
        ...
      }),
    }),
  ],
  providers: [],
})
export class DatabaseModule {}

step 2-1 (with Custom repository)

Add the @CustomTransactionRepository decorator to dynamically register the Custom Repository using the Transaction Module. (be changed v.1.3.0^)

import { CustomTransactionRepository } from 'typeorm-aop-transaction';
import { BaseRepository } from 'typeorm-aop-transaction';

@CustomTransactionRepository(User) // <-- add this Decorator
@Injectable()
export class UserRepository extends BaseRepository<User> {}

If you want to specify a Repository Token explicitly, pass it to the second parameter.

@CustomTransactionRepository(User, USER_REPOSITORY_TOKEN) // <-- add this Decorator
@Injectable()
export class UserRepository extends BaseRepository<User> {}

You can use the setRepository static method to register a CustomRepository as a provider.

@Module({
  imports: [TransactionModule.setRepository([UserRepository])], // <-- register a CustomRepository
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}

step 2-2 (without Custom repository)

If you are not using Custom Repository, you can register and use the Entity class.

@Module({
  imports: [TransactionModule.setRepository([User])], // <-- regist a Entity Class
  controllers: [UserController],
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}

In this case, it must be injected from the service class using the supplied InjectTransactionRepository decorator.

@Injectable()
export class UserService {
  constructor(
    @InjectTransactionRepository(User) // <-- add this decorator
    private readonly userRepository: UserRepository,
  ) {}
  ...

step 3

Use @Transactionl to apply AOP at the method level of the Service Class.

@Injectable()
export class UserService {
  constructor(
    @InjectTransactionRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}

  @Transactional()
  async create(dto: CreateUserDto): Promise<void> {
    const user = this.userMapper.to(User, dto);

    await this.userRepository.insert(user);
  }

  @Transactional({
    propagation: PROPAGATION.SUPPORTS
  })
  async findAll(): Promise<User[]> {
    const user = await this.userRepository.find({
      order: {
        created_at: 'DESC',
      },
    });

    return user;
  }

The currently supported proposals are "REQUIRES_NEW", "REQUIRED", “NESETED”, “NEVER” and isolationLevel supports all isolation levels provided at the TypeORM level.

Propagations

Currently supported transaction propagation levels are REQUIRES_NEW, REQUIRED, NESTED, NEVER and SUPPORTS @Transactional default propagation option is REQUIRED.

REQUIRES_NEW

REQUIRES_NEW propagation level starts a new transaction regardless of the existence of a parent transaction. Moreover, this newly started transaction is committed or rolled back independently of the nested or parent transaction. Here is an example for better understanding.

In the create method that has the REQUIRES_NEW propagation level, the create2 method with the REQUIRED propagation level is being executed and an error occurs during the execution of the create2 method. create2 is rolled back and create is committed, and as a result, the Error is thrown out of the Service Class.

[Nest] 23598  - 2023. 06. 18. 오후 5:56:06   DEBUG [Transactional] 1687078566046|POSTGRES_CONNECTION|create|READ COMMITTED|REQUIRES_NEW - New Transaction
query: START TRANSACTION
query: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["2ce-26531b27f5e8","wjdrms15!","[email protected]","+82-10-3252-2568"]
[Nest] 23598  - 2023. 06. 18. 오후 5:56:06   DEBUG [Transactional] 1687078566046|POSTGRES_CONNECTION|create2|READ COMMITTED|REQUIRED - New Transaction

query: START TRANSACTION
query: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["f4b-55aadba0508b","wjdrms15!","2222 [email protected]","+82-10-3252-2568"]
query: ROLLBACK

query: COMMIT
[Nest] 23598  - 2023. 06. 18. 오후 5:56:06   ERROR [ExceptionsHandler] test

In this case, the method create2 with the REQUIRES_NEW propagation level is being executed within the create method with the REQUIRED propagation level, and an error occurs during the execution of the create2 method. In this case, the result of the create2 method with the REQUIRES_NEW propagation attribute is committed instead of being rolled back.

[Nest] 24146  - 2023. 06. 18. 오후 6:06:06   DEBUG [Transactional] 1687079166691|POSTGRES_CONNECTION|create|READ COMMITTED|REQUIRED - New Transaction
query: START TRANSACTION
query: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["89f-ff92d6554359","wjdrms15!","[email protected]","+82-10-3252-2568"]

[Nest] 24146  - 2023. 06. 18. 오후 6:06:06   DEBUG [Transactional] 1687079166691|POSTGRES_CONNECTION|create2|READ COMMITTED|REQUIRES_NEW - New Transaction
query: START TRANSACTION
query: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["7a3-cce699b7f065","wjdrms15!","2222 [email protected]","+82-10-3252-2568"]
query: COMMIT

query: ROLLBACK
[Nest] 24146  - 2023. 06. 18. 오후 6:06:06   ERROR [ExceptionsHandler] test

REQUIRED

The default propagation level is REQUIRED. If set to REQUIRED, transaction boundary settings depend heavily on the existence of a parent transaction. If there is already an ongoing transaction, it participates in the transaction without starting a new one. If there is no ongoing transaction, a new transaction is started.

In the create method, which has been set to a REQUIRED propagation level, an error occurs while the create2 method, which has been set to a REQUIRED propagation level, is executed. Since they behave like a single transaction, both are rolled back.

[Nest] 24304  - 2023. 06. 18. 오후 6:10:53   DEBUG [Transactional] 1687079453250|POSTGRES_CONNECTION|create|READ COMMITTED|REQUIRED - New Transaction
query: START TRANSACTION
query: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["4ed-be402112bcde","wjdrms15!","[email protected]","+82-10-3252-2568"]
[Nest] 24304  - 2023. 06. 18. 오후 6:10:53   DEBUG [Transactional] 1687079453250|POSTGRES_CONNECTION|create2|READ COMMITTED|REQUIRED - Join Transaction
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["2cd-d3159145e24a","wjdrms15!","2222 [email protected]","+82-10-3252-2568"]

query: ROLLBACK
[Nest] 24304  - 2023. 06. 18. 오후 6:10:53   ERROR [ExceptionsHandler] test

NESTED

This propagation option is very similar to REQUIRED, but there is a difference when the parent transaction exists. In this case, it does not simply participate in the transaction, but sets a savepoint before executing its query. If an error occurs, it rolls back only up to the savepoint it set, so the parent transaction is committed normally.

If there is a NESTED propagation method create2 inside the parent method create with the REQUIRED propagation level, and an error occurs during the execution of create2, create2 saves a savepoint before executing its query. If an error occurs, it rolls back only up to the savepoint it set, so the insert by the parent method is normally committed.

[Nest] 24502  - 2023. 06. 18. 오후 6:15:43   DEBUG [Transactional] 1687079743116|POSTGRES_CONNECTION|create|READ COMMITTED|REQUIRED - New Transaction
query: START TRANSACTION
query: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["615-1bbae146a294","wjdrms15!","[email protected]","+82-10-3252-2568"]
[Nest] 24502  - 2023. 06. 18. 오후 6:15:43   DEBUG [Transactional] 1687079743116|POSTGRES_CONNECTION|create2|READ COMMITTED|NESTED - Make savepiont, Wrap Transaction

query: SAVEPOINT typeorm_1
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["1b9-d065db5d0bc4","wjdrms15!","2222 [email protected]","+82-10-3252-2568"]
query: ROLLBACK TO SAVEPOINT typeorm_1

query: COMMIT
[Nest] 24502  - 2023. 06. 18. 오후 6:15:43   ERROR [ExceptionsHandler] test

NEVER

If the NEVER propagation option is set, it defaults to returning an error if a parent transaction exists.

[Nest] 15178  - 2023. 07. 02. 오후 5:35:17   DEBUG [Transactional] 1688286917592|POSTGRES_CONNECTION|create|READ COMMITTED|REQUIRED - New Transaction
query: START TRANSACTION
query: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["c2d-5b8df90d6607","wjdrms15!","[email protected]","+82-10-3252-2568"]
query: ROLLBACK
**[Nest] 15178  - 2023. 07. 02. 오후 5:35:17   ERROR [ExceptionsHandler] Attempting to join a transaction in progress. Methods with NEVER properties cannot run within a transaction boundary**
Error: Attempting to join a transaction in progress. Methods with NEVER properties cannot run within a transaction boundary
    at AlsTransactionDecorator.<anonymous> (/Users/beobwoo/dev/beebee/server/node_modules/typeorm-aop-transaction/src/providers/als-transaction.decorator.ts:305:15)
    at Generator.throw (<anonymous>)
    at rejected (/Users/beobwoo/dev/beebee/server/node_modules/typeorm-aop-transaction/dist/providers/als-transaction.decorator.js:18:65)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)

If the NEVER propagation option is normally processable, it does not create a transaction it only executes SQL queries.

[Nest] 15328  - 2023. 07. 02. 오후 5:36:42   DEBUG [Transactional] 1688287002875|POSTGRES_CONNECTION|findAll|READ COMMITTED|NEVER - No Transaction
query: SELECT "*" FROM "user" "User" WHERE "User"."deleted_at" IS NULL ORDER BY "User"."created_at" DESC

SUPPORTS

If the SUPPORTS transaction option is set and the parent transaction does not exist, it behaves the same as the NEVER propagation option. Therefore, it only runs SQL Query without any transactions.

[Nest] 15328  - 2023. 07. 02. 오후 5:36:42   DEBUG [Transactional] 1688287002875|POSTGRES_CONNECTION|findAll|READ COMMITTED|NEVER - No Transaction
query: SELECT "*" FROM "user" "User" WHERE "User"."deleted_at" IS NULL ORDER BY "User"."created_at" DESC

If the parent transaction is in progress and is available to participate, it will behave the same way as the REQUIRED propagation option. Therefore, participate in transactions in progress.

[Nest] 15831  - 2023. 07. 02. 오후 5:41:09   DEBUG [Transactional] 1688287269077|POSTGRES_CONNECTION|create|READ COMMITTED|NESTED - New Transaction
query: START TRANSACTION
query: SET TRANSACTION ISOLATION LEVEL READ COMMITTED
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["b0f-a42a40a6ba7f","wjdrms15!","[email protected]","+82-10-3252-2568"]
[Nest] 15831  - 2023. 07. 02. 오후 5:41:09   DEBUG [Transactional] 1688287269077|POSTGRES_CONNECTION|create2|READ COMMITTED|**SUPPORTS - Join Transaction // <-- join**
query: INSERT INTO "user"("created_at", "updated_at", "deleted_at", "id", "user_id", "password", "email", "phone_number") VALUES (DEFAULT, DEFAULT, DEFAULT, DEFAULT, $1, $2, $3, $4) RETURNING "created_at", "updated_at", "deleted_at", "id" -- PARAMETERS: ["42b-d4bbdccc8c9a","wjdrms15!","2222 [email protected]","+82-10-3252-2568"]
query: COMMIT

Logging

If you want to log the generation and participation of transactions according to the propagation option, pass the logging property with the TransactionModule.regist call factor. The default log level is the log.

@Module({
  imports: [
    TransactionModule.regist({
      logging: 'debug', // logging level
    }),
  ],
  controllers: [],
  providers: [],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(TransactionMiddleware).forRoutes('*');
  }
}

// (example) console logging
[Nest] 20212  - 2023. 07. 24. 오후 11:29:57   DEBUG [Transactional] 1690208997228|POSTGRES_CONNECTION|findAll|READ COMMITTED|REQUIRED - New Transaction
[Nest] 20212  - 2023. 07. 24. 오후 11:46:05   DEBUG [Transactional] 1690209965305|POSTGRES_CONNECTION|create|READ COMMITTED|REQUIRED - New Transaction

Message Queue

Bull

This library supports integration with the @nestjs/bull library. However, the following are the precautions. If a consumer of a job registered in bull queue calls the @Transactional service methods, the asynchronously called method has no parent transaction, so it behaves the same as the top-level transaction.

example
// basic-queue.processor.ts
@Processor(QueueName.BASIC_QUEUE)
export class BasicQueueProcessor {
  constructor(
    @Inject(EmploymentOpportunityInjector.EMPLOYMENT_OPPORTUNITY_SERVICE)
    private readonly eopService: EmploymentOpportunityService,
  ) {}

  @Process(AutoDeleteEmploymentOpportunityJobName)
  async deleteEmploymentOpportunity(
    job: Job<AutoDeleteEmploymentOpportunityJob>,
  ) {
    // call REQUIRED delete method
    // has no parent transaction, so start new transaction
    await this.eopService.delete(job.data.eopId);
  }
}

// eop.service.ts
@Transactional()
delete(eopId: string) {
  return this.eopRepository.delete(eopId);
}

Suppose the delete method of the EopService is called during operation processing by the BasicQueueProcessor. From the perspective of a transaction enclosing the delete method, there are no parent transactions, so create a new transaction according to the rules of the REQUIRED propagation option.

Testing

Unit Test

// user.service.ts
@Injectable()
export class UserService {
  constructor(
    @InjectTransactionRepository(User)
    private readonly userRepository: Repository<User>,
    @InjectTransactionRepository(SocialAccount)
    private readonly socialAccountRepository: Repository<SocialAccount>,
  ) {}
  ...

The type of Transactional Repository injected through TransactionModule.setRepository is the same as the Repository<EntityType> provided by TypeORM and uses a token-based provider provided by TypeORM, so you can inject the MockRepository test module by getRepositoryToken method.

// user.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDto } from '../dtos/create-user.dto';
import { UserService } from '../service/user.service';

describe('UserService', () => {
  let service: UserService;
  let userRepository: Repository<User>;
  let socialAccountRepository: Repository<SocialAccount>;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          useValue: {
            create: jest.fn(),
            save: jest.fn(),
            ...
          },
        },
        {
          provide: getRepositoryToken(SocialAccount),
          useValue: {
            create: jest.fn(),
            save: jest.fn(),
            ...
          },
        },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
    userRepository = module.get<Repository<User>>(getRepositoryToken(User));
    socialAccountRepository = module.get<Repository<SocialAccount>>(
      getRepositoryToken(SocialAccount),
    );
  });
 ...

After that, you can create a typical unit test code through the jest.Spy object.

it('should save User Entity and related Entities', async () => {
  const create = jest.spyOn(userRepository, 'create').mockReturnValue(user);
  const save = jest.spyOn(userRepository, 'save').mockResolvedValue({
    ...user,
    _id: 'test uuid',
  });

  await service.create(createUserDto);

  expect(create).toBeCalledTimes(1);
  expect(save).toBeCalledTimes(1);
  ...
});

Integration Test

If you have registered the TransactionModule through TransactionModule.forRoot in AppModule, you can perform the e2e test without any additional procedures. The @TransactionalDecorator uses @toss/aop to provide transaction boundaries to methods in the Service class, so you can use a variety of integrated test methods in addition to the examples below for integrated testing with the Controller.

@Module({
  imports: [
    AopModule,
    TransactionModule.regist({
      defaultConnectionName: <<Optional>>,
    }),
    DatabaseModule,
    ...
  ],
  controllers: [AppController],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(TransactionMiddleware).forRoutes('*');
  }
}

This example uses supertest and jest together for integration testing. Configure the Test Module through AppModule for integrated testing and organize the data in the test database.

describe('UserController (e2e)', () => {
  let app: INestApplication;
  let dataSource: DataSource;

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      imports: [AppModule],
      providers: [],
    }).compile();

    app = module.createNestApplication();
    await app.init();
  });

  beforeEach(async () => {
    jest.restoreAllMocks();

    dataSource = app.get<DataSource>(getDataSourceToken());
    const queryRunner = dataSource.createQueryRunner();
    try {
      await queryRunner.query('TRUNCATE "user" CASCADE');
    } finally {
      await queryRunner.release();
    }
  });

  afterAll(async () => {
    await app.close();
  });

  it('GET /user/list', () => {
    return request(app.getHttpServer()).get('/user/list').expect(<<expedted_value>>);
  });

  ...
});

Future Support Plan

  • add propagation option : ~~NESTED~~, NOT_SUPPORTED, ~~SUPPORTS~~, ~~NEVER~~, MANDATORY
  • ~~add Unit Test~~
  • ~~add integration test~~
  • add Rollback, Commit Callback Hooks
  • ~~remove Loggers~~

Test Coverage

| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | | ------------------------------------------ | ------- | -------- | ------- | ------- | ----------------- | | All files | 100 | 100 | 100 | 100 | | src | 100 | 100 | 100 | 100 | | base.repository.ts | 100 | 100 | 100 | 100 | | src/const | 100 | 100 | 100 | 100 | | custom-repository-metadata.ts | 100 | 100 | 100 | 100 | | propagation.ts | 100 | 100 | 100 | 100 | | src/decorators | 100 | 100 | 100 | 100 | | custom-transaction-repository.decorator.ts | 100 | 100 | 100 | 100 | | inject-transaction-repository.decorator.ts | 100 | 100 | 100 | 100 | | transactional.decorator.ts | 100 | 100 | 100 | 100 | | src/exceptions | 100 | 100 | 100 | 100 | | not-rollback.error.ts | 100 | 100 | 100 | 100 | | src/modules | 100 | 100 | 100 | 100 | | transaciton.module.ts | 100 | 100 | 100 | 100 | | src/providers | 100 | 100 | 100 | 100 | | als-transaction.decorator.ts | 100 | 100 | 100 | 100 | | data-source-map.service.ts | 100 | 100 | 100 | 100 | | transaction.logger.ts | 100 | 100 | 100 | 100 | | transaction.middleware.ts | 100 | 100 | 100 | 100 | | transaction.service.ts | 100 | 100 | 100 | 100 | | src/symbols | 100 | 100 | 100 | 100 | | als-service.symbol.ts | 100 | 100 | 100 | 100 | | data-source-map.service.symbol.ts | 100 | 100 | 100 | 100 | | transaciton-module-option.symbol.ts | 100 | 100 | 100 | 100 | | transaction-decorator.symbol.ts | 100 | 100 | 100 | 100 | | src/test/mocks | 100 | 100 | 100 | 100 | | als.service.mock.ts | 100 | 100 | 100 | 100 | | discovery.service.mock.ts | 100 | 100 | 100 | 100 | | transaction-module-option.mock.ts | 100 | 100 | 100 | 100 | | src/utils | 100 | 100 | 100 | 100 | | is-base-repository-prototype.ts | 100 | 100 | 100 | 100 | | is-typeorm-entity.ts | 100 | 100 | 100 | 100 |

Referenced Libraries