@tsed/mikro-orm 6.96.2 → 7.0.0-beta.1

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 (37) hide show
  1. package/jest.config.js +3 -3
  2. package/lib/cjs/MikroOrmModule.js +2 -0
  3. package/lib/cjs/MikroOrmModule.js.map +1 -1
  4. package/lib/cjs/decorators/entityManager.d.ts +14 -0
  5. package/lib/cjs/decorators/entityManager.js +21 -0
  6. package/lib/cjs/decorators/entityManager.js.map +1 -0
  7. package/lib/cjs/decorators/index.d.ts +1 -0
  8. package/lib/cjs/decorators/index.js +1 -0
  9. package/lib/cjs/decorators/index.js.map +1 -1
  10. package/lib/cjs/interceptors/TransactionalInterceptor.d.ts +5 -2
  11. package/lib/cjs/interceptors/TransactionalInterceptor.js +19 -6
  12. package/lib/cjs/interceptors/TransactionalInterceptor.js.map +1 -1
  13. package/lib/cjs/services/MikroOrmRegistry.d.ts +3 -3
  14. package/lib/cjs/services/MikroOrmRegistry.js +7 -6
  15. package/lib/cjs/services/MikroOrmRegistry.js.map +1 -1
  16. package/lib/cjs/services/RetryStrategy.d.ts +1 -0
  17. package/lib/cjs/services/RetryStrategy.js +2 -0
  18. package/lib/cjs/services/RetryStrategy.js.map +1 -1
  19. package/lib/esm/MikroOrmModule.js +4 -2
  20. package/lib/esm/MikroOrmModule.js.map +1 -1
  21. package/lib/esm/decorators/entityManager.js +17 -0
  22. package/lib/esm/decorators/entityManager.js.map +1 -0
  23. package/lib/esm/decorators/index.js +1 -0
  24. package/lib/esm/decorators/index.js.map +1 -1
  25. package/lib/esm/interceptors/TransactionalInterceptor.js +20 -7
  26. package/lib/esm/interceptors/TransactionalInterceptor.js.map +1 -1
  27. package/lib/esm/services/MikroOrmRegistry.js +8 -7
  28. package/lib/esm/services/MikroOrmRegistry.js.map +1 -1
  29. package/lib/esm/services/RetryStrategy.js +1 -1
  30. package/lib/esm/services/RetryStrategy.js.map +1 -1
  31. package/lib/types/decorators/entityManager.d.ts +14 -0
  32. package/lib/types/decorators/index.d.ts +1 -0
  33. package/lib/types/interceptors/TransactionalInterceptor.d.ts +5 -2
  34. package/lib/types/services/MikroOrmRegistry.d.ts +3 -3
  35. package/lib/types/services/RetryStrategy.d.ts +1 -0
  36. package/package.json +13 -12
  37. package/readme.md +172 -12
package/jest.config.js CHANGED
@@ -5,10 +5,10 @@ module.exports = {
5
5
  ...require("@tsed/jest-config")(__dirname, "mikro-orm"),
6
6
  coverageThreshold: {
7
7
  global: {
8
- branches: 85.29,
8
+ branches: 90.91,
9
9
  functions: 100,
10
- lines: 98.95,
11
- statements: 99.15
10
+ lines: 99.06,
11
+ statements: 99.21
12
12
  }
13
13
  }
14
14
  };
@@ -28,4 +28,6 @@ MikroOrmModule = (0, tslib_1.__decorate)([
28
28
  })
29
29
  ], MikroOrmModule);
30
30
  exports.MikroOrmModule = MikroOrmModule;
31
+ // TODO: the IoC container should provide null or undefined by default until tsedio/tsed#1694 is closed
32
+ (0, di_1.registerProvider)({ provide: services_1.RetryStrategy, useValue: null });
31
33
  //# sourceMappingURL=MikroOrmModule.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MikroOrmModule.js","sourceRoot":"","sources":["../../src/MikroOrmModule.ts"],"names":[],"mappings":";;;;AAAA,yCAAuD;AACvD,iCAAoF;AAoBpF,IAAa,cAAc,GAA3B,MAAa,cAAc;IAOlB,KAAK,CAAC,OAAO;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;QAE3F,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAEM,UAAU;QACf,OAAO,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;IAClD,CAAC;CACF,CAAA;AAdC;IADC,IAAA,aAAQ,EAAC,UAAU,EAAE,EAAE,CAAC;;gDACa;AAGtC;IADC,IAAA,WAAM,GAAE;2CAC2B,2BAAgB;wDAAC;AAL1C,cAAc;IAJ1B,IAAA,WAAM,EAAC;QACN,OAAO,EAAE,CAAC,oBAAS,EAAE,2BAAgB,CAAC;QACtC,IAAI,EAAE,CAAC,kBAAa,EAAE,2BAAgB,CAAC;KACxC,CAAC;GACW,cAAc,CAgB1B;AAhBY,wCAAc","sourcesContent":["import {DBContext, MikroOrmRegistry} from \"./services\";\nimport {Configuration, Constant, Inject, Module, OnDestroy, OnInit} from \"@tsed/di\";\nimport {Options} from \"@mikro-orm/core\";\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace TsED {\n interface Configuration {\n /**\n * The the ORM configuration, entity metadata.\n * If you omit the `options` parameter, your CLI config will be used.\n */\n mikroOrm?: Options[];\n }\n }\n}\n\n@Module({\n imports: [DBContext, MikroOrmRegistry],\n deps: [Configuration, MikroOrmRegistry]\n})\nexport class MikroOrmModule implements OnDestroy, OnInit {\n @Constant(\"mikroOrm\", [])\n private readonly settings!: Options[];\n\n @Inject()\n private readonly mikroOrmRegistry!: MikroOrmRegistry;\n\n public async $onInit(): Promise<void> {\n const promises = this.settings.map((opts) => this.mikroOrmRegistry.createConnection(opts));\n\n await Promise.all(promises);\n }\n\n public $onDestroy(): Promise<void> {\n return this.mikroOrmRegistry.closeConnections();\n }\n}\n"]}
1
+ {"version":3,"file":"MikroOrmModule.js","sourceRoot":"","sources":["../../src/MikroOrmModule.ts"],"names":[],"mappings":";;;;AAAA,yCAAsE;AACtE,iCAAsG;AAoBtG,IAAa,cAAc,GAA3B,MAAa,cAAc;IAOlB,KAAK,CAAC,OAAO;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;QAE3F,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAEM,UAAU;QACf,OAAO,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;IAClD,CAAC;CACF,CAAA;AAdC;IADC,IAAA,aAAQ,EAAC,UAAU,EAAE,EAAE,CAAC;;gDACa;AAGtC;IADC,IAAA,WAAM,GAAE;2CAC2B,2BAAgB;wDAAC;AAL1C,cAAc;IAJ1B,IAAA,WAAM,EAAC;QACN,OAAO,EAAE,CAAC,oBAAS,EAAE,2BAAgB,CAAC;QACtC,IAAI,EAAE,CAAC,kBAAa,EAAE,2BAAgB,CAAC;KACxC,CAAC;GACW,cAAc,CAgB1B;AAhBY,wCAAc;AAkB3B,uGAAuG;AACvG,IAAA,qBAAgB,EAAC,EAAC,OAAO,EAAE,wBAAa,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAC","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"./services\";\nimport {Configuration, Constant, Inject, Module, OnDestroy, OnInit, registerProvider} from \"@tsed/di\";\nimport {Options} from \"@mikro-orm/core\";\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace TsED {\n interface Configuration {\n /**\n * The the ORM configuration, entity metadata.\n * If you omit the `options` parameter, your CLI config will be used.\n */\n mikroOrm?: Options[];\n }\n }\n}\n\n@Module({\n imports: [DBContext, MikroOrmRegistry],\n deps: [Configuration, MikroOrmRegistry]\n})\nexport class MikroOrmModule implements OnDestroy, OnInit {\n @Constant(\"mikroOrm\", [])\n private readonly settings!: Options[];\n\n @Inject()\n private readonly mikroOrmRegistry!: MikroOrmRegistry;\n\n public async $onInit(): Promise<void> {\n const promises = this.settings.map((opts) => this.mikroOrmRegistry.createConnection(opts));\n\n await Promise.all(promises);\n }\n\n public $onDestroy(): Promise<void> {\n return this.mikroOrmRegistry.closeConnections();\n }\n}\n\n// TODO: the IoC container should provide null or undefined by default until tsedio/tsed#1694 is closed\nregisterProvider({provide: RetryStrategy, useValue: null});\n"]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Get the entity manager for the given connection name.
3
+ * @param connectionName
4
+ * @decorator
5
+ * @mikroOrm
6
+ */
7
+ export declare const EntityManager: (connectionName?: string | undefined) => PropertyDecorator;
8
+ /**
9
+ * Get the entity manager for the given connection name.
10
+ * @param connectionName
11
+ * @decorator
12
+ * @mikroOrm
13
+ */
14
+ export declare const Em: (connectionName?: string | undefined) => PropertyDecorator;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Em = exports.EntityManager = void 0;
4
+ const di_1 = require("@tsed/di");
5
+ const services_1 = require("../services");
6
+ /**
7
+ * Get the entity manager for the given connection name.
8
+ * @param connectionName
9
+ * @decorator
10
+ * @mikroOrm
11
+ */
12
+ const EntityManager = (connectionName) => (0, di_1.Inject)(services_1.MikroOrmRegistry, (registry) => { var _a; return (_a = registry.get(connectionName)) === null || _a === void 0 ? void 0 : _a.em; });
13
+ exports.EntityManager = EntityManager;
14
+ /**
15
+ * Get the entity manager for the given connection name.
16
+ * @param connectionName
17
+ * @decorator
18
+ * @mikroOrm
19
+ */
20
+ exports.Em = exports.EntityManager;
21
+ //# sourceMappingURL=entityManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entityManager.js","sourceRoot":"","sources":["../../../src/decorators/entityManager.ts"],"names":[],"mappings":";;;AAAA,iCAAgC;AAChC,0CAA6C;AAE7C;;;;;GAKG;AACI,MAAM,aAAa,GAAG,CAAC,cAAuB,EAAqB,EAAE,CAC1E,IAAA,WAAM,EAAC,2BAAgB,EAAE,CAAC,QAA0B,EAAE,EAAE,WAAC,OAAA,MAAA,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,0CAAE,EAAE,CAAA,EAAA,CAAsB,CAAC;AADrG,QAAA,aAAa,iBACwF;AAElH;;;;;GAKG;AACU,QAAA,EAAE,GAAG,qBAAa,CAAC","sourcesContent":["import {Inject} from \"@tsed/di\";\nimport {MikroOrmRegistry} from \"../services\";\n\n/**\n * Get the entity manager for the given connection name.\n * @param connectionName\n * @decorator\n * @mikroOrm\n */\nexport const EntityManager = (connectionName?: string): PropertyDecorator =>\n Inject(MikroOrmRegistry, (registry: MikroOrmRegistry) => registry.get(connectionName)?.em) as PropertyDecorator;\n\n/**\n * Get the entity manager for the given connection name.\n * @param connectionName\n * @decorator\n * @mikroOrm\n */\nexport const Em = EntityManager;\n"]}
@@ -1,2 +1,3 @@
1
1
  export * from "./transactional";
2
2
  export * from "./connection";
3
+ export * from "./entityManager";
@@ -3,4 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  (0, tslib_1.__exportStar)(require("./transactional"), exports);
5
5
  (0, tslib_1.__exportStar)(require("./connection"), exports);
6
+ (0, tslib_1.__exportStar)(require("./entityManager"), exports);
6
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":";;;AAAA,+DAAgC;AAChC,4DAA6B","sourcesContent":["export * from \"./transactional\";\nexport * from \"./connection\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":";;;AAAA,+DAAgC;AAChC,4DAA6B;AAC7B,+DAAgC","sourcesContent":["export * from \"./transactional\";\nexport * from \"./connection\";\nexport * from \"./entityManager\";\n"]}
@@ -1,13 +1,16 @@
1
1
  import { DBContext, MikroOrmRegistry, RetryStrategy } from "../services";
2
2
  import { InterceptorContext, InterceptorMethods, InterceptorNext } from "@tsed/di";
3
+ import { Logger } from "@tsed/logger";
3
4
  export interface TransactionOptions {
4
5
  connectionName?: string;
5
- retryStrategy?: RetryStrategy;
6
+ retry?: boolean;
6
7
  }
7
8
  export declare class TransactionalInterceptor implements InterceptorMethods {
8
9
  private readonly registry;
9
10
  private readonly context;
10
- constructor(registry: MikroOrmRegistry, context: DBContext);
11
+ private readonly logger;
12
+ private readonly retryStrategy?;
13
+ constructor(registry: MikroOrmRegistry, context: DBContext, logger: Logger, retryStrategy?: RetryStrategy | undefined);
11
14
  intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown>;
12
15
  private runWithinNewCtx;
13
16
  private runUnderCtx;
@@ -4,13 +4,19 @@ exports.TransactionalInterceptor = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const services_1 = require("../services");
6
6
  const di_1 = require("@tsed/di");
7
+ const logger_1 = require("@tsed/logger");
7
8
  let TransactionalInterceptor = class TransactionalInterceptor {
8
- constructor(registry, context) {
9
+ constructor(registry, context, logger, retryStrategy) {
9
10
  this.registry = registry;
10
11
  this.context = context;
12
+ this.logger = logger;
13
+ this.retryStrategy = retryStrategy;
11
14
  }
12
15
  async intercept(context, next) {
13
16
  const options = this.extractContextName(context);
17
+ if (options.retry && !this.retryStrategy) {
18
+ this.logger.warn(`To retry a transaction you have to implement a "${services_1.RetryStrategy.description}" interface`);
19
+ }
14
20
  const ctx = this.context.getContext();
15
21
  if (!ctx) {
16
22
  return this.runWithinNewCtx(next, options);
@@ -29,23 +35,26 @@ let TransactionalInterceptor = class TransactionalInterceptor {
29
35
  const em = orm.em.fork(true, true);
30
36
  ctx.set(em.name, em);
31
37
  const runInTransaction = () => this.executeInTransaction(options.connectionName, next);
32
- return this.context.run(ctx, () => (options.retryStrategy ? options.retryStrategy.acquire(runInTransaction) : runInTransaction()));
38
+ return this.context.run(ctx, () => { var _a; return (options.retry ? (_a = this.retryStrategy) === null || _a === void 0 ? void 0 : _a.acquire(runInTransaction) : runInTransaction()); });
33
39
  }
34
40
  extractContextName(context) {
35
41
  const options = context.options || {};
36
42
  let connectionName;
37
- let retryStrategy;
43
+ let retry;
38
44
  if (typeof options === "string") {
39
45
  connectionName = options;
40
46
  }
41
47
  else if (options) {
42
48
  connectionName = options.connectionName;
43
- retryStrategy = options.retryStrategy;
49
+ retry = options.retry;
44
50
  }
45
51
  if (!connectionName) {
46
52
  connectionName = "default";
47
53
  }
48
- return { connectionName, retryStrategy };
54
+ if (!retry) {
55
+ retry = false;
56
+ }
57
+ return { connectionName, retry };
49
58
  }
50
59
  async executeInTransaction(connectionName, next) {
51
60
  const em = this.context.get(connectionName);
@@ -58,7 +67,11 @@ TransactionalInterceptor = (0, tslib_1.__decorate)([
58
67
  (0, di_1.Interceptor)(),
59
68
  (0, tslib_1.__param)(0, (0, di_1.Inject)()),
60
69
  (0, tslib_1.__param)(1, (0, di_1.Inject)()),
61
- (0, tslib_1.__metadata)("design:paramtypes", [services_1.MikroOrmRegistry, services_1.DBContext])
70
+ (0, tslib_1.__param)(2, (0, di_1.Inject)()),
71
+ (0, tslib_1.__param)(3, (0, di_1.Inject)(services_1.RetryStrategy)),
72
+ (0, tslib_1.__metadata)("design:paramtypes", [services_1.MikroOrmRegistry,
73
+ services_1.DBContext,
74
+ logger_1.Logger, Object])
62
75
  ], TransactionalInterceptor);
63
76
  exports.TransactionalInterceptor = TransactionalInterceptor;
64
77
  //# sourceMappingURL=TransactionalInterceptor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TransactionalInterceptor.js","sourceRoot":"","sources":["../../../src/interceptors/TransactionalInterceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"../services\";\nimport {Inject, Interceptor, InterceptorContext, InterceptorMethods, InterceptorNext} from \"@tsed/di\";\nimport {EntityManager} from \"@mikro-orm/core\";\n\nexport interface TransactionOptions {\n connectionName?: string;\n retryStrategy?: RetryStrategy;\n}\n\n@Interceptor()\nexport class TransactionalInterceptor implements InterceptorMethods {\n constructor(@Inject() private readonly registry: MikroOrmRegistry, @Inject() private readonly context: DBContext) {}\n\n public async intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown> {\n const options = this.extractContextName(context);\n\n const ctx = this.context.getContext();\n\n if (!ctx) {\n return this.runWithinNewCtx(next, options);\n }\n\n if (ctx.has(options.connectionName!)) {\n return next();\n }\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runWithinNewCtx(next: InterceptorNext, options: TransactionOptions): Promise<unknown> | unknown {\n const ctx = new Map<string, EntityManager>();\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runUnderCtx(next: InterceptorNext, ctx: Map<string, EntityManager>, options: TransactionOptions): Promise<unknown> | unknown {\n const orm = this.registry.get(options.connectionName);\n const em = orm.em.fork(true, true);\n\n ctx.set(em.name, em);\n\n const runInTransaction = () => this.executeInTransaction(options.connectionName!, next);\n\n return this.context.run(ctx, () => (options.retryStrategy ? options.retryStrategy.acquire(runInTransaction) : runInTransaction()));\n }\n\n private extractContextName(context: InterceptorContext<unknown>): TransactionOptions {\n const options = context.options || ({} as Partial<TransactionOptions> | string);\n\n let connectionName: string | undefined;\n let retryStrategy: RetryStrategy | undefined;\n\n if (typeof options === \"string\") {\n connectionName = options;\n } else if (options) {\n connectionName = options.connectionName;\n retryStrategy = options.retryStrategy;\n }\n\n if (!connectionName) {\n connectionName = \"default\";\n }\n\n return {connectionName, retryStrategy};\n }\n\n private async executeInTransaction(connectionName: string, next: InterceptorNext): Promise<unknown> {\n const em = this.context.get(connectionName);\n\n const result = await next();\n\n await em?.flush();\n\n return result;\n }\n}\n"]}
1
+ {"version":3,"file":"TransactionalInterceptor.js","sourceRoot":"","sources":["../../../src/interceptors/TransactionalInterceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"../services\";\nimport {Inject, Interceptor, InterceptorContext, InterceptorMethods, InterceptorNext} from \"@tsed/di\";\nimport {EntityManager} from \"@mikro-orm/core\";\nimport {Logger} from \"@tsed/logger\";\n\nexport interface TransactionOptions {\n connectionName?: string;\n retry?: boolean;\n}\n\n@Interceptor()\nexport class TransactionalInterceptor implements InterceptorMethods {\n constructor(\n @Inject() private readonly registry: MikroOrmRegistry,\n @Inject() private readonly context: DBContext,\n @Inject() private readonly logger: Logger,\n @Inject(RetryStrategy)\n private readonly retryStrategy?: RetryStrategy\n ) {}\n\n public async intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown> {\n const options = this.extractContextName(context);\n\n if (options.retry && !this.retryStrategy) {\n this.logger.warn(`To retry a transaction you have to implement a \"${RetryStrategy.description}\" interface`);\n }\n\n const ctx = this.context.getContext();\n\n if (!ctx) {\n return this.runWithinNewCtx(next, options);\n }\n\n if (ctx.has(options.connectionName!)) {\n return next();\n }\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runWithinNewCtx(next: InterceptorNext, options: TransactionOptions): Promise<unknown> | unknown {\n const ctx = new Map<string, EntityManager>();\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runUnderCtx(next: InterceptorNext, ctx: Map<string, EntityManager>, options: TransactionOptions): Promise<unknown> | unknown {\n const orm = this.registry.get(options.connectionName);\n const em = orm.em.fork(true, true);\n\n ctx.set(em.name, em);\n\n const runInTransaction = () => this.executeInTransaction(options.connectionName!, next);\n\n return this.context.run(ctx, () => (options.retry ? this.retryStrategy?.acquire(runInTransaction) : runInTransaction()));\n }\n\n private extractContextName(context: InterceptorContext<unknown>): TransactionOptions {\n const options = context.options || ({} as Partial<TransactionOptions> | string);\n\n let connectionName: string | undefined;\n let retry: boolean | undefined;\n\n if (typeof options === \"string\") {\n connectionName = options;\n } else if (options) {\n connectionName = options.connectionName;\n retry = options.retry;\n }\n\n if (!connectionName) {\n connectionName = \"default\";\n }\n\n if (!retry) {\n retry = false;\n }\n\n return {connectionName, retry};\n }\n\n private async executeInTransaction(connectionName: string, next: InterceptorNext): Promise<unknown> {\n const em = this.context.get(connectionName);\n\n const result = await next();\n\n await em?.flush();\n\n return result;\n }\n}\n"]}
@@ -1,11 +1,11 @@
1
1
  import { MikroOrmFactory } from "./MikroOrmFactory";
2
- import { InjectorService } from "@tsed/di";
3
2
  import { IDatabaseDriver as DatabaseDriver, MikroORM, Options } from "@mikro-orm/core";
3
+ import { Logger } from "@tsed/logger";
4
4
  export declare class MikroOrmRegistry {
5
- private readonly injector;
5
+ private readonly logger;
6
6
  private readonly mikroOrmFactory;
7
7
  private readonly connections;
8
- constructor(injector: InjectorService, mikroOrmFactory: MikroOrmFactory);
8
+ constructor(logger: Logger, mikroOrmFactory: MikroOrmFactory);
9
9
  createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM>;
10
10
  get(id?: string): MikroORM;
11
11
  has(id?: string): boolean;
@@ -5,9 +5,10 @@ const tslib_1 = require("tslib");
5
5
  const MikroOrmFactory_1 = require("./MikroOrmFactory");
6
6
  const di_1 = require("@tsed/di");
7
7
  const core_1 = require("@tsed/core");
8
+ const logger_1 = require("@tsed/logger");
8
9
  let MikroOrmRegistry = class MikroOrmRegistry {
9
- constructor(injector, mikroOrmFactory) {
10
- this.injector = injector;
10
+ constructor(logger, mikroOrmFactory) {
11
+ this.logger = logger;
11
12
  this.mikroOrmFactory = mikroOrmFactory;
12
13
  this.connections = new Map();
13
14
  }
@@ -16,11 +17,11 @@ let MikroOrmRegistry = class MikroOrmRegistry {
16
17
  if (this.has(connectionName)) {
17
18
  return this.get(connectionName);
18
19
  }
19
- this.injector.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);
20
- this.injector.logger.debug(`options: %j`, connectionOptions);
20
+ this.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);
21
+ this.logger.debug(`options: %j`, connectionOptions);
21
22
  const connection = await this.mikroOrmFactory.create(connectionOptions);
22
23
  this.connections.set(connectionName, connection);
23
- this.injector.logger.info(`Connected with MikroOrm to database: %s`, connectionName);
24
+ this.logger.info(`Connected with MikroOrm to database: %s`, connectionName);
24
25
  return connection;
25
26
  }
26
27
  get(id = "default") {
@@ -45,7 +46,7 @@ MikroOrmRegistry = (0, tslib_1.__decorate)([
45
46
  (0, di_1.Injectable)(),
46
47
  (0, tslib_1.__param)(0, (0, di_1.Inject)()),
47
48
  (0, tslib_1.__param)(1, (0, di_1.Inject)()),
48
- (0, tslib_1.__metadata)("design:paramtypes", [di_1.InjectorService, MikroOrmFactory_1.MikroOrmFactory])
49
+ (0, tslib_1.__metadata)("design:paramtypes", [logger_1.Logger, MikroOrmFactory_1.MikroOrmFactory])
49
50
  ], MikroOrmRegistry);
50
51
  exports.MikroOrmRegistry = MikroOrmRegistry;
51
52
  //# sourceMappingURL=MikroOrmRegistry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MikroOrmRegistry.js","sourceRoot":"","sources":["../../../src/services/MikroOrmRegistry.ts"],"names":[],"mappings":";;;;AAAA,uDAAkD;AAClD,iCAA6D;AAE7D,qCAAqD;AAGrD,IAAa,gBAAgB,GAA7B,MAAa,gBAAgB;IAG3B,YAAuC,QAAyB,EAA6B,eAAgC;QAAtF,aAAQ,GAAR,QAAQ,CAAiB;QAA6B,oBAAe,GAAf,eAAe,CAAiB;QAF5G,gBAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAEqE,CAAC;IAE1H,KAAK,CAAC,gBAAgB,CAA2B,iBAA6B;QACnF,MAAM,cAAc,GAAG,IAAA,eAAQ,EAAS,iBAAiB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAErF,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE;YAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;SACjC;QAED,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,iDAAiD,EAAE,cAAc,CAAC,CAAC;QAC7F,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;QAE7D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAExE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAEjD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,EAAE,cAAc,CAAC,CAAC;QAErF,OAAO,UAAU,CAAC;IACpB,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,oEAAoE;QACpE,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;IACnC,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAEM,KAAK,CAAC,gBAAgB;QAC3B,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;QAEnD,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAoB,EAAE,EAAE,CAAC,IAAA,sBAAe,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAE9G,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,UAAoB;QACxC,IAAI,MAAM,UAAU,CAAC,WAAW,EAAE,EAAE;YAClC,MAAM,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAC/B;IACH,CAAC;CACF,CAAA;AA9CY,gBAAgB;IAD5B,IAAA,eAAU,GAAE;IAIE,wBAAA,IAAA,WAAM,GAAE,CAAA;IAA8C,wBAAA,IAAA,WAAM,GAAE,CAAA;kDAA1B,oBAAe,EAA8C,iCAAe;GAHlH,gBAAgB,CA8C5B;AA9CY,4CAAgB","sourcesContent":["import {MikroOrmFactory} from \"./MikroOrmFactory\";\nimport {Inject, Injectable, InjectorService} from \"@tsed/di\";\nimport {IDatabaseDriver as DatabaseDriver, MikroORM, Options} from \"@mikro-orm/core\";\nimport {catchAsyncError, getValue} from \"@tsed/core\";\n\n@Injectable()\nexport class MikroOrmRegistry {\n private readonly connections = new Map<string, MikroORM>();\n\n constructor(@Inject() private readonly injector: InjectorService, @Inject() private readonly mikroOrmFactory: MikroOrmFactory) {}\n\n public async createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM> {\n const connectionName = getValue<string>(connectionOptions, \"contextName\", \"default\");\n\n if (this.has(connectionName)) {\n return this.get(connectionName);\n }\n\n this.injector.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);\n this.injector.logger.debug(`options: %j`, connectionOptions);\n\n const connection = await this.mikroOrmFactory.create(connectionOptions);\n\n this.connections.set(connectionName, connection);\n\n this.injector.logger.info(`Connected with MikroOrm to database: %s`, connectionName);\n\n return connection;\n }\n\n public get(id: string = \"default\"): MikroORM {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.connections.get(id)!;\n }\n\n public has(id: string = \"default\"): boolean {\n return this.connections.has(id);\n }\n\n public async closeConnections(): Promise<void> {\n const connections = [...this.connections.values()];\n\n await Promise.all(connections.map((connection: MikroORM) => catchAsyncError(() => this.dispose(connection))));\n\n this.connections.clear();\n }\n\n private async dispose(connection: MikroORM): Promise<void> {\n if (await connection.isConnected()) {\n await connection.close(false);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"MikroOrmRegistry.js","sourceRoot":"","sources":["../../../src/services/MikroOrmRegistry.ts"],"names":[],"mappings":";;;;AAAA,uDAAkD;AAClD,iCAA4C;AAE5C,qCAAqD;AACrD,yCAAoC;AAGpC,IAAa,gBAAgB,GAA7B,MAAa,gBAAgB;IAG3B,YAAuC,MAAc,EAA6B,eAAgC;QAA3E,WAAM,GAAN,MAAM,CAAQ;QAA6B,oBAAe,GAAf,eAAe,CAAiB;QAFjG,gBAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE0D,CAAC;IAE/G,KAAK,CAAC,gBAAgB,CAA2B,iBAA6B;QACnF,MAAM,cAAc,GAAG,IAAA,eAAQ,EAAS,iBAAiB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAErF,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE;YAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;SACjC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iDAAiD,EAAE,cAAc,CAAC,CAAC;QACpF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;QAEpD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAExE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAEjD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,EAAE,cAAc,CAAC,CAAC;QAE5E,OAAO,UAAU,CAAC;IACpB,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,oEAAoE;QACpE,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;IACnC,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAEM,KAAK,CAAC,gBAAgB;QAC3B,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;QAEnD,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAoB,EAAE,EAAE,CAAC,IAAA,sBAAe,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAE9G,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,UAAoB;QACxC,IAAI,MAAM,UAAU,CAAC,WAAW,EAAE,EAAE;YAClC,MAAM,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAC/B;IACH,CAAC;CACF,CAAA;AA9CY,gBAAgB;IAD5B,IAAA,eAAU,GAAE;IAIE,wBAAA,IAAA,WAAM,GAAE,CAAA;IAAmC,wBAAA,IAAA,WAAM,GAAE,CAAA;kDAAjB,eAAM,EAA8C,iCAAe;GAHvG,gBAAgB,CA8C5B;AA9CY,4CAAgB","sourcesContent":["import {MikroOrmFactory} from \"./MikroOrmFactory\";\nimport {Inject, Injectable} from \"@tsed/di\";\nimport {IDatabaseDriver as DatabaseDriver, MikroORM, Options} from \"@mikro-orm/core\";\nimport {catchAsyncError, getValue} from \"@tsed/core\";\nimport {Logger} from \"@tsed/logger\";\n\n@Injectable()\nexport class MikroOrmRegistry {\n private readonly connections = new Map<string, MikroORM>();\n\n constructor(@Inject() private readonly logger: Logger, @Inject() private readonly mikroOrmFactory: MikroOrmFactory) {}\n\n public async createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM> {\n const connectionName = getValue<string>(connectionOptions, \"contextName\", \"default\");\n\n if (this.has(connectionName)) {\n return this.get(connectionName);\n }\n\n this.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);\n this.logger.debug(`options: %j`, connectionOptions);\n\n const connection = await this.mikroOrmFactory.create(connectionOptions);\n\n this.connections.set(connectionName, connection);\n\n this.logger.info(`Connected with MikroOrm to database: %s`, connectionName);\n\n return connection;\n }\n\n public get(id: string = \"default\"): MikroORM {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.connections.get(id)!;\n }\n\n public has(id: string = \"default\"): boolean {\n return this.connections.has(id);\n }\n\n public async closeConnections(): Promise<void> {\n const connections = [...this.connections.values()];\n\n await Promise.all(connections.map((connection: MikroORM) => catchAsyncError(() => this.dispose(connection))));\n\n this.connections.clear();\n }\n\n private async dispose(connection: MikroORM): Promise<void> {\n if (await connection.isConnected()) {\n await connection.close(false);\n }\n }\n}\n"]}
@@ -1,3 +1,4 @@
1
1
  export interface RetryStrategy {
2
2
  acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;
3
3
  }
4
+ export declare const RetryStrategy: unique symbol;
@@ -1,3 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RetryStrategy = void 0;
4
+ exports.RetryStrategy = Symbol("RetryStrategy");
3
5
  //# sourceMappingURL=RetryStrategy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RetryStrategy.js","sourceRoot":"","sources":["../../../src/services/RetryStrategy.ts"],"names":[],"mappings":"","sourcesContent":["export interface RetryStrategy {\n acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;\n}\n"]}
1
+ {"version":3,"file":"RetryStrategy.js","sourceRoot":"","sources":["../../../src/services/RetryStrategy.ts"],"names":[],"mappings":";;;AAIa,QAAA,aAAa,GAAkB,MAAM,CAAC,eAAe,CAAC,CAAC","sourcesContent":["export interface RetryStrategy {\n acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;\n}\n\nexport const RetryStrategy: unique symbol = Symbol(\"RetryStrategy\");\n"]}
@@ -1,6 +1,6 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
- import { DBContext, MikroOrmRegistry } from "./services";
3
- import { Configuration, Constant, Inject, Module } from "@tsed/di";
2
+ import { DBContext, MikroOrmRegistry, RetryStrategy } from "./services";
3
+ import { Configuration, Constant, Inject, Module, registerProvider } from "@tsed/di";
4
4
  let MikroOrmModule = class MikroOrmModule {
5
5
  async $onInit() {
6
6
  const promises = this.settings.map((opts) => this.mikroOrmRegistry.createConnection(opts));
@@ -25,4 +25,6 @@ MikroOrmModule = __decorate([
25
25
  })
26
26
  ], MikroOrmModule);
27
27
  export { MikroOrmModule };
28
+ // TODO: the IoC container should provide null or undefined by default until tsedio/tsed#1694 is closed
29
+ registerProvider({ provide: RetryStrategy, useValue: null });
28
30
  //# sourceMappingURL=MikroOrmModule.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MikroOrmModule.js","sourceRoot":"","sources":["../../src/MikroOrmModule.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,SAAS,EAAE,gBAAgB,EAAC,MAAM,YAAY,CAAC;AACvD,OAAO,EAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAoB,MAAM,UAAU,CAAC;AAoBpF,IAAa,cAAc,GAA3B,MAAa,cAAc;IAOlB,KAAK,CAAC,OAAO;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;QAE3F,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAEM,UAAU;QACf,OAAO,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;IAClD,CAAC;CACF,CAAA;AAdC;IADC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC;;gDACa;AAGtC;IADC,MAAM,EAAE;8BAC2B,gBAAgB;wDAAC;AAL1C,cAAc;IAJ1B,MAAM,CAAC;QACN,OAAO,EAAE,CAAC,SAAS,EAAE,gBAAgB,CAAC;QACtC,IAAI,EAAE,CAAC,aAAa,EAAE,gBAAgB,CAAC;KACxC,CAAC;GACW,cAAc,CAgB1B;SAhBY,cAAc","sourcesContent":["import {DBContext, MikroOrmRegistry} from \"./services\";\nimport {Configuration, Constant, Inject, Module, OnDestroy, OnInit} from \"@tsed/di\";\nimport {Options} from \"@mikro-orm/core\";\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace TsED {\n interface Configuration {\n /**\n * The the ORM configuration, entity metadata.\n * If you omit the `options` parameter, your CLI config will be used.\n */\n mikroOrm?: Options[];\n }\n }\n}\n\n@Module({\n imports: [DBContext, MikroOrmRegistry],\n deps: [Configuration, MikroOrmRegistry]\n})\nexport class MikroOrmModule implements OnDestroy, OnInit {\n @Constant(\"mikroOrm\", [])\n private readonly settings!: Options[];\n\n @Inject()\n private readonly mikroOrmRegistry!: MikroOrmRegistry;\n\n public async $onInit(): Promise<void> {\n const promises = this.settings.map((opts) => this.mikroOrmRegistry.createConnection(opts));\n\n await Promise.all(promises);\n }\n\n public $onDestroy(): Promise<void> {\n return this.mikroOrmRegistry.closeConnections();\n }\n}\n"]}
1
+ {"version":3,"file":"MikroOrmModule.js","sourceRoot":"","sources":["../../src/MikroOrmModule.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAC,MAAM,YAAY,CAAC;AACtE,OAAO,EAAC,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAqB,gBAAgB,EAAC,MAAM,UAAU,CAAC;AAoBtG,IAAa,cAAc,GAA3B,MAAa,cAAc;IAOlB,KAAK,CAAC,OAAO;QAClB,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;QAE3F,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAEM,UAAU;QACf,OAAO,IAAI,CAAC,gBAAgB,CAAC,gBAAgB,EAAE,CAAC;IAClD,CAAC;CACF,CAAA;AAdC;IADC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC;;gDACa;AAGtC;IADC,MAAM,EAAE;8BAC2B,gBAAgB;wDAAC;AAL1C,cAAc;IAJ1B,MAAM,CAAC;QACN,OAAO,EAAE,CAAC,SAAS,EAAE,gBAAgB,CAAC;QACtC,IAAI,EAAE,CAAC,aAAa,EAAE,gBAAgB,CAAC;KACxC,CAAC;GACW,cAAc,CAgB1B;SAhBY,cAAc;AAkB3B,uGAAuG;AACvG,gBAAgB,CAAC,EAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC,CAAC","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"./services\";\nimport {Configuration, Constant, Inject, Module, OnDestroy, OnInit, registerProvider} from \"@tsed/di\";\nimport {Options} from \"@mikro-orm/core\";\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace TsED {\n interface Configuration {\n /**\n * The the ORM configuration, entity metadata.\n * If you omit the `options` parameter, your CLI config will be used.\n */\n mikroOrm?: Options[];\n }\n }\n}\n\n@Module({\n imports: [DBContext, MikroOrmRegistry],\n deps: [Configuration, MikroOrmRegistry]\n})\nexport class MikroOrmModule implements OnDestroy, OnInit {\n @Constant(\"mikroOrm\", [])\n private readonly settings!: Options[];\n\n @Inject()\n private readonly mikroOrmRegistry!: MikroOrmRegistry;\n\n public async $onInit(): Promise<void> {\n const promises = this.settings.map((opts) => this.mikroOrmRegistry.createConnection(opts));\n\n await Promise.all(promises);\n }\n\n public $onDestroy(): Promise<void> {\n return this.mikroOrmRegistry.closeConnections();\n }\n}\n\n// TODO: the IoC container should provide null or undefined by default until tsedio/tsed#1694 is closed\nregisterProvider({provide: RetryStrategy, useValue: null});\n"]}
@@ -0,0 +1,17 @@
1
+ import { Inject } from "@tsed/di";
2
+ import { MikroOrmRegistry } from "../services";
3
+ /**
4
+ * Get the entity manager for the given connection name.
5
+ * @param connectionName
6
+ * @decorator
7
+ * @mikroOrm
8
+ */
9
+ export const EntityManager = (connectionName) => Inject(MikroOrmRegistry, (registry) => { var _a; return (_a = registry.get(connectionName)) === null || _a === void 0 ? void 0 : _a.em; });
10
+ /**
11
+ * Get the entity manager for the given connection name.
12
+ * @param connectionName
13
+ * @decorator
14
+ * @mikroOrm
15
+ */
16
+ export const Em = EntityManager;
17
+ //# sourceMappingURL=entityManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entityManager.js","sourceRoot":"","sources":["../../../src/decorators/entityManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,UAAU,CAAC;AAChC,OAAO,EAAC,gBAAgB,EAAC,MAAM,aAAa,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,cAAuB,EAAqB,EAAE,CAC1E,MAAM,CAAC,gBAAgB,EAAE,CAAC,QAA0B,EAAE,EAAE,WAAC,OAAA,MAAA,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,0CAAE,EAAE,CAAA,EAAA,CAAsB,CAAC;AAElH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,EAAE,GAAG,aAAa,CAAC","sourcesContent":["import {Inject} from \"@tsed/di\";\nimport {MikroOrmRegistry} from \"../services\";\n\n/**\n * Get the entity manager for the given connection name.\n * @param connectionName\n * @decorator\n * @mikroOrm\n */\nexport const EntityManager = (connectionName?: string): PropertyDecorator =>\n Inject(MikroOrmRegistry, (registry: MikroOrmRegistry) => registry.get(connectionName)?.em) as PropertyDecorator;\n\n/**\n * Get the entity manager for the given connection name.\n * @param connectionName\n * @decorator\n * @mikroOrm\n */\nexport const Em = EntityManager;\n"]}
@@ -1,3 +1,4 @@
1
1
  export * from "./transactional";
2
2
  export * from "./connection";
3
+ export * from "./entityManager";
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC","sourcesContent":["export * from \"./transactional\";\nexport * from \"./connection\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC","sourcesContent":["export * from \"./transactional\";\nexport * from \"./connection\";\nexport * from \"./entityManager\";\n"]}
@@ -1,13 +1,19 @@
1
1
  import { __decorate, __metadata, __param } from "tslib";
2
- import { DBContext, MikroOrmRegistry } from "../services";
2
+ import { DBContext, MikroOrmRegistry, RetryStrategy } from "../services";
3
3
  import { Inject, Interceptor } from "@tsed/di";
4
+ import { Logger } from "@tsed/logger";
4
5
  let TransactionalInterceptor = class TransactionalInterceptor {
5
- constructor(registry, context) {
6
+ constructor(registry, context, logger, retryStrategy) {
6
7
  this.registry = registry;
7
8
  this.context = context;
9
+ this.logger = logger;
10
+ this.retryStrategy = retryStrategy;
8
11
  }
9
12
  async intercept(context, next) {
10
13
  const options = this.extractContextName(context);
14
+ if (options.retry && !this.retryStrategy) {
15
+ this.logger.warn(`To retry a transaction you have to implement a "${RetryStrategy.description}" interface`);
16
+ }
11
17
  const ctx = this.context.getContext();
12
18
  if (!ctx) {
13
19
  return this.runWithinNewCtx(next, options);
@@ -26,23 +32,26 @@ let TransactionalInterceptor = class TransactionalInterceptor {
26
32
  const em = orm.em.fork(true, true);
27
33
  ctx.set(em.name, em);
28
34
  const runInTransaction = () => this.executeInTransaction(options.connectionName, next);
29
- return this.context.run(ctx, () => (options.retryStrategy ? options.retryStrategy.acquire(runInTransaction) : runInTransaction()));
35
+ return this.context.run(ctx, () => { var _a; return (options.retry ? (_a = this.retryStrategy) === null || _a === void 0 ? void 0 : _a.acquire(runInTransaction) : runInTransaction()); });
30
36
  }
31
37
  extractContextName(context) {
32
38
  const options = context.options || {};
33
39
  let connectionName;
34
- let retryStrategy;
40
+ let retry;
35
41
  if (typeof options === "string") {
36
42
  connectionName = options;
37
43
  }
38
44
  else if (options) {
39
45
  connectionName = options.connectionName;
40
- retryStrategy = options.retryStrategy;
46
+ retry = options.retry;
41
47
  }
42
48
  if (!connectionName) {
43
49
  connectionName = "default";
44
50
  }
45
- return { connectionName, retryStrategy };
51
+ if (!retry) {
52
+ retry = false;
53
+ }
54
+ return { connectionName, retry };
46
55
  }
47
56
  async executeInTransaction(connectionName, next) {
48
57
  const em = this.context.get(connectionName);
@@ -55,7 +64,11 @@ TransactionalInterceptor = __decorate([
55
64
  Interceptor(),
56
65
  __param(0, Inject()),
57
66
  __param(1, Inject()),
58
- __metadata("design:paramtypes", [MikroOrmRegistry, DBContext])
67
+ __param(2, Inject()),
68
+ __param(3, Inject(RetryStrategy)),
69
+ __metadata("design:paramtypes", [MikroOrmRegistry,
70
+ DBContext,
71
+ Logger, Object])
59
72
  ], TransactionalInterceptor);
60
73
  export { TransactionalInterceptor };
61
74
  //# sourceMappingURL=TransactionalInterceptor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TransactionalInterceptor.js","sourceRoot":"","sources":["../../../src/interceptors/TransactionalInterceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"../services\";\nimport {Inject, Interceptor, InterceptorContext, InterceptorMethods, InterceptorNext} from \"@tsed/di\";\nimport {EntityManager} from \"@mikro-orm/core\";\n\nexport interface TransactionOptions {\n connectionName?: string;\n retryStrategy?: RetryStrategy;\n}\n\n@Interceptor()\nexport class TransactionalInterceptor implements InterceptorMethods {\n constructor(@Inject() private readonly registry: MikroOrmRegistry, @Inject() private readonly context: DBContext) {}\n\n public async intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown> {\n const options = this.extractContextName(context);\n\n const ctx = this.context.getContext();\n\n if (!ctx) {\n return this.runWithinNewCtx(next, options);\n }\n\n if (ctx.has(options.connectionName!)) {\n return next();\n }\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runWithinNewCtx(next: InterceptorNext, options: TransactionOptions): Promise<unknown> | unknown {\n const ctx = new Map<string, EntityManager>();\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runUnderCtx(next: InterceptorNext, ctx: Map<string, EntityManager>, options: TransactionOptions): Promise<unknown> | unknown {\n const orm = this.registry.get(options.connectionName);\n const em = orm.em.fork(true, true);\n\n ctx.set(em.name, em);\n\n const runInTransaction = () => this.executeInTransaction(options.connectionName!, next);\n\n return this.context.run(ctx, () => (options.retryStrategy ? options.retryStrategy.acquire(runInTransaction) : runInTransaction()));\n }\n\n private extractContextName(context: InterceptorContext<unknown>): TransactionOptions {\n const options = context.options || ({} as Partial<TransactionOptions> | string);\n\n let connectionName: string | undefined;\n let retryStrategy: RetryStrategy | undefined;\n\n if (typeof options === \"string\") {\n connectionName = options;\n } else if (options) {\n connectionName = options.connectionName;\n retryStrategy = options.retryStrategy;\n }\n\n if (!connectionName) {\n connectionName = \"default\";\n }\n\n return {connectionName, retryStrategy};\n }\n\n private async executeInTransaction(connectionName: string, next: InterceptorNext): Promise<unknown> {\n const em = this.context.get(connectionName);\n\n const result = await next();\n\n await em?.flush();\n\n return result;\n }\n}\n"]}
1
+ {"version":3,"file":"TransactionalInterceptor.js","sourceRoot":"","sources":["../../../src/interceptors/TransactionalInterceptor.ts"],"names":[],"mappings":"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","sourcesContent":["import {DBContext, MikroOrmRegistry, RetryStrategy} from \"../services\";\nimport {Inject, Interceptor, InterceptorContext, InterceptorMethods, InterceptorNext} from \"@tsed/di\";\nimport {EntityManager} from \"@mikro-orm/core\";\nimport {Logger} from \"@tsed/logger\";\n\nexport interface TransactionOptions {\n connectionName?: string;\n retry?: boolean;\n}\n\n@Interceptor()\nexport class TransactionalInterceptor implements InterceptorMethods {\n constructor(\n @Inject() private readonly registry: MikroOrmRegistry,\n @Inject() private readonly context: DBContext,\n @Inject() private readonly logger: Logger,\n @Inject(RetryStrategy)\n private readonly retryStrategy?: RetryStrategy\n ) {}\n\n public async intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown> {\n const options = this.extractContextName(context);\n\n if (options.retry && !this.retryStrategy) {\n this.logger.warn(`To retry a transaction you have to implement a \"${RetryStrategy.description}\" interface`);\n }\n\n const ctx = this.context.getContext();\n\n if (!ctx) {\n return this.runWithinNewCtx(next, options);\n }\n\n if (ctx.has(options.connectionName!)) {\n return next();\n }\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runWithinNewCtx(next: InterceptorNext, options: TransactionOptions): Promise<unknown> | unknown {\n const ctx = new Map<string, EntityManager>();\n\n return this.runUnderCtx(next, ctx, options);\n }\n\n private runUnderCtx(next: InterceptorNext, ctx: Map<string, EntityManager>, options: TransactionOptions): Promise<unknown> | unknown {\n const orm = this.registry.get(options.connectionName);\n const em = orm.em.fork(true, true);\n\n ctx.set(em.name, em);\n\n const runInTransaction = () => this.executeInTransaction(options.connectionName!, next);\n\n return this.context.run(ctx, () => (options.retry ? this.retryStrategy?.acquire(runInTransaction) : runInTransaction()));\n }\n\n private extractContextName(context: InterceptorContext<unknown>): TransactionOptions {\n const options = context.options || ({} as Partial<TransactionOptions> | string);\n\n let connectionName: string | undefined;\n let retry: boolean | undefined;\n\n if (typeof options === \"string\") {\n connectionName = options;\n } else if (options) {\n connectionName = options.connectionName;\n retry = options.retry;\n }\n\n if (!connectionName) {\n connectionName = \"default\";\n }\n\n if (!retry) {\n retry = false;\n }\n\n return {connectionName, retry};\n }\n\n private async executeInTransaction(connectionName: string, next: InterceptorNext): Promise<unknown> {\n const em = this.context.get(connectionName);\n\n const result = await next();\n\n await em?.flush();\n\n return result;\n }\n}\n"]}
@@ -1,10 +1,11 @@
1
1
  import { __decorate, __metadata, __param } from "tslib";
2
2
  import { MikroOrmFactory } from "./MikroOrmFactory";
3
- import { Inject, Injectable, InjectorService } from "@tsed/di";
3
+ import { Inject, Injectable } from "@tsed/di";
4
4
  import { catchAsyncError, getValue } from "@tsed/core";
5
+ import { Logger } from "@tsed/logger";
5
6
  let MikroOrmRegistry = class MikroOrmRegistry {
6
- constructor(injector, mikroOrmFactory) {
7
- this.injector = injector;
7
+ constructor(logger, mikroOrmFactory) {
8
+ this.logger = logger;
8
9
  this.mikroOrmFactory = mikroOrmFactory;
9
10
  this.connections = new Map();
10
11
  }
@@ -13,11 +14,11 @@ let MikroOrmRegistry = class MikroOrmRegistry {
13
14
  if (this.has(connectionName)) {
14
15
  return this.get(connectionName);
15
16
  }
16
- this.injector.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);
17
- this.injector.logger.debug(`options: %j`, connectionOptions);
17
+ this.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);
18
+ this.logger.debug(`options: %j`, connectionOptions);
18
19
  const connection = await this.mikroOrmFactory.create(connectionOptions);
19
20
  this.connections.set(connectionName, connection);
20
- this.injector.logger.info(`Connected with MikroOrm to database: %s`, connectionName);
21
+ this.logger.info(`Connected with MikroOrm to database: %s`, connectionName);
21
22
  return connection;
22
23
  }
23
24
  get(id = "default") {
@@ -42,7 +43,7 @@ MikroOrmRegistry = __decorate([
42
43
  Injectable(),
43
44
  __param(0, Inject()),
44
45
  __param(1, Inject()),
45
- __metadata("design:paramtypes", [InjectorService, MikroOrmFactory])
46
+ __metadata("design:paramtypes", [Logger, MikroOrmFactory])
46
47
  ], MikroOrmRegistry);
47
48
  export { MikroOrmRegistry };
48
49
  //# sourceMappingURL=MikroOrmRegistry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MikroOrmRegistry.js","sourceRoot":"","sources":["../../../src/services/MikroOrmRegistry.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAC,MAAM,EAAE,UAAU,EAAE,eAAe,EAAC,MAAM,UAAU,CAAC;AAE7D,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,YAAY,CAAC;AAGrD,IAAa,gBAAgB,GAA7B,MAAa,gBAAgB;IAG3B,YAAuC,QAAyB,EAA6B,eAAgC;QAAtF,aAAQ,GAAR,QAAQ,CAAiB;QAA6B,oBAAe,GAAf,eAAe,CAAiB;QAF5G,gBAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAEqE,CAAC;IAE1H,KAAK,CAAC,gBAAgB,CAA2B,iBAA6B;QACnF,MAAM,cAAc,GAAG,QAAQ,CAAS,iBAAiB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAErF,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE;YAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;SACjC;QAED,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,iDAAiD,EAAE,cAAc,CAAC,CAAC;QAC7F,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;QAE7D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAExE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAEjD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,EAAE,cAAc,CAAC,CAAC;QAErF,OAAO,UAAU,CAAC;IACpB,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,oEAAoE;QACpE,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;IACnC,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAEM,KAAK,CAAC,gBAAgB;QAC3B,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;QAEnD,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAoB,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAE9G,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,UAAoB;QACxC,IAAI,MAAM,UAAU,CAAC,WAAW,EAAE,EAAE;YAClC,MAAM,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAC/B;IACH,CAAC;CACF,CAAA;AA9CY,gBAAgB;IAD5B,UAAU,EAAE;IAIE,WAAA,MAAM,EAAE,CAAA;IAA8C,WAAA,MAAM,EAAE,CAAA;qCAA1B,eAAe,EAA8C,eAAe;GAHlH,gBAAgB,CA8C5B;SA9CY,gBAAgB","sourcesContent":["import {MikroOrmFactory} from \"./MikroOrmFactory\";\nimport {Inject, Injectable, InjectorService} from \"@tsed/di\";\nimport {IDatabaseDriver as DatabaseDriver, MikroORM, Options} from \"@mikro-orm/core\";\nimport {catchAsyncError, getValue} from \"@tsed/core\";\n\n@Injectable()\nexport class MikroOrmRegistry {\n private readonly connections = new Map<string, MikroORM>();\n\n constructor(@Inject() private readonly injector: InjectorService, @Inject() private readonly mikroOrmFactory: MikroOrmFactory) {}\n\n public async createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM> {\n const connectionName = getValue<string>(connectionOptions, \"contextName\", \"default\");\n\n if (this.has(connectionName)) {\n return this.get(connectionName);\n }\n\n this.injector.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);\n this.injector.logger.debug(`options: %j`, connectionOptions);\n\n const connection = await this.mikroOrmFactory.create(connectionOptions);\n\n this.connections.set(connectionName, connection);\n\n this.injector.logger.info(`Connected with MikroOrm to database: %s`, connectionName);\n\n return connection;\n }\n\n public get(id: string = \"default\"): MikroORM {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.connections.get(id)!;\n }\n\n public has(id: string = \"default\"): boolean {\n return this.connections.has(id);\n }\n\n public async closeConnections(): Promise<void> {\n const connections = [...this.connections.values()];\n\n await Promise.all(connections.map((connection: MikroORM) => catchAsyncError(() => this.dispose(connection))));\n\n this.connections.clear();\n }\n\n private async dispose(connection: MikroORM): Promise<void> {\n if (await connection.isConnected()) {\n await connection.close(false);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"MikroOrmRegistry.js","sourceRoot":"","sources":["../../../src/services/MikroOrmRegistry.ts"],"names":[],"mappings":";AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAC,MAAM,EAAE,UAAU,EAAC,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAC,eAAe,EAAE,QAAQ,EAAC,MAAM,YAAY,CAAC;AACrD,OAAO,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AAGpC,IAAa,gBAAgB,GAA7B,MAAa,gBAAgB;IAG3B,YAAuC,MAAc,EAA6B,eAAgC;QAA3E,WAAM,GAAN,MAAM,CAAQ;QAA6B,oBAAe,GAAf,eAAe,CAAiB;QAFjG,gBAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE0D,CAAC;IAE/G,KAAK,CAAC,gBAAgB,CAA2B,iBAA6B;QACnF,MAAM,cAAc,GAAG,QAAQ,CAAS,iBAAiB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAErF,IAAI,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE;YAC5B,OAAO,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;SACjC;QAED,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,iDAAiD,EAAE,cAAc,CAAC,CAAC;QACpF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;QAEpD,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAExE,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAEjD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,EAAE,cAAc,CAAC,CAAC;QAE5E,OAAO,UAAU,CAAC;IACpB,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,oEAAoE;QACpE,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;IACnC,CAAC;IAEM,GAAG,CAAC,KAAa,SAAS;QAC/B,OAAO,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAClC,CAAC;IAEM,KAAK,CAAC,gBAAgB;QAC3B,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC;QAEnD,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAoB,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAE9G,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,UAAoB;QACxC,IAAI,MAAM,UAAU,CAAC,WAAW,EAAE,EAAE;YAClC,MAAM,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;SAC/B;IACH,CAAC;CACF,CAAA;AA9CY,gBAAgB;IAD5B,UAAU,EAAE;IAIE,WAAA,MAAM,EAAE,CAAA;IAAmC,WAAA,MAAM,EAAE,CAAA;qCAAjB,MAAM,EAA8C,eAAe;GAHvG,gBAAgB,CA8C5B;SA9CY,gBAAgB","sourcesContent":["import {MikroOrmFactory} from \"./MikroOrmFactory\";\nimport {Inject, Injectable} from \"@tsed/di\";\nimport {IDatabaseDriver as DatabaseDriver, MikroORM, Options} from \"@mikro-orm/core\";\nimport {catchAsyncError, getValue} from \"@tsed/core\";\nimport {Logger} from \"@tsed/logger\";\n\n@Injectable()\nexport class MikroOrmRegistry {\n private readonly connections = new Map<string, MikroORM>();\n\n constructor(@Inject() private readonly logger: Logger, @Inject() private readonly mikroOrmFactory: MikroOrmFactory) {}\n\n public async createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM> {\n const connectionName = getValue<string>(connectionOptions, \"contextName\", \"default\");\n\n if (this.has(connectionName)) {\n return this.get(connectionName);\n }\n\n this.logger.info(`Create connection with MikroOrm to database: %s`, connectionName);\n this.logger.debug(`options: %j`, connectionOptions);\n\n const connection = await this.mikroOrmFactory.create(connectionOptions);\n\n this.connections.set(connectionName, connection);\n\n this.logger.info(`Connected with MikroOrm to database: %s`, connectionName);\n\n return connection;\n }\n\n public get(id: string = \"default\"): MikroORM {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.connections.get(id)!;\n }\n\n public has(id: string = \"default\"): boolean {\n return this.connections.has(id);\n }\n\n public async closeConnections(): Promise<void> {\n const connections = [...this.connections.values()];\n\n await Promise.all(connections.map((connection: MikroORM) => catchAsyncError(() => this.dispose(connection))));\n\n this.connections.clear();\n }\n\n private async dispose(connection: MikroORM): Promise<void> {\n if (await connection.isConnected()) {\n await connection.close(false);\n }\n }\n}\n"]}
@@ -1,2 +1,2 @@
1
- export {};
1
+ export const RetryStrategy = Symbol("RetryStrategy");
2
2
  //# sourceMappingURL=RetryStrategy.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RetryStrategy.js","sourceRoot":"","sources":["../../../src/services/RetryStrategy.ts"],"names":[],"mappings":"","sourcesContent":["export interface RetryStrategy {\n acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;\n}\n"]}
1
+ {"version":3,"file":"RetryStrategy.js","sourceRoot":"","sources":["../../../src/services/RetryStrategy.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,aAAa,GAAkB,MAAM,CAAC,eAAe,CAAC,CAAC","sourcesContent":["export interface RetryStrategy {\n acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;\n}\n\nexport const RetryStrategy: unique symbol = Symbol(\"RetryStrategy\");\n"]}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Get the entity manager for the given connection name.
3
+ * @param connectionName
4
+ * @decorator
5
+ * @mikroOrm
6
+ */
7
+ export declare const EntityManager: (connectionName?: string | undefined) => PropertyDecorator;
8
+ /**
9
+ * Get the entity manager for the given connection name.
10
+ * @param connectionName
11
+ * @decorator
12
+ * @mikroOrm
13
+ */
14
+ export declare const Em: (connectionName?: string | undefined) => PropertyDecorator;
@@ -1,2 +1,3 @@
1
1
  export * from "./transactional";
2
2
  export * from "./connection";
3
+ export * from "./entityManager";
@@ -1,13 +1,16 @@
1
1
  import { DBContext, MikroOrmRegistry, RetryStrategy } from "../services";
2
2
  import { InterceptorContext, InterceptorMethods, InterceptorNext } from "@tsed/di";
3
+ import { Logger } from "@tsed/logger";
3
4
  export interface TransactionOptions {
4
5
  connectionName?: string;
5
- retryStrategy?: RetryStrategy;
6
+ retry?: boolean;
6
7
  }
7
8
  export declare class TransactionalInterceptor implements InterceptorMethods {
8
9
  private readonly registry;
9
10
  private readonly context;
10
- constructor(registry: MikroOrmRegistry, context: DBContext);
11
+ private readonly logger;
12
+ private readonly retryStrategy?;
13
+ constructor(registry: MikroOrmRegistry, context: DBContext, logger: Logger, retryStrategy?: RetryStrategy | undefined);
11
14
  intercept(context: InterceptorContext<unknown>, next: InterceptorNext): Promise<unknown>;
12
15
  private runWithinNewCtx;
13
16
  private runUnderCtx;
@@ -1,11 +1,11 @@
1
1
  import { MikroOrmFactory } from "./MikroOrmFactory";
2
- import { InjectorService } from "@tsed/di";
3
2
  import { IDatabaseDriver as DatabaseDriver, MikroORM, Options } from "@mikro-orm/core";
3
+ import { Logger } from "@tsed/logger";
4
4
  export declare class MikroOrmRegistry {
5
- private readonly injector;
5
+ private readonly logger;
6
6
  private readonly mikroOrmFactory;
7
7
  private readonly connections;
8
- constructor(injector: InjectorService, mikroOrmFactory: MikroOrmFactory);
8
+ constructor(logger: Logger, mikroOrmFactory: MikroOrmFactory);
9
9
  createConnection<T extends DatabaseDriver>(connectionOptions: Options<T>): Promise<MikroORM>;
10
10
  get(id?: string): MikroORM;
11
11
  has(id?: string): boolean;
@@ -1,3 +1,4 @@
1
1
  export interface RetryStrategy {
2
2
  acquire<T extends (...args: unknown[]) => unknown>(task: T): Promise<ReturnType<T>>;
3
3
  }
4
+ export declare const RetryStrategy: unique symbol;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsed/mikro-orm",
3
- "version": "6.96.2",
3
+ "version": "7.0.0-beta.1",
4
4
  "description": "MikroORM package for Ts.ED framework",
5
5
  "private": false,
6
6
  "source": "./src/index.ts",
@@ -24,26 +24,27 @@
24
24
  "devDependencies": {
25
25
  "@mikro-orm/core": "^4.5.9",
26
26
  "@mikro-orm/mongodb": "^4.5.9",
27
- "@tsed/common": "6.96.2",
28
- "@tsed/core": "6.96.2",
29
- "@tsed/di": "6.96.2",
30
- "@tsed/json-mapper": "6.96.2",
31
- "@tsed/schema": "6.96.2",
27
+ "@tsed/common": "7.0.0-beta.1",
28
+ "@tsed/core": "7.0.0-beta.1",
29
+ "@tsed/di": "7.0.0-beta.1",
30
+ "@tsed/json-mapper": "7.0.0-beta.1",
31
+ "@tsed/logger": "^6.0.0",
32
+ "@tsed/schema": "7.0.0-beta.1",
33
+ "@tsed/testing-mongoose": "7.0.0-beta.1",
32
34
  "ts-mockito": "^2.6.1"
33
35
  },
34
36
  "peerDependencies": {
35
37
  "@mikro-orm/core": ">=4.5.9",
36
- "@tsed/common": "^6.96.2",
37
- "@tsed/core": "^6.96.2",
38
- "@tsed/di": "^6.96.2",
39
- "@tsed/json-mapper": "^6.96.2",
40
- "@tsed/schema": "^6.96.2"
38
+ "@tsed/common": "^7.0.0-beta.1",
39
+ "@tsed/core": "^7.0.0-beta.1",
40
+ "@tsed/di": "^7.0.0-beta.1",
41
+ "@tsed/logger": "^6.0.0"
41
42
  },
42
43
  "repository": "https://github.com/tsedio/tsed",
43
44
  "bugs": {
44
45
  "url": "https://github.com/tsedio/tsed/issues"
45
46
  },
46
- "homepage": "https://github.com/tsedio/tsed/tree/production/packages/orm/mikro-orm",
47
+ "homepage": "https://github.com/tsedio/tsed/tree/alpha/packages/orm/mikro-orm",
47
48
  "author": "Romain Lenzotti",
48
49
  "license": "MIT"
49
50
  }
package/readme.md CHANGED
@@ -3,7 +3,7 @@
3
3
  </p>
4
4
 
5
5
  <div align="center">
6
- <h1>MikroORM</h1>
6
+ <h1>MikroOrm</h1>
7
7
 
8
8
  [![Build & Release](https://github.com/tsedio/tsed/workflows/Build%20&%20Release/badge.svg)](https://github.com/tsedio/tsed/actions?query=workflow%3A%22Build+%26+Release%22)
9
9
  [![PR Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/tsedio/tsed/blob/master/CONTRIBUTING.md)
@@ -33,7 +33,7 @@ A package of Ts.ED framework. See website: https://tsed.io/tutorials/mikro-orm
33
33
 
34
34
  Currently, `@tsed/mikro-orm` allows you:
35
35
 
36
- - Configure one or more MikroOrm connections via the `@ServerSettings` configuration. All databases will be initialized
36
+ - Configure one or more MikroOrm connections via the `@Configuration` decorator. All databases will be initialized
37
37
  when the server starts during the server's `OnInit` phase.
38
38
  - Use the Entity MikroOrm as Model for Controllers, AJV Validation and Swagger.
39
39
 
@@ -41,15 +41,35 @@ Currently, `@tsed/mikro-orm` allows you:
41
41
 
42
42
  To begin, install the MikroOrm module for TS.ED:
43
43
 
44
+ <Tabs class="-code">
45
+ <Tab label="NPM">
46
+
47
+ ```bash
48
+ npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/mongodb # for mongo
49
+ npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/mysql # for mysql/mariadb
50
+ npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/mariadb # for mysql/mariadb
51
+ npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/postgresql # for postgresql
52
+ npm install @mikro-orm/core @tsed/mikro-orm @mikro-orm/sqlite # for sqlite
53
+ ```
54
+
55
+ </Tab>
56
+ <Tab label="Yarn">
57
+
44
58
  ```bash
45
- npm install --save @tsed/mikro-orm
46
- npm install --save @mikro-orm/core
59
+ yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/mongodb # for mongo
60
+ yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/mysql # for mysql/mariadb
61
+ yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/mariadb # for mysql/mariadb
62
+ yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/postgresql # for postgresql
63
+ yarn add @mikro-orm/core @tsed/mikro-orm @mikro-orm/sqlite # for sqlite
47
64
  ```
48
65
 
49
- Then import `@tsed/mikro-orm` in your Server:
66
+ </Tab>
67
+ </Tabs>
68
+
69
+ Once the installation process is completed, we can import the `MikroOrmModule` into the `Server` configuration:
50
70
 
51
71
  ```typescript
52
- import {Configuration} from "@tsed/common";
72
+ import {Configuration} from "@tsed/di";
53
73
  import {MikroOrmModule} from "@tsed/mikro-orm";
54
74
 
55
75
  @Configuration({
@@ -71,16 +91,14 @@ import {MikroOrmModule} from "@tsed/mikro-orm";
71
91
  }
72
92
  ]
73
93
  })
74
- export class Server {
75
-
76
- }
94
+ export class Server {}
77
95
  ```
78
96
 
79
- For more information about MikroOrm look his documentation [here](https://mikro-orm.io/docs);
97
+ The `mikroOrm` options accepts the same configuration object as `init()` from the MikroORM package. Check [this page](https://mikro-orm.io/docs/configuration) for the complete configuration documentation.
80
98
 
81
99
  ## Obtain a connection
82
100
 
83
- `Connection` decorator lets you retrieve an instance of MikroOrm Connection.
101
+ `@Connection` decorator lets you retrieve an instance of MikroOrm Connection.
84
102
 
85
103
  ```typescript
86
104
  import {Injectable, AfterRoutesInit} from "@tsed/common";
@@ -93,7 +111,6 @@ export class UsersService {
93
111
  private readonly connection!: MikroORM;
94
112
 
95
113
  async create(user: User): Promise<User> {
96
-
97
114
  // do something
98
115
  // ...
99
116
  // Then save
@@ -112,6 +129,62 @@ export class UsersService {
112
129
  }
113
130
  ```
114
131
 
132
+ It's also possible to inject a connection by his name:
133
+
134
+ ```ts
135
+ import {Injectable} from "@tsed/di";
136
+
137
+ @Injectable()
138
+ export class MyService {
139
+ @Connection("mongo")
140
+ private readonly connection!: MikroORM;
141
+ }
142
+ ```
143
+
144
+ ## Obtain EntityManager
145
+
146
+ `@EntityManager` and `@Em` decorators lets you retrieve an instance of EntityManager.
147
+
148
+ ```typescript
149
+ import {Injectable, AfterRoutesInit} from "@tsed/common";
150
+ import {Em} from "@tsed/mikro-orm";
151
+ import {EntityManager} from '@mikro-orm/mysql'; // Import EntityManager from your driver package or `@mikro-orm/knex`
152
+
153
+ @Injectable()
154
+ export class UsersService {
155
+ @Em()
156
+ private readonly em!: EntityManager;
157
+
158
+ async create(user: User): Promise<User> {
159
+ await this.em.persistAndFlush(user);
160
+ console.log("Saved a new user with id: " + user.id);
161
+
162
+ return user;
163
+ }
164
+ }
165
+ ```
166
+
167
+ It's also possible to inject Entity manager by his connection name:
168
+
169
+ ```typescript
170
+ import {Injectable, AfterRoutesInit} from "@tsed/common";
171
+ import {Em} from "@tsed/mikro-orm";
172
+ import {EntityManager} from '@mikro-orm/mysql'; // Import EntityManager from your driver package or `@mikro-orm/knex`
173
+
174
+ @Injectable()
175
+ export class UsersService {
176
+ @Em("connectionName")
177
+ private readonly em!: EntityManager;
178
+
179
+ async create(user: User): Promise<User> {
180
+ await this.em.persistAndFlush(user);
181
+ console.log("Saved a new user with id: " + user.id);
182
+
183
+ return user;
184
+ }
185
+ }
186
+ ```
187
+
115
188
  ## Use Entity with Controller
116
189
 
117
190
  To begin, we need to define an Entity MikroOrm like this and use Ts.ED Decorator to define the JSON Schema.
@@ -200,6 +273,93 @@ export class UsersCtrl {
200
273
  }
201
274
  ```
202
275
 
276
+ By default, the automatic retry policy is disabled. You can implement your own to match the business requirements and the nature of the failure. For some noncritical operations, it is better to fail as soon as possible rather than retry a coupe of times. For example, in an interactive web application, it is better to fail right after a smaller number of retries with only a short delay between retry attempts, and display a message to the user (for example, "please try again later").
277
+
278
+ The `@Transactional()` decorator allows you to enable a retry policy for the particular resources. You just need to implement the `RetryStrategy` interface and use `registerProvider()` or `@OverrideProvider()` to register it in the IoC container. Below you can find an example to handle occurred optimistic locks based on [an exponential backoff retry strategy](https://en.wikipedia.org/wiki/Exponential_backoff).
279
+
280
+ ```ts
281
+ import { OptimisticLockError } from '@mikro-orm/core';
282
+ import { RetryStrategy } from '@tsed/mikro-orm';
283
+
284
+ export interface ExponentialBackoffOptions {
285
+ maxDepth: number;
286
+ }
287
+
288
+ export class ExponentialBackoff implements RetryStrategy {
289
+ private depth = 0;
290
+
291
+ constructor(private readonly options: ExponentialBackoffOptions) {}
292
+
293
+ public async acquire<T extends (...args: unknown[]) => unknown>(
294
+ task: T
295
+ ): Promise<ReturnType<T>> {
296
+ try {
297
+ return (await task()) as ReturnType<T>;
298
+ } catch (e) {
299
+ if (
300
+ this.shouldRetry(e as Error) &&
301
+ this.depth < this.options.maxDepth
302
+ ) {
303
+ return this.retry(task);
304
+ }
305
+
306
+ throw e;
307
+ }
308
+ }
309
+
310
+ private shouldRetry(error: Error): boolean {
311
+ return error instanceof OptimisticLockError;
312
+ }
313
+
314
+ private async retry<T extends (...args: unknown[]) => unknown>(
315
+ task: T
316
+ ): Promise<ReturnType<T>> {
317
+ await this.sleep(2 ** this.depth * 50);
318
+
319
+ this.depth += 1;
320
+
321
+ return this.acquire(task);
322
+ }
323
+
324
+ private sleep(milliseconds: number): Promise<void> {
325
+ return new Promise(resolve => setTimeout(resolve, milliseconds));
326
+ }
327
+ }
328
+
329
+ registerProvider({
330
+ provide: RetryStrategy,
331
+ useFactory(): ExponentialBackoff {
332
+ return new ExponentialBackoff({maxDepth: 3})
333
+ }
334
+ });
335
+ ```
336
+
337
+ `ExponentialBackoff` invokes passed function recursively is contained in a try/catch block. The method returns control to the interceptor if the call to the `task` function succeeds without throwing an exception. If the `task` method fails, the catch block examines the reason for the failure. If it's optimistic locking the code waits for a short delay before retrying the operation.
338
+
339
+ Once a retry strategy is implemented, you can enable an automatic retry mechanism using the `@Transactional` decorator like that:
340
+
341
+ ```typescript
342
+ import {Controller, Post, BodyParams, Inject, Get} from "@tsed/common";
343
+ import {Transactional} from "@tsed/mikro-orm";
344
+
345
+ @Controller("/users")
346
+ export class UsersCtrl {
347
+ @Inject()
348
+ private usersService: UsersService;
349
+
350
+ @Post("/")
351
+ @Transactional({retry: true})
352
+ create(@BodyParams() user: User): Promise<User> {
353
+ return this.usersService.create(user);
354
+ }
355
+
356
+ @Get("/")
357
+ getList(): Promise<User[]> {
358
+ return this.usersService.find();
359
+ }
360
+ }
361
+ ```
362
+
203
363
  ## Contributors
204
364
 
205
365
  Please read [contributing guidelines here](https://tsed.io/CONTRIBUTING.html)