@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.
- package/LICENSE +21 -0
- package/README.md +265 -0
- package/dist/bootstrap/transactional-methods.bootstrap.d.ts +42 -0
- package/dist/bootstrap/transactional-methods.bootstrap.js +129 -0
- package/dist/bootstrap/transactional-methods.bootstrap.js.map +1 -0
- package/dist/context/transaction-context-view.d.ts +38 -0
- package/dist/context/transaction-context-view.js +48 -0
- package/dist/context/transaction-context-view.js.map +1 -0
- package/dist/context/transaction.context.d.ts +114 -0
- package/dist/context/transaction.context.js +112 -0
- package/dist/context/transaction.context.js.map +1 -0
- package/dist/decorators/inject-decorators.d.ts +36 -0
- package/dist/decorators/inject-decorators.js +45 -0
- package/dist/decorators/inject-decorators.js.map +1 -0
- package/dist/decorators/transactional.decorator.d.ts +64 -0
- package/dist/decorators/transactional.decorator.js +82 -0
- package/dist/decorators/transactional.decorator.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptor/transactional.interceptor.d.ts +28 -0
- package/dist/interceptor/transactional.interceptor.js +56 -0
- package/dist/interceptor/transactional.interceptor.js.map +1 -0
- package/dist/internal/markers.d.ts +15 -0
- package/dist/internal/markers.js +18 -0
- package/dist/internal/markers.js.map +1 -0
- package/dist/manager/adapter.registry.d.ts +103 -0
- package/dist/manager/adapter.registry.js +179 -0
- package/dist/manager/adapter.registry.js.map +1 -0
- package/dist/manager/transaction.manager.d.ts +129 -0
- package/dist/manager/transaction.manager.js +412 -0
- package/dist/manager/transaction.manager.js.map +1 -0
- package/dist/module/transactional.module.d.ts +242 -0
- package/dist/module/transactional.module.js +374 -0
- package/dist/module/transactional.module.js.map +1 -0
- package/dist/observability/transaction-observer.d.ts +78 -0
- package/dist/observability/transaction-observer.js +18 -0
- package/dist/observability/transaction-observer.js.map +1 -0
- package/dist/testing/in-memory.adapter.d.ts +66 -0
- package/dist/testing/in-memory.adapter.js +83 -0
- package/dist/testing/in-memory.adapter.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +18 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tokens/constants.d.ts +14 -0
- package/dist/tokens/constants.js +17 -0
- package/dist/tokens/constants.js.map +1 -0
- package/dist/tokens/index.d.ts +3 -0
- package/dist/tokens/index.js +11 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/tokens/token-utils.d.ts +52 -0
- package/dist/tokens/token-utils.js +67 -0
- package/dist/tokens/token-utils.js.map +1 -0
- package/dist/types/domain-event.d.ts +18 -0
- package/dist/types/domain-event.js +3 -0
- package/dist/types/domain-event.js.map +1 -0
- package/dist/types/errors.d.ts +50 -0
- package/dist/types/errors.js +61 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/isolation.d.ts +11 -0
- package/dist/types/isolation.js +3 -0
- package/dist/types/isolation.js.map +1 -0
- package/dist/types/propagation.d.ts +63 -0
- package/dist/types/propagation.js +67 -0
- package/dist/types/propagation.js.map +1 -0
- package/dist/types/transaction-adapter.d.ts +72 -0
- package/dist/types/transaction-adapter.js +3 -0
- package/dist/types/transaction-adapter.js.map +1 -0
- package/dist/types/transaction-handle.d.ts +23 -0
- package/dist/types/transaction-handle.js +3 -0
- package/dist/types/transaction-handle.js.map +1 -0
- package/dist/types/transaction-options.d.ts +88 -0
- package/dist/types/transaction-options.js +3 -0
- package/dist/types/transaction-options.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,374 @@
|
|
|
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 TransactionalModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.TransactionalModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const core_1 = require("@nestjs/core");
|
|
13
|
+
const transactional_methods_bootstrap_1 = require("../bootstrap/transactional-methods.bootstrap");
|
|
14
|
+
const transaction_context_view_1 = require("../context/transaction-context-view");
|
|
15
|
+
const transactional_interceptor_1 = require("../interceptor/transactional.interceptor");
|
|
16
|
+
const adapter_registry_1 = require("../manager/adapter.registry");
|
|
17
|
+
const transaction_manager_1 = require("../manager/transaction.manager");
|
|
18
|
+
const transaction_observer_1 = require("../observability/transaction-observer");
|
|
19
|
+
const token_utils_1 = require("../tokens/token-utils");
|
|
20
|
+
const ASYNC_OPTIONS_TOKEN = (id) => Symbol(`TRANSACTIONAL_ASYNC_OPTIONS[${id}]`);
|
|
21
|
+
const ASYNC_REGISTRATION_TOKEN = (id) => Symbol(`TRANSACTIONAL_ASYNC_REGISTRATION[${id}]`);
|
|
22
|
+
/**
|
|
23
|
+
* NestJS module that wires the core transactional runtime:
|
|
24
|
+
* {@link AdapterRegistry}, {@link TransactionManager}, and (by
|
|
25
|
+
* default) the global {@link TransactionalInterceptor}. ADR-018
|
|
26
|
+
* shape — multi-dataSource deployments call {@link forRoot} once per
|
|
27
|
+
* dataSource. Static class storage coordinates singletons across
|
|
28
|
+
* calls (mirrors Phase 14.3.2 `OutboxModule` per ADR-019).
|
|
29
|
+
*
|
|
30
|
+
* The first call registers the process-wide infrastructure; subsequent
|
|
31
|
+
* calls only contribute per-dataSource providers. Adapter-specific
|
|
32
|
+
* registration (TypeORM, Prisma, ...) is handled by the integration
|
|
33
|
+
* package's `forFeature` helper — this module only provides the
|
|
34
|
+
* adapter-agnostic infrastructure.
|
|
35
|
+
*/
|
|
36
|
+
let TransactionalModule = class TransactionalModule {
|
|
37
|
+
static { TransactionalModule_1 = this; }
|
|
38
|
+
/**
|
|
39
|
+
* @internal
|
|
40
|
+
* Process-wide map of adapter-bearing `forRoot` calls, keyed by
|
|
41
|
+
* `adapter.dataSourceName`. Used for dedup of duplicate dataSource
|
|
42
|
+
* registrations at module-definition time. The {@link AdapterRegistry}
|
|
43
|
+
* itself is populated imperatively by per-DS adapter providers
|
|
44
|
+
* (factory side effect) — this Map exists for `forRoot`-call-time
|
|
45
|
+
* coordination only.
|
|
46
|
+
*
|
|
47
|
+
* Tests that build multiple modules sequentially MUST call
|
|
48
|
+
* {@link resetForTesting} between cases.
|
|
49
|
+
*/
|
|
50
|
+
static registrations = new Map();
|
|
51
|
+
/**
|
|
52
|
+
* @internal
|
|
53
|
+
* `true` once any `forRoot` (or `forRootAsync`) call has run.
|
|
54
|
+
* First-call-special providers (singletons, interceptor, methods
|
|
55
|
+
* bootstrap, observers) are only registered when this is `false` at
|
|
56
|
+
* the start of a call.
|
|
57
|
+
*
|
|
58
|
+
* Tracked separately from {@link registrations} because the
|
|
59
|
+
* shorthand `forRoot({})` (no adapter) still registers
|
|
60
|
+
* infrastructure but contributes nothing to the Map. Q5 invariant:
|
|
61
|
+
* a second `forRoot({})` after infrastructure is already registered
|
|
62
|
+
* throws — the first call already wired everything; the second has
|
|
63
|
+
* nothing to add.
|
|
64
|
+
*/
|
|
65
|
+
static infrastructureRegistered = false;
|
|
66
|
+
/**
|
|
67
|
+
* @internal
|
|
68
|
+
* Counter for `forRootAsync`-only token uniqueness. Not strictly
|
|
69
|
+
* needed for `Symbol()` (each call returns a unique symbol
|
|
70
|
+
* regardless of description), but keeping a numeric id makes
|
|
71
|
+
* provider names deterministic in error messages.
|
|
72
|
+
*/
|
|
73
|
+
static asyncCounter = 0;
|
|
74
|
+
/**
|
|
75
|
+
* Test-only — drop every registration so a subsequent `forRoot`
|
|
76
|
+
* starts from a clean slate. Mirrors the pattern used with
|
|
77
|
+
* `OutboxModule.resetForTesting` and `EntitiesMetadataStorage` in
|
|
78
|
+
* `@nestjs/typeorm` test suites.
|
|
79
|
+
*
|
|
80
|
+
* Production code should never call this. Calling at runtime after
|
|
81
|
+
* the module has been initialised does NOT clear the provider tree
|
|
82
|
+
* NestJS already built — it only affects subsequent `forRoot` calls.
|
|
83
|
+
*
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
static resetForTesting() {
|
|
87
|
+
this.registrations.clear();
|
|
88
|
+
this.infrastructureRegistered = false;
|
|
89
|
+
this.asyncCounter = 0;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Synchronous registration. Each call registers one dataSource's
|
|
93
|
+
* adapter (or, with `adapter` omitted, only the process-wide
|
|
94
|
+
* infrastructure for an integration package's `forFeature` to write
|
|
95
|
+
* into).
|
|
96
|
+
*
|
|
97
|
+
* @example Single-adapter
|
|
98
|
+
* ```ts
|
|
99
|
+
* TransactionalModule.forRoot({ isGlobal: true, adapter })
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* @example Multi-adapter (multiple calls)
|
|
103
|
+
* ```ts
|
|
104
|
+
* TransactionalModule.forRoot({ isGlobal: true, adapter: defaultAdapter }),
|
|
105
|
+
* TransactionalModule.forRoot({ adapter: billingAdapter }),
|
|
106
|
+
* TransactionalModule.forRoot({ adapter: inventoryAdapter }),
|
|
107
|
+
* ```
|
|
108
|
+
*
|
|
109
|
+
* @example Infrastructure-only (TypeORM forFeature handles adapters)
|
|
110
|
+
* ```ts
|
|
111
|
+
* TransactionalModule.forRoot({ isGlobal: true }),
|
|
112
|
+
* TypeOrmTransactionalModule.forFeature({ dataSource }),
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
static forRoot(options = {}) {
|
|
116
|
+
const isFirst = !this.infrastructureRegistered;
|
|
117
|
+
const adapter = options.adapter;
|
|
118
|
+
if (adapter !== undefined) {
|
|
119
|
+
const ds = adapter.dataSourceName;
|
|
120
|
+
if (this.registrations.has(ds)) {
|
|
121
|
+
throw new Error(`TransactionalModule.forRoot — dataSource '${ds}' already registered. ` +
|
|
122
|
+
`dataSource names must be unique across forRoot calls. ` +
|
|
123
|
+
`If this is a test, call TransactionalModule.resetForTesting() between cases.`);
|
|
124
|
+
}
|
|
125
|
+
this.registrations.set(ds, {
|
|
126
|
+
adapterName: adapter.name,
|
|
127
|
+
instanceName: ds,
|
|
128
|
+
adapter,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
else if (!isFirst) {
|
|
132
|
+
throw new Error(`TransactionalModule.forRoot({}) called without adapter, but infrastructure ` +
|
|
133
|
+
`has already been registered by an earlier forRoot call. Pass an adapter to ` +
|
|
134
|
+
`register an additional dataSource, or omit this call entirely. ` +
|
|
135
|
+
`If this is a test, call TransactionalModule.resetForTesting() between cases.`);
|
|
136
|
+
}
|
|
137
|
+
if (!isFirst && options.observers !== undefined) {
|
|
138
|
+
throw new Error(`TransactionalModule.forRoot({ observers }) — observers can only be passed in ` +
|
|
139
|
+
`the first forRoot call. Subsequent calls must omit the observers field.`);
|
|
140
|
+
}
|
|
141
|
+
const providers = [];
|
|
142
|
+
const exportTokens = [];
|
|
143
|
+
if (adapter !== undefined) {
|
|
144
|
+
providers.push(...buildPerDataSourceProviders(adapter));
|
|
145
|
+
exportTokens.push(...buildPerDataSourceExports(adapter.dataSourceName));
|
|
146
|
+
}
|
|
147
|
+
if (isFirst) {
|
|
148
|
+
this.infrastructureRegistered = true;
|
|
149
|
+
// ADAPTER_REGISTRY factory closes over the static `registrations`
|
|
150
|
+
// Map. By the time NestJS resolves this factory, every synchronous
|
|
151
|
+
// `forRoot` body has run and the Map is fully populated. Pattern
|
|
152
|
+
// mirrors Phase 14.3.2 `OutboxModule` per ADR-019.
|
|
153
|
+
providers.push({
|
|
154
|
+
provide: adapter_registry_1.ADAPTER_REGISTRY,
|
|
155
|
+
useFactory: () => buildRegistryFromStaticStorage(TransactionalModule_1),
|
|
156
|
+
});
|
|
157
|
+
providers.push({
|
|
158
|
+
provide: adapter_registry_1.AdapterRegistry,
|
|
159
|
+
useExisting: adapter_registry_1.ADAPTER_REGISTRY,
|
|
160
|
+
});
|
|
161
|
+
providers.push(transaction_manager_1.TransactionManager);
|
|
162
|
+
if (options.observers !== undefined) {
|
|
163
|
+
providers.push({
|
|
164
|
+
provide: transaction_observer_1.TRANSACTION_OBSERVERS,
|
|
165
|
+
useValue: [...options.observers],
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (options.registerInterceptor !== false) {
|
|
169
|
+
providers.push({
|
|
170
|
+
provide: core_1.APP_INTERCEPTOR,
|
|
171
|
+
useClass: transactional_interceptor_1.TransactionalInterceptor,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (options.registerMethodsBootstrap !== false) {
|
|
175
|
+
providers.push(transactional_methods_bootstrap_1.TransactionalMethodsBootstrap);
|
|
176
|
+
}
|
|
177
|
+
exportTokens.push(transaction_manager_1.TransactionManager, adapter_registry_1.ADAPTER_REGISTRY, adapter_registry_1.AdapterRegistry);
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
module: TransactionalModule_1,
|
|
181
|
+
global: options.isGlobal ?? true,
|
|
182
|
+
imports: [core_1.DiscoveryModule],
|
|
183
|
+
providers,
|
|
184
|
+
exports: exportTokens,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Asynchronous registration. Each call registers one dataSource's
|
|
189
|
+
* adapter (resolved asynchronously via `useFactory`). Multi-DS
|
|
190
|
+
* deployments call `forRootAsync` once per dataSource.
|
|
191
|
+
*
|
|
192
|
+
* **Per-DS DI token limitation**: per-DS tokens
|
|
193
|
+
* (`getTransactionalAdapterToken(ds)`, etc.) are NOT registered
|
|
194
|
+
* for `forRootAsync` calls because the dataSource identifier is
|
|
195
|
+
* only known after the async factory runs, while NestJS provider
|
|
196
|
+
* tokens must be declared statically. If per-DS injection matters,
|
|
197
|
+
* use sync `forRoot({ adapter })` instead — build the adapter
|
|
198
|
+
* configuration through your own async logic before reaching the
|
|
199
|
+
* module imports.
|
|
200
|
+
*
|
|
201
|
+
* `forRootAsync` still works for `AdapterRegistry`-based access
|
|
202
|
+
* (`@Transactional({ dataSource })`,
|
|
203
|
+
* `getCurrentEntityManager(dataSource)`,
|
|
204
|
+
* `manager.run({ dataSource })`) — those route through the registry,
|
|
205
|
+
* which is populated by the async factory's side effect via
|
|
206
|
+
* `AdapterRegistry.register(...)`.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* TransactionalModule.forRootAsync({
|
|
211
|
+
* inject: [ConfigService],
|
|
212
|
+
* useFactory: (config: ConfigService) => ({
|
|
213
|
+
* adapter: buildAdapterFromConfig(config),
|
|
214
|
+
* }),
|
|
215
|
+
* });
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
static forRootAsync(options) {
|
|
219
|
+
const isFirst = !this.infrastructureRegistered;
|
|
220
|
+
const id = this.asyncCounter++;
|
|
221
|
+
const asyncToken = ASYNC_OPTIONS_TOKEN(id);
|
|
222
|
+
const registrationToken = ASYNC_REGISTRATION_TOKEN(id);
|
|
223
|
+
// forRootAsync cannot dedup at call time (dataSource name unknown
|
|
224
|
+
// until factory runs). The eager-registration factory below calls
|
|
225
|
+
// AdapterRegistry.register at provider-resolution time; duplicate
|
|
226
|
+
// dataSources propagate as registry-level overwrites at runtime
|
|
227
|
+
// rather than module-build-time errors. This is the documented
|
|
228
|
+
// limitation that pushes per-DS-injection-needs to sync forRoot.
|
|
229
|
+
const asyncOptionsProvider = {
|
|
230
|
+
provide: asyncToken,
|
|
231
|
+
useFactory: options.useFactory,
|
|
232
|
+
inject: options.inject ? [...options.inject] : undefined,
|
|
233
|
+
};
|
|
234
|
+
const adapterEagerRegistrationProvider = {
|
|
235
|
+
provide: registrationToken,
|
|
236
|
+
useFactory: (opts, registry) => {
|
|
237
|
+
if (opts.adapter !== undefined) {
|
|
238
|
+
registry.register({
|
|
239
|
+
adapterName: opts.adapter.name,
|
|
240
|
+
instanceName: opts.adapter.dataSourceName,
|
|
241
|
+
adapter: opts.adapter,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return true;
|
|
245
|
+
},
|
|
246
|
+
inject: [asyncToken, adapter_registry_1.ADAPTER_REGISTRY],
|
|
247
|
+
};
|
|
248
|
+
const providers = [
|
|
249
|
+
asyncOptionsProvider,
|
|
250
|
+
adapterEagerRegistrationProvider,
|
|
251
|
+
];
|
|
252
|
+
const exportTokens = [];
|
|
253
|
+
if (isFirst) {
|
|
254
|
+
this.infrastructureRegistered = true;
|
|
255
|
+
// forRootAsync's `AdapterRegistry` is built fresh and the
|
|
256
|
+
// eager-registration factory below mutates it via
|
|
257
|
+
// `register(...)`. Ordering: NestJS resolves the registry
|
|
258
|
+
// factory, then the eager-registration factory whose `inject`
|
|
259
|
+
// depends on it — so by the time consumers read the registry,
|
|
260
|
+
// every adapter has been registered.
|
|
261
|
+
providers.push({
|
|
262
|
+
provide: adapter_registry_1.ADAPTER_REGISTRY,
|
|
263
|
+
useFactory: () => new adapter_registry_1.AdapterRegistry(),
|
|
264
|
+
});
|
|
265
|
+
providers.push({
|
|
266
|
+
provide: adapter_registry_1.AdapterRegistry,
|
|
267
|
+
useExisting: adapter_registry_1.ADAPTER_REGISTRY,
|
|
268
|
+
});
|
|
269
|
+
providers.push(transaction_manager_1.TransactionManager);
|
|
270
|
+
// First-call-only observers honoring (Q2 invariant). The async
|
|
271
|
+
// factory may return `observers` — only the first forRootAsync's
|
|
272
|
+
// observers are wired; subsequent calls' observers are ignored
|
|
273
|
+
// (we cannot throw at call time because the value is only known
|
|
274
|
+
// after the factory runs).
|
|
275
|
+
providers.push({
|
|
276
|
+
provide: transaction_observer_1.TRANSACTION_OBSERVERS,
|
|
277
|
+
useFactory: (opts) => opts.observers ? [...opts.observers] : [],
|
|
278
|
+
inject: [asyncToken],
|
|
279
|
+
});
|
|
280
|
+
if (options.registerInterceptor !== false) {
|
|
281
|
+
providers.push({
|
|
282
|
+
provide: core_1.APP_INTERCEPTOR,
|
|
283
|
+
useClass: transactional_interceptor_1.TransactionalInterceptor,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
if (options.registerMethodsBootstrap !== false) {
|
|
287
|
+
providers.push(transactional_methods_bootstrap_1.TransactionalMethodsBootstrap);
|
|
288
|
+
}
|
|
289
|
+
exportTokens.push(transaction_manager_1.TransactionManager, adapter_registry_1.ADAPTER_REGISTRY, adapter_registry_1.AdapterRegistry);
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
module: TransactionalModule_1,
|
|
293
|
+
global: options.isGlobal ?? true,
|
|
294
|
+
imports: [core_1.DiscoveryModule, ...(options.imports ?? [])],
|
|
295
|
+
providers,
|
|
296
|
+
exports: exportTokens,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
exports.TransactionalModule = TransactionalModule;
|
|
301
|
+
exports.TransactionalModule = TransactionalModule = TransactionalModule_1 = __decorate([
|
|
302
|
+
(0, common_1.Module)({})
|
|
303
|
+
], TransactionalModule);
|
|
304
|
+
/**
|
|
305
|
+
* Per-dataSource DI providers for ADR-018's token-based access pattern.
|
|
306
|
+
* Each adapter-bearing `forRoot` call registers, for the supplied
|
|
307
|
+
* adapter:
|
|
308
|
+
*
|
|
309
|
+
* - `getTransactionalAdapterToken(ds)` — `useValue` the adapter
|
|
310
|
+
* directly. The {@link AdapterRegistry}'s population is handled by
|
|
311
|
+
* the first-call factory (which closes over the static
|
|
312
|
+
* `registrations` Map and walks every entry at resolution time);
|
|
313
|
+
* per-DS adapter providers therefore do NOT need to inject the
|
|
314
|
+
* registry — they would not see it across multi-call DI scopes
|
|
315
|
+
* without `isGlobal: true`.
|
|
316
|
+
* - `getTransactionContextToken(ds)` — a {@link TransactionContextView}
|
|
317
|
+
* pre-bound to the dataSource. Sync — the dataSource name is known
|
|
318
|
+
* at call time.
|
|
319
|
+
* - `getTransactionManagerToken(ds)` — `useExisting` alias to the
|
|
320
|
+
* class-token {@link TransactionManager}. Per-call dataSource
|
|
321
|
+
* selection still goes through `manager.run({ dataSource })`; this
|
|
322
|
+
* token exists for symmetry with `@nestjs/typeorm`'s per-dataSource
|
|
323
|
+
* injection pattern (DD-022).
|
|
324
|
+
*
|
|
325
|
+
* `useExisting: TransactionManager` here works without cross-module
|
|
326
|
+
* import because each DynamicModule with `module: TransactionalModule`
|
|
327
|
+
* shares a class-token resolution within the parent module's DI tree
|
|
328
|
+
* once at least one of them registers the class. For consumer modules
|
|
329
|
+
* to reach `TransactionManager` and `AdapterRegistry`, set
|
|
330
|
+
* `isGlobal: true` on the first `forRoot` call (the typical pattern).
|
|
331
|
+
*/
|
|
332
|
+
function buildPerDataSourceProviders(adapter) {
|
|
333
|
+
const ds = adapter.dataSourceName;
|
|
334
|
+
return [
|
|
335
|
+
{
|
|
336
|
+
provide: (0, token_utils_1.getTransactionalAdapterToken)(ds),
|
|
337
|
+
useValue: adapter,
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
provide: (0, token_utils_1.getTransactionContextToken)(ds),
|
|
341
|
+
useValue: new transaction_context_view_1.TransactionContextView(ds),
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
provide: (0, token_utils_1.getTransactionManagerToken)(ds),
|
|
345
|
+
useExisting: transaction_manager_1.TransactionManager,
|
|
346
|
+
},
|
|
347
|
+
];
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Build a fresh {@link AdapterRegistry} populated from the static
|
|
351
|
+
* `registrations` Map on {@link TransactionalModule}. Called by the
|
|
352
|
+
* first-`forRoot`'s `ADAPTER_REGISTRY` factory at provider-resolution
|
|
353
|
+
* time — by then every synchronous `forRoot` body has populated the
|
|
354
|
+
* Map. Pattern mirrors Phase 14.3.2 `OutboxModule` per ADR-019.
|
|
355
|
+
*
|
|
356
|
+
* @internal
|
|
357
|
+
*/
|
|
358
|
+
function buildRegistryFromStaticStorage(moduleClass) {
|
|
359
|
+
const registry = new adapter_registry_1.AdapterRegistry();
|
|
360
|
+
// The Map is `private static` — read it through a structural cast.
|
|
361
|
+
const registrations = moduleClass.registrations;
|
|
362
|
+
for (const reg of registrations.values()) {
|
|
363
|
+
registry.register(reg);
|
|
364
|
+
}
|
|
365
|
+
return registry;
|
|
366
|
+
}
|
|
367
|
+
function buildPerDataSourceExports(ds) {
|
|
368
|
+
return [
|
|
369
|
+
(0, token_utils_1.getTransactionalAdapterToken)(ds),
|
|
370
|
+
(0, token_utils_1.getTransactionContextToken)(ds),
|
|
371
|
+
(0, token_utils_1.getTransactionManagerToken)(ds),
|
|
372
|
+
];
|
|
373
|
+
}
|
|
374
|
+
//# sourceMappingURL=transactional.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional.module.js","sourceRoot":"","sources":["../../src/module/transactional.module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { ExtendedTransactionOptions } from '../types/transaction-options';
|
|
2
|
+
/**
|
|
3
|
+
* Context passed to {@link TransactionObserver.onTransactionStart}. Built
|
|
4
|
+
* once by {@link TransactionManager} when a new transaction begins —
|
|
5
|
+
* i.e. the adapter has produced a {@link TransactionHandle} and the entry
|
|
6
|
+
* has been registered on the active {@link TransactionContextStore}.
|
|
7
|
+
*/
|
|
8
|
+
export interface TransactionStartContext {
|
|
9
|
+
/** Unique identifier of the transaction (from the adapter's handle). */
|
|
10
|
+
readonly transactionId: string;
|
|
11
|
+
/** Adapter type name, e.g. `'typeorm'`. */
|
|
12
|
+
readonly adapterName: string;
|
|
13
|
+
/** Adapter instance name, e.g. `'primary'`, `'billing'`. */
|
|
14
|
+
readonly adapterInstanceName: string;
|
|
15
|
+
/** Correlation id of the surrounding {@link TransactionContextStore}. */
|
|
16
|
+
readonly correlationId: string;
|
|
17
|
+
/** Options as passed to `TransactionManager.run`. */
|
|
18
|
+
readonly options: ExtendedTransactionOptions;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Context passed to {@link TransactionObserver.onTransactionCommit}.
|
|
22
|
+
* Emitted after the adapter has committed; immediately before the
|
|
23
|
+
* `afterCommitHooks` run.
|
|
24
|
+
*/
|
|
25
|
+
export interface TransactionCommitContext extends TransactionStartContext {
|
|
26
|
+
/** Elapsed wall-clock time between transaction start and commit, in ms. */
|
|
27
|
+
readonly durationMs: number;
|
|
28
|
+
/** Number of `afterCommit` hooks registered on this transaction. */
|
|
29
|
+
readonly commitCount: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Context passed to {@link TransactionObserver.onTransactionRollback}.
|
|
33
|
+
* Emitted after the adapter has rolled back; immediately before the
|
|
34
|
+
* `afterRollbackHooks` run.
|
|
35
|
+
*/
|
|
36
|
+
export interface TransactionRollbackContext extends TransactionStartContext {
|
|
37
|
+
/** Elapsed wall-clock time between transaction start and rollback, in ms. */
|
|
38
|
+
readonly durationMs: number;
|
|
39
|
+
/** The error that caused the rollback — propagated to the caller afterwards. */
|
|
40
|
+
readonly error: unknown;
|
|
41
|
+
/** Number of `afterRollback` hooks registered on this transaction. */
|
|
42
|
+
readonly rollbackCount: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Observer interface for monitoring transaction lifecycle. All methods are
|
|
46
|
+
* optional so an observer can subscribe only to the events it cares about
|
|
47
|
+
* (e.g. a metrics exporter typically wires only `onTransactionCommit` /
|
|
48
|
+
* `onTransactionRollback`).
|
|
49
|
+
*
|
|
50
|
+
* Observer implementations **must not** depend on any side effect of the
|
|
51
|
+
* transactional database state, and **must not** throw to influence the
|
|
52
|
+
* outcome — {@link TransactionManager} catches observer errors and logs
|
|
53
|
+
* them via its NestJS Logger, continuing with the remaining observers.
|
|
54
|
+
* This is a monitoring hook, not a control-flow hook — use the
|
|
55
|
+
* `registerAfterCommit` / `registerAfterRollback` APIs on the manager if
|
|
56
|
+
* you need to react to the transaction outcome within the same unit of
|
|
57
|
+
* work.
|
|
58
|
+
*/
|
|
59
|
+
export interface TransactionObserver {
|
|
60
|
+
onTransactionStart?(ctx: TransactionStartContext): void;
|
|
61
|
+
onTransactionCommit?(ctx: TransactionCommitContext): void;
|
|
62
|
+
onTransactionRollback?(ctx: TransactionRollbackContext): void;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* DI token under which {@link TransactionObserver} instances are
|
|
66
|
+
* registered. Wired automatically by
|
|
67
|
+
* `TransactionalModule.forRoot({ observers })`; can also be provided by
|
|
68
|
+
* user code for DI-resolved observers:
|
|
69
|
+
*
|
|
70
|
+
* ```ts
|
|
71
|
+
* providers: [
|
|
72
|
+
* MetricsObserver,
|
|
73
|
+
* { provide: TRANSACTION_OBSERVERS, useFactory: (m) => [m], inject: [MetricsObserver] },
|
|
74
|
+
* ]
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export declare const TRANSACTION_OBSERVERS: unique symbol;
|
|
78
|
+
//# sourceMappingURL=transaction-observer.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TRANSACTION_OBSERVERS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* DI token under which {@link TransactionObserver} instances are
|
|
6
|
+
* registered. Wired automatically by
|
|
7
|
+
* `TransactionalModule.forRoot({ observers })`; can also be provided by
|
|
8
|
+
* user code for DI-resolved observers:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* providers: [
|
|
12
|
+
* MetricsObserver,
|
|
13
|
+
* { provide: TRANSACTION_OBSERVERS, useFactory: (m) => [m], inject: [MetricsObserver] },
|
|
14
|
+
* ]
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
exports.TRANSACTION_OBSERVERS = Symbol('TRANSACTION_OBSERVERS');
|
|
18
|
+
//# sourceMappingURL=transaction-observer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction-observer.js","sourceRoot":"","sources":["../../src/observability/transaction-observer.ts"],"names":[],"mappings":";;;AA2EA;;;;;;;;;;;;GAYG;AACU,QAAA,qBAAqB,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { TransactionAdapter } from '../types/transaction-adapter';
|
|
2
|
+
import type { TransactionHandle } from '../types/transaction-handle';
|
|
3
|
+
import type { TransactionOptions } from '../types/transaction-options';
|
|
4
|
+
/**
|
|
5
|
+
* Handle emitted by {@link InMemoryTransactionAdapter}. Extends
|
|
6
|
+
* {@link TransactionHandle} with a mutable `operations` array that tests
|
|
7
|
+
* can push into to simulate a unit of work and later assert on what the
|
|
8
|
+
* adapter recorded.
|
|
9
|
+
*/
|
|
10
|
+
export interface InMemoryTransactionHandle extends TransactionHandle {
|
|
11
|
+
/** Mutable scratch space for callers to record simulated operations. */
|
|
12
|
+
readonly operations: unknown[];
|
|
13
|
+
}
|
|
14
|
+
/** A committed transaction record captured by {@link InMemoryTransactionAdapter}. */
|
|
15
|
+
export interface InMemoryCommittedTransaction {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly operations: unknown[];
|
|
18
|
+
readonly options: TransactionOptions;
|
|
19
|
+
}
|
|
20
|
+
/** A rolled-back transaction record captured by {@link InMemoryTransactionAdapter}. */
|
|
21
|
+
export interface InMemoryRolledBackTransaction extends InMemoryCommittedTransaction {
|
|
22
|
+
readonly error: unknown;
|
|
23
|
+
}
|
|
24
|
+
/** A savepoint lifecycle record captured by {@link InMemoryTransactionAdapter}. */
|
|
25
|
+
export interface InMemorySavepointRecord {
|
|
26
|
+
readonly parentId: string;
|
|
27
|
+
readonly savepointId: string;
|
|
28
|
+
}
|
|
29
|
+
/** A rolled-back savepoint record: {@link InMemorySavepointRecord} plus the error. */
|
|
30
|
+
export interface InMemoryRolledBackSavepointRecord extends InMemorySavepointRecord {
|
|
31
|
+
readonly error: unknown;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Adapter implementation intended for unit tests. Keeps the whole transaction
|
|
35
|
+
* lifecycle in memory and exposes observation arrays (committed, rolled back,
|
|
36
|
+
* savepoints created/released/rolled back) for assertions.
|
|
37
|
+
*
|
|
38
|
+
* Not suitable for production use — issues no real SQL, does no real
|
|
39
|
+
* persistence, and offers no isolation guarantees. Test-only.
|
|
40
|
+
*
|
|
41
|
+
* Exported through `@nestjs-transactional/core/testing`, never through the
|
|
42
|
+
* main entry point.
|
|
43
|
+
*/
|
|
44
|
+
export declare class InMemoryTransactionAdapter implements TransactionAdapter<InMemoryTransactionHandle> {
|
|
45
|
+
readonly name = "in-memory";
|
|
46
|
+
/**
|
|
47
|
+
* dataSource name this adapter is bound to (DD-021). Defaults to
|
|
48
|
+
* `'default'`; override via the constructor to register multiple
|
|
49
|
+
* in-memory adapters under distinct dataSource names in tests.
|
|
50
|
+
*/
|
|
51
|
+
readonly dataSourceName: string;
|
|
52
|
+
constructor(dataSourceName?: string);
|
|
53
|
+
committedTransactions: InMemoryCommittedTransaction[];
|
|
54
|
+
rolledBackTransactions: InMemoryRolledBackTransaction[];
|
|
55
|
+
savepointsCreated: InMemorySavepointRecord[];
|
|
56
|
+
savepointsReleased: InMemorySavepointRecord[];
|
|
57
|
+
savepointsRolledBack: InMemoryRolledBackSavepointRecord[];
|
|
58
|
+
runInTransaction<T>(options: TransactionOptions, fn: (handle: InMemoryTransactionHandle) => Promise<T>): Promise<T>;
|
|
59
|
+
runInSavepoint<T>(parent: InMemoryTransactionHandle, fn: (handle: InMemoryTransactionHandle) => Promise<T>): Promise<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Clear all observation arrays. Call between tests to avoid cross-test
|
|
62
|
+
* leakage when reusing a single adapter instance.
|
|
63
|
+
*/
|
|
64
|
+
reset(): void;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=in-memory.adapter.d.ts.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InMemoryTransactionAdapter = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
/**
|
|
6
|
+
* Adapter implementation intended for unit tests. Keeps the whole transaction
|
|
7
|
+
* lifecycle in memory and exposes observation arrays (committed, rolled back,
|
|
8
|
+
* savepoints created/released/rolled back) for assertions.
|
|
9
|
+
*
|
|
10
|
+
* Not suitable for production use — issues no real SQL, does no real
|
|
11
|
+
* persistence, and offers no isolation guarantees. Test-only.
|
|
12
|
+
*
|
|
13
|
+
* Exported through `@nestjs-transactional/core/testing`, never through the
|
|
14
|
+
* main entry point.
|
|
15
|
+
*/
|
|
16
|
+
class InMemoryTransactionAdapter {
|
|
17
|
+
name = 'in-memory';
|
|
18
|
+
/**
|
|
19
|
+
* dataSource name this adapter is bound to (DD-021). Defaults to
|
|
20
|
+
* `'default'`; override via the constructor to register multiple
|
|
21
|
+
* in-memory adapters under distinct dataSource names in tests.
|
|
22
|
+
*/
|
|
23
|
+
dataSourceName;
|
|
24
|
+
constructor(dataSourceName = 'default') {
|
|
25
|
+
this.dataSourceName = dataSourceName;
|
|
26
|
+
}
|
|
27
|
+
committedTransactions = [];
|
|
28
|
+
rolledBackTransactions = [];
|
|
29
|
+
savepointsCreated = [];
|
|
30
|
+
savepointsReleased = [];
|
|
31
|
+
savepointsRolledBack = [];
|
|
32
|
+
async runInTransaction(options, fn) {
|
|
33
|
+
const handle = {
|
|
34
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
35
|
+
adapterName: this.name,
|
|
36
|
+
operations: [],
|
|
37
|
+
};
|
|
38
|
+
try {
|
|
39
|
+
const result = await fn(handle);
|
|
40
|
+
this.committedTransactions.push({
|
|
41
|
+
id: handle.id,
|
|
42
|
+
operations: handle.operations,
|
|
43
|
+
options,
|
|
44
|
+
});
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
this.rolledBackTransactions.push({
|
|
49
|
+
id: handle.id,
|
|
50
|
+
operations: handle.operations,
|
|
51
|
+
options,
|
|
52
|
+
error,
|
|
53
|
+
});
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async runInSavepoint(parent, fn) {
|
|
58
|
+
const savepointId = (0, node_crypto_1.randomUUID)();
|
|
59
|
+
this.savepointsCreated.push({ parentId: parent.id, savepointId });
|
|
60
|
+
try {
|
|
61
|
+
const result = await fn(parent);
|
|
62
|
+
this.savepointsReleased.push({ parentId: parent.id, savepointId });
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
this.savepointsRolledBack.push({ parentId: parent.id, savepointId, error });
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Clear all observation arrays. Call between tests to avoid cross-test
|
|
72
|
+
* leakage when reusing a single adapter instance.
|
|
73
|
+
*/
|
|
74
|
+
reset() {
|
|
75
|
+
this.committedTransactions = [];
|
|
76
|
+
this.rolledBackTransactions = [];
|
|
77
|
+
this.savepointsCreated = [];
|
|
78
|
+
this.savepointsReleased = [];
|
|
79
|
+
this.savepointsRolledBack = [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.InMemoryTransactionAdapter = InMemoryTransactionAdapter;
|
|
83
|
+
//# sourceMappingURL=in-memory.adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-memory.adapter.js","sourceRoot":"","sources":["../../src/testing/in-memory.adapter.ts"],"names":[],"mappings":";;;AAAA,6CAAyC;AAwCzC;;;;;;;;;;GAUG;AACH,MAAa,0BAA0B;IAC5B,IAAI,GAAG,WAAW,CAAC;IAE5B;;;;OAIG;IACM,cAAc,CAAS;IAEhC,YAAY,cAAc,GAAG,SAAS;QACpC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAED,qBAAqB,GAAmC,EAAE,CAAC;IAC3D,sBAAsB,GAAoC,EAAE,CAAC;IAC7D,iBAAiB,GAA8B,EAAE,CAAC;IAClD,kBAAkB,GAA8B,EAAE,CAAC;IACnD,oBAAoB,GAAwC,EAAE,CAAC;IAE/D,KAAK,CAAC,gBAAgB,CACpB,OAA2B,EAC3B,EAAqD;QAErD,MAAM,MAAM,GAA8B;YACxC,EAAE,EAAE,IAAA,wBAAU,GAAE;YAChB,WAAW,EAAE,IAAI,CAAC,IAAI;YACtB,UAAU,EAAE,EAAE;SACf,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC;gBAC9B,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO;aACR,CAAC,CAAC;YACH,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC;gBAC/B,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO;gBACP,KAAK;aACN,CAAC,CAAC;YACH,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,MAAiC,EACjC,EAAqD;QAErD,MAAM,WAAW,GAAG,IAAA,wBAAU,GAAE,CAAC;QACjC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;QAElE,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;YACnE,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5E,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,IAAI,CAAC,qBAAqB,GAAG,EAAE,CAAC;QAChC,IAAI,CAAC,sBAAsB,GAAG,EAAE,CAAC;QACjC,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAC;IACjC,CAAC;CACF;AA7ED,gEA6EC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
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("./in-memory.adapter"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC"}
|