@nestjs-transactional/core 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +265 -0
  3. package/dist/bootstrap/transactional-methods.bootstrap.d.ts +42 -0
  4. package/dist/bootstrap/transactional-methods.bootstrap.js +129 -0
  5. package/dist/bootstrap/transactional-methods.bootstrap.js.map +1 -0
  6. package/dist/context/transaction-context-view.d.ts +38 -0
  7. package/dist/context/transaction-context-view.js +48 -0
  8. package/dist/context/transaction-context-view.js.map +1 -0
  9. package/dist/context/transaction.context.d.ts +114 -0
  10. package/dist/context/transaction.context.js +112 -0
  11. package/dist/context/transaction.context.js.map +1 -0
  12. package/dist/decorators/inject-decorators.d.ts +36 -0
  13. package/dist/decorators/inject-decorators.js +45 -0
  14. package/dist/decorators/inject-decorators.js.map +1 -0
  15. package/dist/decorators/transactional.decorator.d.ts +64 -0
  16. package/dist/decorators/transactional.decorator.js +82 -0
  17. package/dist/decorators/transactional.decorator.js.map +1 -0
  18. package/dist/index.d.ts +19 -0
  19. package/dist/index.js +35 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/interceptor/transactional.interceptor.d.ts +28 -0
  22. package/dist/interceptor/transactional.interceptor.js +56 -0
  23. package/dist/interceptor/transactional.interceptor.js.map +1 -0
  24. package/dist/internal/markers.d.ts +15 -0
  25. package/dist/internal/markers.js +18 -0
  26. package/dist/internal/markers.js.map +1 -0
  27. package/dist/manager/adapter.registry.d.ts +103 -0
  28. package/dist/manager/adapter.registry.js +179 -0
  29. package/dist/manager/adapter.registry.js.map +1 -0
  30. package/dist/manager/transaction.manager.d.ts +129 -0
  31. package/dist/manager/transaction.manager.js +412 -0
  32. package/dist/manager/transaction.manager.js.map +1 -0
  33. package/dist/module/transactional.module.d.ts +242 -0
  34. package/dist/module/transactional.module.js +374 -0
  35. package/dist/module/transactional.module.js.map +1 -0
  36. package/dist/observability/transaction-observer.d.ts +78 -0
  37. package/dist/observability/transaction-observer.js +18 -0
  38. package/dist/observability/transaction-observer.js.map +1 -0
  39. package/dist/testing/in-memory.adapter.d.ts +66 -0
  40. package/dist/testing/in-memory.adapter.js +83 -0
  41. package/dist/testing/in-memory.adapter.js.map +1 -0
  42. package/dist/testing/index.d.ts +2 -0
  43. package/dist/testing/index.js +18 -0
  44. package/dist/testing/index.js.map +1 -0
  45. package/dist/tokens/constants.d.ts +14 -0
  46. package/dist/tokens/constants.js +17 -0
  47. package/dist/tokens/constants.js.map +1 -0
  48. package/dist/tokens/index.d.ts +3 -0
  49. package/dist/tokens/index.js +11 -0
  50. package/dist/tokens/index.js.map +1 -0
  51. package/dist/tokens/token-utils.d.ts +52 -0
  52. package/dist/tokens/token-utils.js +67 -0
  53. package/dist/tokens/token-utils.js.map +1 -0
  54. package/dist/types/domain-event.d.ts +18 -0
  55. package/dist/types/domain-event.js +3 -0
  56. package/dist/types/domain-event.js.map +1 -0
  57. package/dist/types/errors.d.ts +50 -0
  58. package/dist/types/errors.js +61 -0
  59. package/dist/types/errors.js.map +1 -0
  60. package/dist/types/isolation.d.ts +11 -0
  61. package/dist/types/isolation.js +3 -0
  62. package/dist/types/isolation.js.map +1 -0
  63. package/dist/types/propagation.d.ts +63 -0
  64. package/dist/types/propagation.js +67 -0
  65. package/dist/types/propagation.js.map +1 -0
  66. package/dist/types/transaction-adapter.d.ts +72 -0
  67. package/dist/types/transaction-adapter.js +3 -0
  68. package/dist/types/transaction-adapter.js.map +1 -0
  69. package/dist/types/transaction-handle.d.ts +23 -0
  70. package/dist/types/transaction-handle.js +3 -0
  71. package/dist/types/transaction-handle.js.map +1 -0
  72. package/dist/types/transaction-options.d.ts +88 -0
  73. package/dist/types/transaction-options.js +3 -0
  74. package/dist/types/transaction-options.js.map +1 -0
  75. package/package.json +74 -0
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TransactionContext = void 0;
4
+ const node_async_hooks_1 = require("node:async_hooks");
5
+ const errors_1 = require("../types/errors");
6
+ const als = new node_async_hooks_1.AsyncLocalStorage();
7
+ /**
8
+ * Thin façade over `AsyncLocalStorage` that carries the active
9
+ * {@link TransactionContextStore} across async boundaries. This is the
10
+ * foundation of the module — every decorator, interceptor, and adapter helper
11
+ * ultimately asks this class whether a transaction is live on the current
12
+ * async chain.
13
+ *
14
+ * **Internal Map key format**: `${adapterName}:${instanceName}` (composite)
15
+ * for historical reasons and cross-package compatibility — typeorm
16
+ * helpers, the CQRS dispatcher, and the outbox publisher all consume
17
+ * this format directly via {@link getActiveTransaction}. The composite
18
+ * key is also the format `TransactionManager` writes under.
19
+ *
20
+ * **Public dataSource-name access**:
21
+ * {@link getActiveTransactionByDataSource} provides dataSource-name
22
+ * lookup for Phase 14.2+ multi-adapter consumers — it scans the Map
23
+ * for the entry whose `adapterInstanceName === dataSource`. Both
24
+ * access patterns coexist; future cleanup is possible once
25
+ * cross-package consumers migrate to the dataSource-name lookup and
26
+ * no backwards-compatibility constraints remain.
27
+ */
28
+ class TransactionContext {
29
+ /**
30
+ * Run `fn` inside a transaction context scope.
31
+ *
32
+ * - If there is no active store on the current async chain, a new store is
33
+ * created (empty active-transaction map, `correlationId`, current time)
34
+ * and installed for the duration of `fn`.
35
+ * - If there is already an active store, `fn` is executed directly — the
36
+ * existing store is reused. The `correlationId` argument is ignored in
37
+ * that case; the outermost scope owns the correlation id.
38
+ *
39
+ * Propagates the value resolved (or error thrown) by `fn`.
40
+ */
41
+ static run(correlationId, fn) {
42
+ if (als.getStore() !== undefined) {
43
+ return fn();
44
+ }
45
+ const store = {
46
+ activeTransactions: new Map(),
47
+ correlationId,
48
+ startedAt: new Date(),
49
+ };
50
+ return als.run(store, fn);
51
+ }
52
+ /** Return the active store, or `undefined` if called outside any run() scope. */
53
+ static getStore() {
54
+ return als.getStore();
55
+ }
56
+ /** Return the active transaction registered under `adapterInstanceName`, or `undefined`. */
57
+ static getActiveTransaction(adapterInstanceName) {
58
+ return als.getStore()?.activeTransactions.get(adapterInstanceName);
59
+ }
60
+ /**
61
+ * Return the active transaction whose adapter instance was
62
+ * registered under the given dataSource name (DD-020 / DD-023).
63
+ * Scans the active-transactions Map for the entry whose
64
+ * `adapterInstanceName === dataSource`.
65
+ *
66
+ * Returns the first match — there is exactly one active transaction
67
+ * per dataSource by construction (multiple adapters sharing the
68
+ * same dataSource name is rejected at the registry level by
69
+ * {@link AdapterRegistry.getByDataSource}).
70
+ *
71
+ * Used by {@link TransactionContextView} and by smart-facade
72
+ * publishers to answer "is there an active transaction for *my*
73
+ * dataSource?" without needing to know the adapter type that owns
74
+ * the dataSource.
75
+ */
76
+ static getActiveTransactionByDataSource(dataSource) {
77
+ const store = als.getStore();
78
+ if (store === undefined) {
79
+ return undefined;
80
+ }
81
+ for (const tx of store.activeTransactions.values()) {
82
+ if (tx.adapterInstanceName === dataSource) {
83
+ return tx;
84
+ }
85
+ }
86
+ return undefined;
87
+ }
88
+ /**
89
+ * Register `tx` under `adapterInstanceName` on the current store.
90
+ *
91
+ * @throws {IllegalTransactionStateError} If called outside of a run() scope.
92
+ * Adapters must not attempt to register a transaction without a
93
+ * surrounding context — doing so would leak the transaction state.
94
+ */
95
+ static setActiveTransaction(adapterInstanceName, tx) {
96
+ const store = als.getStore();
97
+ if (store === undefined) {
98
+ throw new errors_1.IllegalTransactionStateError('Cannot set active transaction outside of TransactionContext.run()');
99
+ }
100
+ store.activeTransactions.set(adapterInstanceName, tx);
101
+ }
102
+ /**
103
+ * Remove the active transaction registered under `adapterInstanceName`.
104
+ * Idempotent: a no-op when no store is active or the instance is not
105
+ * registered, so that adapter cleanup paths can call it unconditionally.
106
+ */
107
+ static removeActiveTransaction(adapterInstanceName) {
108
+ als.getStore()?.activeTransactions.delete(adapterInstanceName);
109
+ }
110
+ }
111
+ exports.TransactionContext = TransactionContext;
112
+ //# sourceMappingURL=transaction.context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.context.js","sourceRoot":"","sources":["../../src/context/transaction.context.ts"],"names":[],"mappings":";;;AAAA,uDAAqD;AAErD,4CAA+D;AAwD/D,MAAM,GAAG,GAAG,IAAI,oCAAiB,EAA2B,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAa,kBAAkB;IAC7B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,GAAG,CAAI,aAAqB,EAAE,EAAoB;QACvD,IAAI,GAAG,CAAC,QAAQ,EAAE,KAAK,SAAS,EAAE,CAAC;YACjC,OAAO,EAAE,EAAE,CAAC;QACd,CAAC;QACD,MAAM,KAAK,GAA4B;YACrC,kBAAkB,EAAE,IAAI,GAAG,EAA6B;YACxD,aAAa;YACb,SAAS,EAAE,IAAI,IAAI,EAAE;SACtB,CAAC;QACF,OAAO,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,iFAAiF;IACjF,MAAM,CAAC,QAAQ;QACb,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAED,4FAA4F;IAC5F,MAAM,CAAC,oBAAoB,CAAC,mBAA2B;QACrD,OAAO,GAAG,CAAC,QAAQ,EAAE,EAAE,kBAAkB,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACrE,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,gCAAgC,CACrC,UAAkB;QAElB,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,kBAAkB,CAAC,MAAM,EAAE,EAAE,CAAC;YACnD,IAAI,EAAE,CAAC,mBAAmB,KAAK,UAAU,EAAE,CAAC;gBAC1C,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,oBAAoB,CAAC,mBAA2B,EAAE,EAAqB;QAC5E,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,qCAA4B,CACpC,mEAAmE,CACpE,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,mBAAmB,EAAE,EAAE,CAAC,CAAC;IACxD,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,uBAAuB,CAAC,mBAA2B;QACxD,GAAG,CAAC,QAAQ,EAAE,EAAE,kBAAkB,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACjE,CAAC;CACF;AA3FD,gDA2FC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Inject the per-dataSource `TransactionManager`. Sugar over
3
+ * `@Inject(getTransactionManagerToken(dataSource))` for IDE
4
+ * discoverability — matches `@nestjs/typeorm`'s
5
+ * `@InjectRepository(Entity, dataSource?)` ergonomics (DD-022).
6
+ *
7
+ * Default `dataSource` is `'default'`, so single-adapter consumers
8
+ * write `@InjectTransactionManager()` exactly where they would have
9
+ * written `@Inject(TransactionManager)` before.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * class BillingService {
14
+ * constructor(
15
+ * @InjectTransactionManager()
16
+ * private readonly txManager: TransactionManager,
17
+ * @InjectTransactionManager('billing')
18
+ * private readonly billingTxManager: TransactionManager,
19
+ * ) {}
20
+ * }
21
+ * ```
22
+ */
23
+ export declare const InjectTransactionManager: (dataSource?: string) => ParameterDecorator;
24
+ /**
25
+ * Inject the per-dataSource `TransactionContext`. See
26
+ * {@link InjectTransactionManager} for ergonomics.
27
+ */
28
+ export declare const InjectTransactionContext: (dataSource?: string) => ParameterDecorator;
29
+ /**
30
+ * Inject the per-dataSource `TransactionAdapter` instance. Useful
31
+ * for advanced consumers that need to call adapter-specific methods
32
+ * not exposed through `TransactionManager`. See
33
+ * {@link InjectTransactionManager} for ergonomics.
34
+ */
35
+ export declare const InjectTransactionalAdapter: (dataSource?: string) => ParameterDecorator;
36
+ //# sourceMappingURL=inject-decorators.d.ts.map
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InjectTransactionalAdapter = exports.InjectTransactionContext = exports.InjectTransactionManager = void 0;
4
+ const common_1 = require("@nestjs/common");
5
+ const constants_1 = require("../tokens/constants");
6
+ const token_utils_1 = require("../tokens/token-utils");
7
+ /**
8
+ * Inject the per-dataSource `TransactionManager`. Sugar over
9
+ * `@Inject(getTransactionManagerToken(dataSource))` for IDE
10
+ * discoverability — matches `@nestjs/typeorm`'s
11
+ * `@InjectRepository(Entity, dataSource?)` ergonomics (DD-022).
12
+ *
13
+ * Default `dataSource` is `'default'`, so single-adapter consumers
14
+ * write `@InjectTransactionManager()` exactly where they would have
15
+ * written `@Inject(TransactionManager)` before.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * class BillingService {
20
+ * constructor(
21
+ * @InjectTransactionManager()
22
+ * private readonly txManager: TransactionManager,
23
+ * @InjectTransactionManager('billing')
24
+ * private readonly billingTxManager: TransactionManager,
25
+ * ) {}
26
+ * }
27
+ * ```
28
+ */
29
+ const InjectTransactionManager = (dataSource = constants_1.DEFAULT_DATA_SOURCE_NAME) => (0, common_1.Inject)((0, token_utils_1.getTransactionManagerToken)(dataSource));
30
+ exports.InjectTransactionManager = InjectTransactionManager;
31
+ /**
32
+ * Inject the per-dataSource `TransactionContext`. See
33
+ * {@link InjectTransactionManager} for ergonomics.
34
+ */
35
+ const InjectTransactionContext = (dataSource = constants_1.DEFAULT_DATA_SOURCE_NAME) => (0, common_1.Inject)((0, token_utils_1.getTransactionContextToken)(dataSource));
36
+ exports.InjectTransactionContext = InjectTransactionContext;
37
+ /**
38
+ * Inject the per-dataSource `TransactionAdapter` instance. Useful
39
+ * for advanced consumers that need to call adapter-specific methods
40
+ * not exposed through `TransactionManager`. See
41
+ * {@link InjectTransactionManager} for ergonomics.
42
+ */
43
+ const InjectTransactionalAdapter = (dataSource = constants_1.DEFAULT_DATA_SOURCE_NAME) => (0, common_1.Inject)((0, token_utils_1.getTransactionalAdapterToken)(dataSource));
44
+ exports.InjectTransactionalAdapter = InjectTransactionalAdapter;
45
+ //# sourceMappingURL=inject-decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inject-decorators.js","sourceRoot":"","sources":["../../src/decorators/inject-decorators.ts"],"names":[],"mappings":";;;AAAA,2CAAwC;AAExC,mDAA+D;AAC/D,uDAI+B;AAE/B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACI,MAAM,wBAAwB,GAAG,CACtC,aAAqB,oCAAwB,EACzB,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,wCAA0B,EAAC,UAAU,CAAC,CAAC,CAAC;AAF3D,QAAA,wBAAwB,4BAEmC;AAExE;;;GAGG;AACI,MAAM,wBAAwB,GAAG,CACtC,aAAqB,oCAAwB,EACzB,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,wCAA0B,EAAC,UAAU,CAAC,CAAC,CAAC;AAF3D,QAAA,wBAAwB,4BAEmC;AAExE;;;;;GAKG;AACI,MAAM,0BAA0B,GAAG,CACxC,aAAqB,oCAAwB,EACzB,EAAE,CAAC,IAAA,eAAM,EAAC,IAAA,0CAA4B,EAAC,UAAU,CAAC,CAAC,CAAC;AAF7D,QAAA,0BAA0B,8BAEmC"}
@@ -0,0 +1,64 @@
1
+ import type { ExtendedTransactionOptions } from '../types/transaction-options';
2
+ /**
3
+ * Metadata key under which {@link Transactional} stores its options on a
4
+ * target (a method's function object or a class constructor). Exposed for
5
+ * advanced introspection; most code should use
6
+ * {@link getTransactionalMetadata}.
7
+ */
8
+ export declare const TRANSACTIONAL_METADATA: unique symbol;
9
+ /**
10
+ * Shape of the metadata attached by {@link Transactional}. Currently an
11
+ * alias for {@link ExtendedTransactionOptions} — kept as a distinct name so
12
+ * the decorator's public surface is self-documenting and so decorator-only
13
+ * options can be added later without touching the manager's options type.
14
+ */
15
+ export type TransactionalMetadata = ExtendedTransactionOptions;
16
+ /**
17
+ * Mark a method or a class as transactional.
18
+ *
19
+ * **Metadata-only**: this decorator does NOT wrap the method at decoration
20
+ * time. The actual wrapping is performed at runtime by the three coordinated
21
+ * mechanisms described in ADR-005 — `TransactionalInterceptor` for
22
+ * request-boundary handlers, `TransactionalMethodsBootstrap` for regular
23
+ * `@Injectable` providers, and `CqrsHandlerWrapper` for CQRS handlers.
24
+ *
25
+ * Usage:
26
+ * - As a method decorator, the metadata is written onto `descriptor.value`
27
+ * (the method function).
28
+ * - As a class decorator, the metadata is written onto the class
29
+ * constructor. Downstream wrapping mechanisms treat every method of the
30
+ * class as transactional with these options (unless a method has its own
31
+ * `@Transactional` which overrides).
32
+ *
33
+ * Default propagation is {@link PropagationMode.REQUIRED}.
34
+ */
35
+ export declare function Transactional(options?: Partial<TransactionalMetadata>): MethodDecorator & ClassDecorator;
36
+ /**
37
+ * Alias for `@Transactional({ readOnly: true })`. Use on query methods that
38
+ * must not mutate state. Options are applied first and the `readOnly: true`
39
+ * flag is overlaid on top, so callers cannot turn it off from here — use
40
+ * `@Transactional` directly if that is the intent.
41
+ */
42
+ export declare const ReadOnly: (options?: Partial<TransactionalMetadata>) => MethodDecorator & ClassDecorator;
43
+ /**
44
+ * Alias for `@Transactional({ adapterInstance })`. Targets a specific
45
+ * adapter instance, for multi-datasource setups:
46
+ *
47
+ * ```ts
48
+ * @TransactionalOn('billing')
49
+ * async issueInvoice() { ... }
50
+ * ```
51
+ *
52
+ * The `adapterInstance` argument overrides any `adapterInstance` set in the
53
+ * options object.
54
+ */
55
+ export declare const TransactionalOn: (adapterInstance: string, options?: Partial<TransactionalMetadata>) => MethodDecorator & ClassDecorator;
56
+ /**
57
+ * Read the {@link TransactionalMetadata} stored by {@link Transactional} on
58
+ * `target`. Returns `undefined` when the target was not decorated.
59
+ *
60
+ * @param target - A class constructor (for class-level metadata) or a
61
+ * method function (for method-level metadata).
62
+ */
63
+ export declare function getTransactionalMetadata(target: object): TransactionalMetadata | undefined;
64
+ //# sourceMappingURL=transactional.decorator.d.ts.map
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TransactionalOn = exports.ReadOnly = exports.TRANSACTIONAL_METADATA = void 0;
4
+ exports.Transactional = Transactional;
5
+ exports.getTransactionalMetadata = getTransactionalMetadata;
6
+ const propagation_1 = require("../types/propagation");
7
+ /**
8
+ * Metadata key under which {@link Transactional} stores its options on a
9
+ * target (a method's function object or a class constructor). Exposed for
10
+ * advanced introspection; most code should use
11
+ * {@link getTransactionalMetadata}.
12
+ */
13
+ exports.TRANSACTIONAL_METADATA = Symbol('TRANSACTIONAL_METADATA');
14
+ /**
15
+ * Mark a method or a class as transactional.
16
+ *
17
+ * **Metadata-only**: this decorator does NOT wrap the method at decoration
18
+ * time. The actual wrapping is performed at runtime by the three coordinated
19
+ * mechanisms described in ADR-005 — `TransactionalInterceptor` for
20
+ * request-boundary handlers, `TransactionalMethodsBootstrap` for regular
21
+ * `@Injectable` providers, and `CqrsHandlerWrapper` for CQRS handlers.
22
+ *
23
+ * Usage:
24
+ * - As a method decorator, the metadata is written onto `descriptor.value`
25
+ * (the method function).
26
+ * - As a class decorator, the metadata is written onto the class
27
+ * constructor. Downstream wrapping mechanisms treat every method of the
28
+ * class as transactional with these options (unless a method has its own
29
+ * `@Transactional` which overrides).
30
+ *
31
+ * Default propagation is {@link PropagationMode.REQUIRED}.
32
+ */
33
+ function Transactional(options = {}) {
34
+ const metadata = {
35
+ propagation: propagation_1.PropagationMode.REQUIRED,
36
+ ...options,
37
+ };
38
+ const decorator = (target, _propertyKey, descriptor) => {
39
+ const methodTarget = descriptor?.value;
40
+ if (typeof methodTarget === 'function') {
41
+ Reflect.defineMetadata(exports.TRANSACTIONAL_METADATA, metadata, methodTarget);
42
+ }
43
+ else {
44
+ Reflect.defineMetadata(exports.TRANSACTIONAL_METADATA, metadata, target);
45
+ }
46
+ };
47
+ return decorator;
48
+ }
49
+ /**
50
+ * Alias for `@Transactional({ readOnly: true })`. Use on query methods that
51
+ * must not mutate state. Options are applied first and the `readOnly: true`
52
+ * flag is overlaid on top, so callers cannot turn it off from here — use
53
+ * `@Transactional` directly if that is the intent.
54
+ */
55
+ const ReadOnly = (options = {}) => Transactional({ ...options, readOnly: true });
56
+ exports.ReadOnly = ReadOnly;
57
+ /**
58
+ * Alias for `@Transactional({ adapterInstance })`. Targets a specific
59
+ * adapter instance, for multi-datasource setups:
60
+ *
61
+ * ```ts
62
+ * @TransactionalOn('billing')
63
+ * async issueInvoice() { ... }
64
+ * ```
65
+ *
66
+ * The `adapterInstance` argument overrides any `adapterInstance` set in the
67
+ * options object.
68
+ */
69
+ const TransactionalOn = (adapterInstance, options = {}) => Transactional({ ...options, adapterInstance });
70
+ exports.TransactionalOn = TransactionalOn;
71
+ /**
72
+ * Read the {@link TransactionalMetadata} stored by {@link Transactional} on
73
+ * `target`. Returns `undefined` when the target was not decorated.
74
+ *
75
+ * @param target - A class constructor (for class-level metadata) or a
76
+ * method function (for method-level metadata).
77
+ */
78
+ function getTransactionalMetadata(target) {
79
+ const value = Reflect.getMetadata(exports.TRANSACTIONAL_METADATA, target);
80
+ return value;
81
+ }
82
+ //# sourceMappingURL=transactional.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactional.decorator.js","sourceRoot":"","sources":["../../src/decorators/transactional.decorator.ts"],"names":[],"mappings":";;;AAsCA,sCAsBC;AAoCD,4DAGC;AAnGD,sDAAuD;AAGvD;;;;;GAKG;AACU,QAAA,sBAAsB,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAUvE;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,aAAa,CAC3B,UAA0C,EAAE;IAE5C,MAAM,QAAQ,GAA0B;QACtC,WAAW,EAAE,6BAAe,CAAC,QAAQ;QACrC,GAAG,OAAO;KACX,CAAC;IAEF,MAAM,SAAS,GAAqC,CAClD,MAAc,EACd,YAA8B,EAC9B,UAA+B,EACzB,EAAE;QACR,MAAM,YAAY,GAAY,UAAU,EAAE,KAAK,CAAC;QAChD,IAAI,OAAO,YAAY,KAAK,UAAU,EAAE,CAAC;YACvC,OAAO,CAAC,cAAc,CAAC,8BAAsB,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAC;QACzE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,cAAc,CAAC,8BAAsB,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QACnE,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACI,MAAM,QAAQ,GAAG,CACtB,UAA0C,EAAE,EACV,EAAE,CAAC,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;AAFxE,QAAA,QAAQ,YAEgE;AAErF;;;;;;;;;;;GAWG;AACI,MAAM,eAAe,GAAG,CAC7B,eAAuB,EACvB,UAA0C,EAAE,EACV,EAAE,CAAC,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC;AAHzE,QAAA,eAAe,mBAG0D;AAEtF;;;;;;GAMG;AACH,SAAgB,wBAAwB,CAAC,MAAc;IACrD,MAAM,KAAK,GAAY,OAAO,CAAC,WAAW,CAAC,8BAAsB,EAAE,MAAM,CAAC,CAAC;IAC3E,OAAO,KAA0C,CAAC;AACpD,CAAC"}
@@ -0,0 +1,19 @@
1
+ export * from './types/propagation';
2
+ export * from './types/isolation';
3
+ export * from './types/transaction-handle';
4
+ export * from './types/transaction-options';
5
+ export * from './types/transaction-adapter';
6
+ export * from './types/domain-event';
7
+ export * from './types/errors';
8
+ export * from './context/transaction.context';
9
+ export * from './context/transaction-context-view';
10
+ export * from './manager/adapter.registry';
11
+ export * from './manager/transaction.manager';
12
+ export * from './decorators/transactional.decorator';
13
+ export * from './decorators/inject-decorators';
14
+ export * from './tokens';
15
+ export * from './interceptor/transactional.interceptor';
16
+ export * from './bootstrap/transactional-methods.bootstrap';
17
+ export * from './module/transactional.module';
18
+ export * from './observability/transaction-observer';
19
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types/propagation"), exports);
18
+ __exportStar(require("./types/isolation"), exports);
19
+ __exportStar(require("./types/transaction-handle"), exports);
20
+ __exportStar(require("./types/transaction-options"), exports);
21
+ __exportStar(require("./types/transaction-adapter"), exports);
22
+ __exportStar(require("./types/domain-event"), exports);
23
+ __exportStar(require("./types/errors"), exports);
24
+ __exportStar(require("./context/transaction.context"), exports);
25
+ __exportStar(require("./context/transaction-context-view"), exports);
26
+ __exportStar(require("./manager/adapter.registry"), exports);
27
+ __exportStar(require("./manager/transaction.manager"), exports);
28
+ __exportStar(require("./decorators/transactional.decorator"), exports);
29
+ __exportStar(require("./decorators/inject-decorators"), exports);
30
+ __exportStar(require("./tokens"), exports);
31
+ __exportStar(require("./interceptor/transactional.interceptor"), exports);
32
+ __exportStar(require("./bootstrap/transactional-methods.bootstrap"), exports);
33
+ __exportStar(require("./module/transactional.module"), exports);
34
+ __exportStar(require("./observability/transaction-observer"), exports);
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,oDAAkC;AAClC,6DAA2C;AAC3C,8DAA4C;AAC5C,8DAA4C;AAC5C,uDAAqC;AACrC,iDAA+B;AAE/B,gEAA8C;AAC9C,qEAAmD;AACnD,6DAA2C;AAC3C,gEAA8C;AAE9C,uEAAqD;AACrD,iEAA+C;AAE/C,2CAAyB;AAEzB,0EAAwD;AACxD,8EAA4D;AAC5D,gEAA8C;AAE9C,uEAAqD"}
@@ -0,0 +1,28 @@
1
+ import { type CallHandler, type ExecutionContext, type NestInterceptor } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { type Observable } from 'rxjs';
4
+ import { TransactionManager } from '../manager/transaction.manager';
5
+ /**
6
+ * NestJS interceptor that wraps a request-boundary handler in a transaction
7
+ * when the handler (or its controller class) carries `@Transactional()`
8
+ * metadata.
9
+ *
10
+ * Registered at the request boundary via `APP_INTERCEPTOR`. It is one of
11
+ * the three coordinated wrapping mechanisms described in ADR-005 — the
12
+ * other two are `TransactionalMethodsBootstrap` (for plain `@Injectable`
13
+ * services) and `CqrsHandlerWrapper` (for CQRS handlers).
14
+ *
15
+ * Metadata lookup: method-level `@Transactional` overrides class-level.
16
+ * If neither is present the handler is passed through without any
17
+ * transactional wrapping.
18
+ *
19
+ * Not exported from the package's public API — consumers enable it via
20
+ * `TransactionalModule.forRoot`.
21
+ */
22
+ export declare class TransactionalInterceptor implements NestInterceptor {
23
+ private readonly manager;
24
+ private readonly reflector;
25
+ constructor(manager: TransactionManager, reflector: Reflector);
26
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
27
+ }
28
+ //# sourceMappingURL=transactional.interceptor.d.ts.map
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TransactionalInterceptor = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const core_1 = require("@nestjs/core");
15
+ const rxjs_1 = require("rxjs");
16
+ const transactional_decorator_1 = require("../decorators/transactional.decorator");
17
+ const transaction_manager_1 = require("../manager/transaction.manager");
18
+ /**
19
+ * NestJS interceptor that wraps a request-boundary handler in a transaction
20
+ * when the handler (or its controller class) carries `@Transactional()`
21
+ * metadata.
22
+ *
23
+ * Registered at the request boundary via `APP_INTERCEPTOR`. It is one of
24
+ * the three coordinated wrapping mechanisms described in ADR-005 — the
25
+ * other two are `TransactionalMethodsBootstrap` (for plain `@Injectable`
26
+ * services) and `CqrsHandlerWrapper` (for CQRS handlers).
27
+ *
28
+ * Metadata lookup: method-level `@Transactional` overrides class-level.
29
+ * If neither is present the handler is passed through without any
30
+ * transactional wrapping.
31
+ *
32
+ * Not exported from the package's public API — consumers enable it via
33
+ * `TransactionalModule.forRoot`.
34
+ */
35
+ let TransactionalInterceptor = class TransactionalInterceptor {
36
+ manager;
37
+ reflector;
38
+ constructor(manager, reflector) {
39
+ this.manager = manager;
40
+ this.reflector = reflector;
41
+ }
42
+ intercept(context, next) {
43
+ const metadata = this.reflector.getAllAndOverride(transactional_decorator_1.TRANSACTIONAL_METADATA, [context.getHandler(), context.getClass()]);
44
+ if (metadata === undefined) {
45
+ return next.handle();
46
+ }
47
+ return (0, rxjs_1.defer)(() => this.manager.run(metadata, () => (0, rxjs_1.firstValueFrom)(next.handle())));
48
+ }
49
+ };
50
+ exports.TransactionalInterceptor = TransactionalInterceptor;
51
+ exports.TransactionalInterceptor = TransactionalInterceptor = __decorate([
52
+ (0, common_1.Injectable)(),
53
+ __metadata("design:paramtypes", [transaction_manager_1.TransactionManager,
54
+ core_1.Reflector])
55
+ ], TransactionalInterceptor);
56
+ //# sourceMappingURL=transactional.interceptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactional.interceptor.js","sourceRoot":"","sources":["../../src/interceptor/transactional.interceptor.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAKwB;AACxB,uCAAyC;AACzC,+BAA8D;AAE9D,mFAG+C;AAC/C,wEAAoE;AAEpE;;;;;;;;;;;;;;;;GAgBG;AAEI,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IAEhB;IACA;IAFnB,YACmB,OAA2B,EAC3B,SAAoB;QADpB,YAAO,GAAP,OAAO,CAAoB;QAC3B,cAAS,GAAT,SAAS,CAAW;IACpC,CAAC;IAEJ,SAAS,CAAC,OAAyB,EAAE,IAAiB;QACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAC/C,gDAAsB,EACtB,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAC3C,CAAC;QAEF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,CAAC;QAED,OAAO,IAAA,YAAK,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAA,qBAAc,EAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACtF,CAAC;CACF,CAAA;AAlBY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAGiB,wCAAkB;QAChB,gBAAS;GAH5B,wBAAwB,CAkBpC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Identity marker placed via `Reflect.defineMetadata` on methods that have
3
+ * been wrapped by one of the coordinated wrapping mechanisms described in
4
+ * ADR-005 (`TransactionalInterceptor`, `TransactionalMethodsBootstrap`,
5
+ * and `CqrsHandlerWrapper`). Each mechanism checks this marker before
6
+ * wrapping so a method is never wrapped twice.
7
+ *
8
+ * `Symbol.for` gives a process-global symbol, so multiple copies of this
9
+ * package in the same dependency tree still resolve to the same identity.
10
+ *
11
+ * Internal: not re-exported from the public API. The three wrapping
12
+ * mechanisms are the only legitimate users.
13
+ */
14
+ export declare const WRAPPED_MARKER: unique symbol;
15
+ //# sourceMappingURL=markers.d.ts.map
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WRAPPED_MARKER = void 0;
4
+ /**
5
+ * Identity marker placed via `Reflect.defineMetadata` on methods that have
6
+ * been wrapped by one of the coordinated wrapping mechanisms described in
7
+ * ADR-005 (`TransactionalInterceptor`, `TransactionalMethodsBootstrap`,
8
+ * and `CqrsHandlerWrapper`). Each mechanism checks this marker before
9
+ * wrapping so a method is never wrapped twice.
10
+ *
11
+ * `Symbol.for` gives a process-global symbol, so multiple copies of this
12
+ * package in the same dependency tree still resolve to the same identity.
13
+ *
14
+ * Internal: not re-exported from the public API. The three wrapping
15
+ * mechanisms are the only legitimate users.
16
+ */
17
+ exports.WRAPPED_MARKER = Symbol.for('@nestjs-transactional/wrapped');
18
+ //# sourceMappingURL=markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.js","sourceRoot":"","sources":["../../src/internal/markers.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACU,QAAA,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC"}
@@ -0,0 +1,103 @@
1
+ import type { TransactionAdapter } from '../types/transaction-adapter';
2
+ /**
3
+ * A single entry in the {@link AdapterRegistry}: an adapter instance bound to
4
+ * its type name and instance name. Used as both the input shape of
5
+ * {@link AdapterRegistry.register} and the element shape returned by
6
+ * {@link AdapterRegistry.getAll}.
7
+ */
8
+ export interface AdapterRegistration {
9
+ /** Adapter type name, e.g. `'typeorm'`, `'prisma'`. */
10
+ readonly adapterName: string;
11
+ /** Instance name within the adapter type, e.g. `'primary'`, `'billing'`. */
12
+ readonly instanceName: string;
13
+ /** The adapter implementation registered under this (adapterName, instanceName) pair. */
14
+ readonly adapter: TransactionAdapter;
15
+ }
16
+ /**
17
+ * DI token for the {@link AdapterRegistry}. Consumers who need to register
18
+ * adapters manually (for example, integration tests or custom modules)
19
+ * can inject this token and call {@link AdapterRegistry.register}.
20
+ */
21
+ export declare const ADAPTER_REGISTRY: unique symbol;
22
+ /**
23
+ * In-memory registry of {@link TransactionAdapter} instances, keyed by
24
+ * `(adapterName, instanceName)`. Each adapter type (e.g. `'typeorm'`) may
25
+ * have multiple instances (e.g. `'primary'`, `'billing'`) — the registry
26
+ * keeps them separate so that `@Transactional({ adapterInstance: 'billing' })`
27
+ * can target a specific one.
28
+ *
29
+ * The first adapter registered becomes the default; any later registration
30
+ * can override the default by passing `isDefault = true`.
31
+ *
32
+ * Implementation note: the internal key format is `${adapterName}:${instanceName}`.
33
+ * Neither `adapterName` nor `instanceName` may contain `:` — enforced by
34
+ * convention, not at runtime.
35
+ */
36
+ export declare class AdapterRegistry {
37
+ private readonly adapters;
38
+ private defaultAdapterName;
39
+ private defaultInstanceName;
40
+ /**
41
+ * Register an adapter under `(registration.adapterName, registration.instanceName)`.
42
+ *
43
+ * The first registration always becomes the default. Passing
44
+ * `isDefault = true` on any later registration switches the default to it.
45
+ *
46
+ * Re-registering the same pair overwrites the previously stored adapter;
47
+ * the default pointer is left unchanged unless `isDefault = true` is set.
48
+ */
49
+ register(registration: AdapterRegistration, isDefault?: boolean): void;
50
+ /**
51
+ * Look up an adapter by `(adapterName, instanceName)`.
52
+ *
53
+ * @throws {TransactionAdapterNotFoundError} If no adapter is registered
54
+ * under that pair.
55
+ */
56
+ get(adapterName: string, instanceName: string): TransactionAdapter;
57
+ /**
58
+ * Look up an adapter by its public dataSource name (DD-020). Iterates
59
+ * the registered entries and returns the one whose `instanceName`
60
+ * matches `dataSource` — there is exactly one per dataSource by
61
+ * convention.
62
+ *
63
+ * If multiple registrations share the same `instanceName` across
64
+ * different adapter types (e.g. `typeorm:billing` AND
65
+ * `prisma:billing`), throws — that configuration is ambiguous and
66
+ * the user must disambiguate via the `adapter` + `adapterInstance`
67
+ * options instead.
68
+ *
69
+ * @throws {IllegalTransactionStateError} If two or more adapters share
70
+ * the same dataSource name.
71
+ * @throws {TransactionAdapterNotFoundError} If no adapter is registered
72
+ * under this dataSource.
73
+ */
74
+ getByDataSource(dataSource: string): TransactionAdapter;
75
+ /**
76
+ * Find the adapter type name (`'typeorm'`, `'prisma'`, ...) registered
77
+ * under the given dataSource. Used by the manager to compose the
78
+ * composite Map key when only the dataSource name is known.
79
+ *
80
+ * Same disambiguation rules as {@link getByDataSource}.
81
+ */
82
+ getAdapterNameByDataSource(dataSource: string): string;
83
+ /**
84
+ * Return the name of the adapter type currently marked default.
85
+ *
86
+ * @throws {IllegalTransactionStateError} If no adapter has been registered
87
+ * yet — there is no default to choose.
88
+ */
89
+ getDefaultAdapterName(): string;
90
+ /**
91
+ * Return the name of the default adapter instance. Defaults to `'default'`
92
+ * before any adapter is registered — matches the common convention in
93
+ * per-ORM `forFeature({ instanceName: 'default' })` helpers.
94
+ */
95
+ getDefaultInstanceName(): string;
96
+ /**
97
+ * Return every registration as a new array. Used by bootstrap and
98
+ * observability code to enumerate configured adapters at runtime.
99
+ */
100
+ getAll(): AdapterRegistration[];
101
+ private static keyFor;
102
+ }
103
+ //# sourceMappingURL=adapter.registry.d.ts.map