@nestjs-transactional/typeorm 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 (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +361 -0
  3. package/dist/adapter/typeorm.adapter.d.ts +39 -0
  4. package/dist/adapter/typeorm.adapter.js +79 -0
  5. package/dist/adapter/typeorm.adapter.js.map +1 -0
  6. package/dist/helpers/get-entity-manager.d.ts +27 -0
  7. package/dist/helpers/get-entity-manager.js +52 -0
  8. package/dist/helpers/get-entity-manager.js.map +1 -0
  9. package/dist/index.d.ts +5 -0
  10. package/dist/index.js +21 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/module/typeorm-transactional.module.d.ts +157 -0
  13. package/dist/module/typeorm-transactional.module.js +289 -0
  14. package/dist/module/typeorm-transactional.module.js.map +1 -0
  15. package/dist/patching/data-source-patches.d.ts +48 -0
  16. package/dist/patching/data-source-patches.js +139 -0
  17. package/dist/patching/data-source-patches.js.map +1 -0
  18. package/dist/patching/entity-manager-patches.d.ts +31 -0
  19. package/dist/patching/entity-manager-patches.js +86 -0
  20. package/dist/patching/entity-manager-patches.js.map +1 -0
  21. package/dist/patching/index.d.ts +49 -0
  22. package/dist/patching/index.js +75 -0
  23. package/dist/patching/index.js.map +1 -0
  24. package/dist/patching/managed-registry.d.ts +59 -0
  25. package/dist/patching/managed-registry.js +112 -0
  26. package/dist/patching/managed-registry.js.map +1 -0
  27. package/dist/patching/repository-patches.d.ts +56 -0
  28. package/dist/patching/repository-patches.js +150 -0
  29. package/dist/patching/repository-patches.js.map +1 -0
  30. package/dist/patching/symbols.d.ts +53 -0
  31. package/dist/patching/symbols.js +56 -0
  32. package/dist/patching/symbols.js.map +1 -0
  33. package/dist/types/typeorm-transaction-handle.d.ts +17 -0
  34. package/dist/types/typeorm-transaction-handle.js +3 -0
  35. package/dist/types/typeorm-transaction-handle.js.map +1 -0
  36. package/package.json +79 -0
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Wrap `EntityManager.prototype.getRepository` so the resulting
3
+ * `Repository` instance carries the {@link TYPEORM_ENTITY_MANAGER_NAME}
4
+ * stamp pointing at this `EntityManager`. The patched
5
+ * `Repository.prototype.manager` getter then has a stable fallback
6
+ * (the original, non-active manager) and a way to discover the
7
+ * dataSource name (via `original.connection`).
8
+ *
9
+ * This wrap matters specifically for the
10
+ * `@InjectEntityManager() em.getRepository(Entity).save(...)` user
11
+ * pattern (Phase 14.20 Q1 Option A coverage proof): the injected
12
+ * `EntityManager` is the DataSource's default (non-transactional)
13
+ * manager. Calling `em.getRepository(Entity)` would, without this
14
+ * wrap, return a Repository whose only `manager` reference is `em`
15
+ * itself — and the patched `Repository.prototype.manager` getter
16
+ * would have nothing to read. With the wrap, the returned repo
17
+ * carries `em` under the stash symbol, the getter resolves the
18
+ * dataSource name from `em.connection`, and the lookup proceeds
19
+ * normally. Net effect: even when reaching a Repository through
20
+ * `@InjectEntityManager`, the active transactional EntityManager is
21
+ * still used.
22
+ *
23
+ * `@InjectEntityManager` + direct method call (`em.save(Entity, ...)`)
24
+ * is NOT covered by this patch — that is the documented limitation
25
+ * (Phase 14.20 Q5). Use `getCurrentEntityManager()` as the escape
26
+ * hatch.
27
+ */
28
+ export declare function applyEntityManagerPatches(): void;
29
+ /** Predicate exported for unit tests. @internal */
30
+ export declare function areEntityManagerPatchesApplied(): boolean;
31
+ //# sourceMappingURL=entity-manager-patches.d.ts.map
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/unbound-method */
3
+ // The patching machinery wraps `EntityManager.prototype.getRepository`
4
+ // and stamps hidden symbol-keyed properties on returned
5
+ // `Repository` instances. These properties live outside TypeORM's
6
+ // public types by design, so unsafe-access and unbound-method
7
+ // lint rules fire on every access. File-level disable keeps the
8
+ // patch code readable; the runtime contract is documented in the
9
+ // JSDoc above each patch.
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.applyEntityManagerPatches = applyEntityManagerPatches;
12
+ exports.areEntityManagerPatchesApplied = areEntityManagerPatchesApplied;
13
+ const typeorm_1 = require("typeorm");
14
+ const symbols_1 = require("./symbols");
15
+ /**
16
+ * Tracks whether `EntityManager.prototype.getRepository` has been
17
+ * wrapped. Idempotent install pattern matching
18
+ * `repository-patches.ts` — once installed, the wrap lives for the
19
+ * remainder of the process. See repository-patches.ts JSDoc for
20
+ * the design-note rationale.
21
+ */
22
+ let installed = false;
23
+ /**
24
+ * Captured original of `EntityManager.prototype.getRepository` for
25
+ * the wrapper to delegate into.
26
+ */
27
+ let originalGetRepository;
28
+ /**
29
+ * Wrap `EntityManager.prototype.getRepository` so the resulting
30
+ * `Repository` instance carries the {@link TYPEORM_ENTITY_MANAGER_NAME}
31
+ * stamp pointing at this `EntityManager`. The patched
32
+ * `Repository.prototype.manager` getter then has a stable fallback
33
+ * (the original, non-active manager) and a way to discover the
34
+ * dataSource name (via `original.connection`).
35
+ *
36
+ * This wrap matters specifically for the
37
+ * `@InjectEntityManager() em.getRepository(Entity).save(...)` user
38
+ * pattern (Phase 14.20 Q1 Option A coverage proof): the injected
39
+ * `EntityManager` is the DataSource's default (non-transactional)
40
+ * manager. Calling `em.getRepository(Entity)` would, without this
41
+ * wrap, return a Repository whose only `manager` reference is `em`
42
+ * itself — and the patched `Repository.prototype.manager` getter
43
+ * would have nothing to read. With the wrap, the returned repo
44
+ * carries `em` under the stash symbol, the getter resolves the
45
+ * dataSource name from `em.connection`, and the lookup proceeds
46
+ * normally. Net effect: even when reaching a Repository through
47
+ * `@InjectEntityManager`, the active transactional EntityManager is
48
+ * still used.
49
+ *
50
+ * `@InjectEntityManager` + direct method call (`em.save(Entity, ...)`)
51
+ * is NOT covered by this patch — that is the documented limitation
52
+ * (Phase 14.20 Q5). Use `getCurrentEntityManager()` as the escape
53
+ * hatch.
54
+ */
55
+ function applyEntityManagerPatches() {
56
+ if (installed) {
57
+ return;
58
+ }
59
+ originalGetRepository = typeorm_1.EntityManager.prototype.getRepository;
60
+ // The generic shape of `getRepository<Entity>` resists clean
61
+ // typing on a wrapper; cast through `any` to keep the wrap a
62
+ // single statement. Runtime contract is unchanged.
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ typeorm_1.EntityManager.prototype.getRepository = function patchedGetRepository(
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ target) {
67
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
68
+ const repo = originalGetRepository.call(this, target);
69
+ // Stamp the EM if not already set. EntityManager has internal
70
+ // caching, so the same Repository may be returned for repeat
71
+ // calls — re-stamping with the same value is a no-op, but the
72
+ // guard makes the intent explicit.
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ if (repo[symbols_1.TYPEORM_ENTITY_MANAGER_NAME] === undefined) {
75
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
+ repo[symbols_1.TYPEORM_ENTITY_MANAGER_NAME] = this;
77
+ }
78
+ return repo;
79
+ };
80
+ installed = true;
81
+ }
82
+ /** Predicate exported for unit tests. @internal */
83
+ function areEntityManagerPatchesApplied() {
84
+ return installed;
85
+ }
86
+ //# sourceMappingURL=entity-manager-patches.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-manager-patches.js","sourceRoot":"","sources":["../../src/patching/entity-manager-patches.ts"],"names":[],"mappings":";AAAA,2IAA2I;AAC3I,uEAAuE;AACvE,wDAAwD;AACxD,kEAAkE;AAClE,8DAA8D;AAC9D,gEAAgE;AAChE,iEAAiE;AACjE,0BAA0B;;AAgD1B,8DA+BC;AAGD,wEAEC;AAlFD,qCAAwC;AAExC,uCAAwD;AAExD;;;;;;GAMG;AACH,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB;;;GAGG;AACH,IAAI,qBAA+E,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,SAAgB,yBAAyB;IACvC,IAAI,SAAS,EAAE,CAAC;QACd,OAAO;IACT,CAAC;IAED,qBAAqB,GAAG,uBAAa,CAAC,SAAS,CAAC,aAAa,CAAC;IAE9D,6DAA6D;IAC7D,6DAA6D;IAC7D,mDAAmD;IACnD,8DAA8D;IAC7D,uBAAa,CAAC,SAAiB,CAAC,aAAa,GAAG,SAAS,oBAAoB;IAE5E,8DAA8D;IAC9D,MAAW;QAEX,oEAAoE;QACpE,MAAM,IAAI,GAAG,qBAAsB,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvD,8DAA8D;QAC9D,6DAA6D;QAC7D,8DAA8D;QAC9D,mCAAmC;QACnC,8DAA8D;QAC9D,IAAK,IAAY,CAAC,qCAA2B,CAAC,KAAK,SAAS,EAAE,CAAC;YAC7D,8DAA8D;YAC7D,IAAY,CAAC,qCAA2B,CAAC,GAAG,IAAI,CAAC;QACpD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,mDAAmD;AACnD,SAAgB,8BAA8B;IAC5C,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Phase 14.20 transparent transactional repositories — patching
3
+ * machinery. Exported so the module layer can drive `applyAllPatches`
4
+ * in `forRoot`, and so unit tests can probe state directly. None of
5
+ * these symbols are intended for application code — public API stays
6
+ * `getCurrentEntityManager` / `@Transactional` / `@InjectRepository`.
7
+ *
8
+ * @internal
9
+ */
10
+ export { applyRepositoryPatches, areRepositoryPatchesApplied, } from './repository-patches';
11
+ export { applyEntityManagerPatches, areEntityManagerPatchesApplied, } from './entity-manager-patches';
12
+ export { patchDataSourceInstance } from './data-source-patches';
13
+ export { getActiveEntityManager, getManagedDataSourceName, isManaged, markAsManaged, resetManagedRegistry, } from './managed-registry';
14
+ export { TYPEORM_DATA_SOURCE_NAME, TYPEORM_DATA_SOURCE_PATCHED, TYPEORM_ENTITY_MANAGER_NAME, } from './symbols';
15
+ /**
16
+ * Apply both prototype-level patch families in one call. Used by
17
+ * `TypeOrmTransactionalModule.forRoot`'s registration factory.
18
+ * Idempotent — calling more than once is a no-op (each install
19
+ * routine guards on its own `installed` flag).
20
+ *
21
+ * Per-instance DataSource patches ({@link patchDataSourceInstance})
22
+ * are NOT included here because they apply to a specific instance
23
+ * and are driven separately at registration time, also idempotent.
24
+ */
25
+ export declare function applyAllPatches(): void;
26
+ /**
27
+ * Test-only — drop the managed-DataSources WeakSet so cached
28
+ * repositories from a prior test fall through the patched getter
29
+ * to their captured original manager (autocommit), as if the
30
+ * patches were never engaged.
31
+ *
32
+ * Prototype-level patches are NOT removed — they were installed
33
+ * once-and-stay (see `repository-patches.ts` design note for the
34
+ * rationale: removing a prototype getter would silently break
35
+ * Repository instances constructed under the patched setter that
36
+ * have no own-property `manager`).
37
+ *
38
+ * Per-instance DataSource patches survive on those DataSource
39
+ * instances; tests that destroy and recreate the DataSource
40
+ * between cases (the typical pattern) are unaffected. The
41
+ * idempotent-marker design (`TYPEORM_DATA_SOURCE_PATCHED`)
42
+ * additionally makes re-registering the SAME `DataSource` after
43
+ * `resetManagedRegistry` safe — `patchDataSourceInstance` becomes
44
+ * a no-op.
45
+ *
46
+ * @internal
47
+ */
48
+ export declare function resetPatchingForTesting(): void;
49
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /**
3
+ * Phase 14.20 transparent transactional repositories — patching
4
+ * machinery. Exported so the module layer can drive `applyAllPatches`
5
+ * in `forRoot`, and so unit tests can probe state directly. None of
6
+ * these symbols are intended for application code — public API stays
7
+ * `getCurrentEntityManager` / `@Transactional` / `@InjectRepository`.
8
+ *
9
+ * @internal
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TYPEORM_ENTITY_MANAGER_NAME = exports.TYPEORM_DATA_SOURCE_PATCHED = exports.TYPEORM_DATA_SOURCE_NAME = exports.resetManagedRegistry = exports.markAsManaged = exports.isManaged = exports.getManagedDataSourceName = exports.getActiveEntityManager = exports.patchDataSourceInstance = exports.areEntityManagerPatchesApplied = exports.applyEntityManagerPatches = exports.areRepositoryPatchesApplied = exports.applyRepositoryPatches = void 0;
13
+ exports.applyAllPatches = applyAllPatches;
14
+ exports.resetPatchingForTesting = resetPatchingForTesting;
15
+ const entity_manager_patches_1 = require("./entity-manager-patches");
16
+ const managed_registry_1 = require("./managed-registry");
17
+ const repository_patches_1 = require("./repository-patches");
18
+ var repository_patches_2 = require("./repository-patches");
19
+ Object.defineProperty(exports, "applyRepositoryPatches", { enumerable: true, get: function () { return repository_patches_2.applyRepositoryPatches; } });
20
+ Object.defineProperty(exports, "areRepositoryPatchesApplied", { enumerable: true, get: function () { return repository_patches_2.areRepositoryPatchesApplied; } });
21
+ var entity_manager_patches_2 = require("./entity-manager-patches");
22
+ Object.defineProperty(exports, "applyEntityManagerPatches", { enumerable: true, get: function () { return entity_manager_patches_2.applyEntityManagerPatches; } });
23
+ Object.defineProperty(exports, "areEntityManagerPatchesApplied", { enumerable: true, get: function () { return entity_manager_patches_2.areEntityManagerPatchesApplied; } });
24
+ var data_source_patches_1 = require("./data-source-patches");
25
+ Object.defineProperty(exports, "patchDataSourceInstance", { enumerable: true, get: function () { return data_source_patches_1.patchDataSourceInstance; } });
26
+ var managed_registry_2 = require("./managed-registry");
27
+ Object.defineProperty(exports, "getActiveEntityManager", { enumerable: true, get: function () { return managed_registry_2.getActiveEntityManager; } });
28
+ Object.defineProperty(exports, "getManagedDataSourceName", { enumerable: true, get: function () { return managed_registry_2.getManagedDataSourceName; } });
29
+ Object.defineProperty(exports, "isManaged", { enumerable: true, get: function () { return managed_registry_2.isManaged; } });
30
+ Object.defineProperty(exports, "markAsManaged", { enumerable: true, get: function () { return managed_registry_2.markAsManaged; } });
31
+ Object.defineProperty(exports, "resetManagedRegistry", { enumerable: true, get: function () { return managed_registry_2.resetManagedRegistry; } });
32
+ var symbols_1 = require("./symbols");
33
+ Object.defineProperty(exports, "TYPEORM_DATA_SOURCE_NAME", { enumerable: true, get: function () { return symbols_1.TYPEORM_DATA_SOURCE_NAME; } });
34
+ Object.defineProperty(exports, "TYPEORM_DATA_SOURCE_PATCHED", { enumerable: true, get: function () { return symbols_1.TYPEORM_DATA_SOURCE_PATCHED; } });
35
+ Object.defineProperty(exports, "TYPEORM_ENTITY_MANAGER_NAME", { enumerable: true, get: function () { return symbols_1.TYPEORM_ENTITY_MANAGER_NAME; } });
36
+ /**
37
+ * Apply both prototype-level patch families in one call. Used by
38
+ * `TypeOrmTransactionalModule.forRoot`'s registration factory.
39
+ * Idempotent — calling more than once is a no-op (each install
40
+ * routine guards on its own `installed` flag).
41
+ *
42
+ * Per-instance DataSource patches ({@link patchDataSourceInstance})
43
+ * are NOT included here because they apply to a specific instance
44
+ * and are driven separately at registration time, also idempotent.
45
+ */
46
+ function applyAllPatches() {
47
+ (0, repository_patches_1.applyRepositoryPatches)();
48
+ (0, entity_manager_patches_1.applyEntityManagerPatches)();
49
+ }
50
+ /**
51
+ * Test-only — drop the managed-DataSources WeakSet so cached
52
+ * repositories from a prior test fall through the patched getter
53
+ * to their captured original manager (autocommit), as if the
54
+ * patches were never engaged.
55
+ *
56
+ * Prototype-level patches are NOT removed — they were installed
57
+ * once-and-stay (see `repository-patches.ts` design note for the
58
+ * rationale: removing a prototype getter would silently break
59
+ * Repository instances constructed under the patched setter that
60
+ * have no own-property `manager`).
61
+ *
62
+ * Per-instance DataSource patches survive on those DataSource
63
+ * instances; tests that destroy and recreate the DataSource
64
+ * between cases (the typical pattern) are unaffected. The
65
+ * idempotent-marker design (`TYPEORM_DATA_SOURCE_PATCHED`)
66
+ * additionally makes re-registering the SAME `DataSource` after
67
+ * `resetManagedRegistry` safe — `patchDataSourceInstance` becomes
68
+ * a no-op.
69
+ *
70
+ * @internal
71
+ */
72
+ function resetPatchingForTesting() {
73
+ (0, managed_registry_1.resetManagedRegistry)();
74
+ }
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/patching/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAsCH,0CAGC;AAwBD,0DAEC;AAjED,qEAAqE;AACrE,yDAA0D;AAC1D,6DAA8D;AAE9D,2DAG8B;AAF5B,4HAAA,sBAAsB,OAAA;AACtB,iIAAA,2BAA2B,OAAA;AAE7B,mEAGkC;AAFhC,mIAAA,yBAAyB,OAAA;AACzB,wIAAA,8BAA8B,OAAA;AAEhC,6DAAgE;AAAvD,8HAAA,uBAAuB,OAAA;AAChC,uDAM4B;AAL1B,0HAAA,sBAAsB,OAAA;AACtB,4HAAA,wBAAwB,OAAA;AACxB,6GAAA,SAAS,OAAA;AACT,iHAAA,aAAa,OAAA;AACb,wHAAA,oBAAoB,OAAA;AAEtB,qCAImB;AAHjB,mHAAA,wBAAwB,OAAA;AACxB,sHAAA,2BAA2B,OAAA;AAC3B,sHAAA,2BAA2B,OAAA;AAG7B;;;;;;;;;GASG;AACH,SAAgB,eAAe;IAC7B,IAAA,2CAAsB,GAAE,CAAC;IACzB,IAAA,kDAAyB,GAAE,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,SAAgB,uBAAuB;IACrC,IAAA,uCAAoB,GAAE,CAAC;AACzB,CAAC"}
@@ -0,0 +1,59 @@
1
+ import type { DataSource, EntityManager } from 'typeorm';
2
+ /**
3
+ * Register `dataSource` as a "managed" DataSource under the supplied
4
+ * `name`. Stamps the name as a hidden property on the DataSource
5
+ * (under {@link TYPEORM_DATA_SOURCE_NAME}) and adds the instance to
6
+ * the {@link managedDataSources} WeakSet.
7
+ *
8
+ * Both pieces of state are read by the patched `Repository.prototype.manager`
9
+ * getter and by the per-instance `DataSource.manager` getter — the
10
+ * WeakSet membership decides whether the dispatch is transactional at
11
+ * all, and the stamped name resolves the
12
+ * `TransactionContext.getActiveTransactionByDataSource(name)` lookup.
13
+ *
14
+ * Idempotent — calling twice with the same `(dataSource, name)` pair
15
+ * is a no-op. Calling twice with different names overwrites the
16
+ * stamped name silently; in practice every callsite is
17
+ * `TypeOrmTransactionalModule.forRoot`, which dedups upstream via its
18
+ * own static-Map mechanism, so this collision should never occur in
19
+ * production.
20
+ */
21
+ export declare function markAsManaged(dataSource: DataSource, name: string): void;
22
+ /** Predicate: is `dataSource` registered as managed? */
23
+ export declare function isManaged(dataSource: DataSource): boolean;
24
+ /**
25
+ * Read the dataSource name stamped on `dataSource` by
26
+ * {@link markAsManaged}. Returns `undefined` if the DataSource has
27
+ * not been registered as managed.
28
+ */
29
+ export declare function getManagedDataSourceName(dataSource: DataSource): string | undefined;
30
+ /**
31
+ * Resolve the active transactional `EntityManager` for the given
32
+ * dataSource name from {@link TransactionContext}. Returns
33
+ * `undefined` when there is no active transaction on the current
34
+ * async chain for this dataSource — the patched getters use that
35
+ * to fall back to the original manager (autocommit semantics).
36
+ *
37
+ * Centralised here so every patch site goes through the same
38
+ * resolution rule — one place to evolve when the underlying lookup
39
+ * changes.
40
+ */
41
+ export declare function getActiveEntityManager(dataSourceName: string): EntityManager | undefined;
42
+ /**
43
+ * Test-only — drop every managed DataSource registration so a fresh
44
+ * test starts from a clean slate. Recreates the `WeakSet` (no
45
+ * `.clear()` API on `WeakSet`).
46
+ *
47
+ * NOTE: this does NOT undo the {@link TYPEORM_DATA_SOURCE_NAME} stamp
48
+ * on individual DataSource instances, nor does it revert the
49
+ * per-instance patches applied by {@link patchDataSourceInstance}.
50
+ * Tests that build a fresh `DataSource` per case are unaffected
51
+ * (the stale stamp is never observed). Tests that reuse a
52
+ * `DataSource` across `TransactionalModule.resetForTesting()` calls
53
+ * are not supported — destroy and recreate the DataSource between
54
+ * cases instead.
55
+ *
56
+ * @internal
57
+ */
58
+ export declare function resetManagedRegistry(): void;
59
+ //# sourceMappingURL=managed-registry.d.ts.map
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return */
3
+ // The patching machinery stamps and reads hidden symbol-keyed
4
+ // properties on TypeORM internals (`DataSource`, `EntityManager`,
5
+ // `Repository`). These properties live outside TypeORM's public
6
+ // types by design, so unsafe-access lint rules fire on every
7
+ // access. File-level disable keeps the patch code readable; the
8
+ // runtime contract is documented in JSDoc above each patch.
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.markAsManaged = markAsManaged;
11
+ exports.isManaged = isManaged;
12
+ exports.getManagedDataSourceName = getManagedDataSourceName;
13
+ exports.getActiveEntityManager = getActiveEntityManager;
14
+ exports.resetManagedRegistry = resetManagedRegistry;
15
+ const core_1 = require("@nestjs-transactional/core");
16
+ const symbols_1 = require("./symbols");
17
+ /**
18
+ * Process-wide set of `DataSource` instances we have registered as
19
+ * "managed" via {@link markAsManaged}. The patched
20
+ * `Repository.prototype.manager` getter consults this set to decide
21
+ * whether to dispatch transactionally or pass through unchanged —
22
+ * non-managed DataSources (e.g. those a user created manually outside
23
+ * `TypeOrmTransactionalModule`) MUST behave exactly as TypeORM does
24
+ * normally.
25
+ *
26
+ * `WeakSet` over `Set`: a managed DataSource that is destroyed and
27
+ * eligible for GC should not be retained by us.
28
+ *
29
+ * `let` not `const` so {@link resetManagedRegistry} can swap in a
30
+ * fresh empty `WeakSet` — the standard `WeakSet` API offers no
31
+ * `.clear()` method, and recreating the reference is the cleanest
32
+ * way to drop every managed entry at once for test isolation.
33
+ */
34
+ let managedDataSources = new WeakSet();
35
+ /**
36
+ * Register `dataSource` as a "managed" DataSource under the supplied
37
+ * `name`. Stamps the name as a hidden property on the DataSource
38
+ * (under {@link TYPEORM_DATA_SOURCE_NAME}) and adds the instance to
39
+ * the {@link managedDataSources} WeakSet.
40
+ *
41
+ * Both pieces of state are read by the patched `Repository.prototype.manager`
42
+ * getter and by the per-instance `DataSource.manager` getter — the
43
+ * WeakSet membership decides whether the dispatch is transactional at
44
+ * all, and the stamped name resolves the
45
+ * `TransactionContext.getActiveTransactionByDataSource(name)` lookup.
46
+ *
47
+ * Idempotent — calling twice with the same `(dataSource, name)` pair
48
+ * is a no-op. Calling twice with different names overwrites the
49
+ * stamped name silently; in practice every callsite is
50
+ * `TypeOrmTransactionalModule.forRoot`, which dedups upstream via its
51
+ * own static-Map mechanism, so this collision should never occur in
52
+ * production.
53
+ */
54
+ function markAsManaged(dataSource, name) {
55
+ managedDataSources.add(dataSource);
56
+ // Stamp the name as a hidden property. Cast to any to avoid a
57
+ // TypeORM type-shape change: we intentionally augment the runtime
58
+ // object without touching its public surface.
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ dataSource[symbols_1.TYPEORM_DATA_SOURCE_NAME] = name;
61
+ }
62
+ /** Predicate: is `dataSource` registered as managed? */
63
+ function isManaged(dataSource) {
64
+ return managedDataSources.has(dataSource);
65
+ }
66
+ /**
67
+ * Read the dataSource name stamped on `dataSource` by
68
+ * {@link markAsManaged}. Returns `undefined` if the DataSource has
69
+ * not been registered as managed.
70
+ */
71
+ function getManagedDataSourceName(dataSource) {
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ return dataSource[symbols_1.TYPEORM_DATA_SOURCE_NAME];
74
+ }
75
+ /**
76
+ * Resolve the active transactional `EntityManager` for the given
77
+ * dataSource name from {@link TransactionContext}. Returns
78
+ * `undefined` when there is no active transaction on the current
79
+ * async chain for this dataSource — the patched getters use that
80
+ * to fall back to the original manager (autocommit semantics).
81
+ *
82
+ * Centralised here so every patch site goes through the same
83
+ * resolution rule — one place to evolve when the underlying lookup
84
+ * changes.
85
+ */
86
+ function getActiveEntityManager(dataSourceName) {
87
+ const activeTx = core_1.TransactionContext.getActiveTransactionByDataSource(dataSourceName);
88
+ if (activeTx === undefined) {
89
+ return undefined;
90
+ }
91
+ return activeTx.handle.entityManager;
92
+ }
93
+ /**
94
+ * Test-only — drop every managed DataSource registration so a fresh
95
+ * test starts from a clean slate. Recreates the `WeakSet` (no
96
+ * `.clear()` API on `WeakSet`).
97
+ *
98
+ * NOTE: this does NOT undo the {@link TYPEORM_DATA_SOURCE_NAME} stamp
99
+ * on individual DataSource instances, nor does it revert the
100
+ * per-instance patches applied by {@link patchDataSourceInstance}.
101
+ * Tests that build a fresh `DataSource` per case are unaffected
102
+ * (the stale stamp is never observed). Tests that reuse a
103
+ * `DataSource` across `TransactionalModule.resetForTesting()` calls
104
+ * are not supported — destroy and recreate the DataSource between
105
+ * cases instead.
106
+ *
107
+ * @internal
108
+ */
109
+ function resetManagedRegistry() {
110
+ managedDataSources = new WeakSet();
111
+ }
112
+ //# sourceMappingURL=managed-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"managed-registry.js","sourceRoot":"","sources":["../../src/patching/managed-registry.ts"],"names":[],"mappings":";AAAA,oGAAoG;AACpG,8DAA8D;AAC9D,kEAAkE;AAClE,gEAAgE;AAChE,6DAA6D;AAC7D,gEAAgE;AAChE,4DAA4D;;AA+C5D,sCAOC;AAGD,8BAEC;AAOD,4DAGC;AAaD,wDAMC;AAkBD,oDAEC;AA1GD,qDAAgE;AAKhE,uCAAqD;AAErD;;;;;;;;;;;;;;;;GAgBG;AACH,IAAI,kBAAkB,GAAG,IAAI,OAAO,EAAc,CAAC;AAEnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,aAAa,CAAC,UAAsB,EAAE,IAAY;IAChE,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACnC,8DAA8D;IAC9D,kEAAkE;IAClE,8CAA8C;IAC9C,8DAA8D;IAC7D,UAAkB,CAAC,kCAAwB,CAAC,GAAG,IAAI,CAAC;AACvD,CAAC;AAED,wDAAwD;AACxD,SAAgB,SAAS,CAAC,UAAsB;IAC9C,OAAO,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,SAAgB,wBAAwB,CAAC,UAAsB;IAC7D,8DAA8D;IAC9D,OAAQ,UAAkB,CAAC,kCAAwB,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,sBAAsB,CAAC,cAAsB;IAC3D,MAAM,QAAQ,GAAG,yBAAkB,CAAC,gCAAgC,CAAC,cAAc,CAAC,CAAC;IACrF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAQ,QAAQ,CAAC,MAAmC,CAAC,aAAa,CAAC;AACrE,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,oBAAoB;IAClC,kBAAkB,GAAG,IAAI,OAAO,EAAc,CAAC;AACjD,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Install the prototype-level patches that make every `Repository`
3
+ * instance transactionally aware. Idempotent — calling more than
4
+ * once per process is a no-op.
5
+ *
6
+ * Two patches:
7
+ *
8
+ * 1. `Repository.prototype.manager` — replaced with a getter/setter
9
+ * pair. The setter intercepts the constructor's
10
+ * `this.manager = manager` assignment and stashes the value
11
+ * under {@link TYPEORM_ENTITY_MANAGER_NAME}; the getter consults
12
+ * `TransactionContext` and returns the active transactional
13
+ * `EntityManager` when one exists for this repository's
14
+ * dataSource (falling back to the stashed original otherwise).
15
+ *
16
+ * Since TypeORM's `Repository` methods are all of the shape
17
+ * `return this.manager.<method>(this.metadata.target, ...)`, this
18
+ * single getter patch transparently routes every public Repository
19
+ * operation (save, find, findOne, update, delete, query,
20
+ * createQueryBuilder, count, exists, sum, average, ...) through
21
+ * the transactional EntityManager when a transaction is active.
22
+ *
23
+ * 2. `Repository.prototype.extend` — wrapped so that a custom
24
+ * repository class produced via `repo.extend(...)` keeps the
25
+ * {@link TYPEORM_ENTITY_MANAGER_NAME} stamp on each constructed
26
+ * instance. Without this wrap, `.extend()` chains end up with
27
+ * `this.manager` undefined inside the patched getter (because the
28
+ * extended class re-runs the assignment that the patched setter
29
+ * consumes — but the setter only stashes; nothing else
30
+ * initializes the stash on the extended subclass).
31
+ *
32
+ * The mirror `EntityManager.prototype.getRepository` wrap lives
33
+ * in `entity-manager-patches.ts` so the symmetry is obvious from
34
+ * the file layout.
35
+ *
36
+ * **Design note — no revert path.** Reverting a prototype patch by
37
+ * deleting the descriptor would silently break every `Repository`
38
+ * instance constructed under the patched setter — those instances
39
+ * have no own-property `manager` (the setter only stashed the
40
+ * value); deleting the prototype descriptor leaves their
41
+ * `repo.manager` as `undefined`. Tests that need isolation should
42
+ * destroy and recreate the `DataSource` instead, which causes
43
+ * `TypeORM`'s `EntityManager`/`Repository` cache to be replaced
44
+ * along with the DataSource. The `WeakSet`-based managed-DataSource
45
+ * registry (see `managed-registry.ts`) provides the test-isolation
46
+ * lever that *is* safe to flip: after `resetManagedRegistry()`,
47
+ * cached repositories from the prior test fall through the
48
+ * patched getter to their captured original manager (autocommit) —
49
+ * never a broken `undefined`.
50
+ */
51
+ export declare function applyRepositoryPatches(): void;
52
+ /**
53
+ * Predicate exported for unit tests. @internal
54
+ */
55
+ export declare function areRepositoryPatchesApplied(): boolean;
56
+ //# sourceMappingURL=repository-patches.d.ts.map
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/unbound-method */
3
+ // The patching machinery stamps and reads hidden symbol-keyed
4
+ // properties on TypeORM `Repository` instances. These properties
5
+ // live outside TypeORM's public types by design, so unsafe-access
6
+ // and unbound-method lint rules fire on every access. File-level
7
+ // disable keeps the patch code readable; the runtime contract is
8
+ // documented in the JSDoc above each patch.
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.applyRepositoryPatches = applyRepositoryPatches;
11
+ exports.areRepositoryPatchesApplied = areRepositoryPatchesApplied;
12
+ const typeorm_1 = require("typeorm");
13
+ const managed_registry_1 = require("./managed-registry");
14
+ const symbols_1 = require("./symbols");
15
+ /**
16
+ * Tracks whether the prototype patch has been installed on
17
+ * `Repository.prototype`. Once installed, the patch lives for the
18
+ * remainder of the process — there is no `revert` story (see
19
+ * design note below). The only re-entry path is calling
20
+ * `applyRepositoryPatches` again after install, which is a no-op.
21
+ */
22
+ let installed = false;
23
+ /**
24
+ * Captured original of `Repository.prototype.extend` for the wrapper
25
+ * to delegate into. Captured once at install-time; never restored.
26
+ */
27
+ let originalExtend;
28
+ /**
29
+ * Install the prototype-level patches that make every `Repository`
30
+ * instance transactionally aware. Idempotent — calling more than
31
+ * once per process is a no-op.
32
+ *
33
+ * Two patches:
34
+ *
35
+ * 1. `Repository.prototype.manager` — replaced with a getter/setter
36
+ * pair. The setter intercepts the constructor's
37
+ * `this.manager = manager` assignment and stashes the value
38
+ * under {@link TYPEORM_ENTITY_MANAGER_NAME}; the getter consults
39
+ * `TransactionContext` and returns the active transactional
40
+ * `EntityManager` when one exists for this repository's
41
+ * dataSource (falling back to the stashed original otherwise).
42
+ *
43
+ * Since TypeORM's `Repository` methods are all of the shape
44
+ * `return this.manager.<method>(this.metadata.target, ...)`, this
45
+ * single getter patch transparently routes every public Repository
46
+ * operation (save, find, findOne, update, delete, query,
47
+ * createQueryBuilder, count, exists, sum, average, ...) through
48
+ * the transactional EntityManager when a transaction is active.
49
+ *
50
+ * 2. `Repository.prototype.extend` — wrapped so that a custom
51
+ * repository class produced via `repo.extend(...)` keeps the
52
+ * {@link TYPEORM_ENTITY_MANAGER_NAME} stamp on each constructed
53
+ * instance. Without this wrap, `.extend()` chains end up with
54
+ * `this.manager` undefined inside the patched getter (because the
55
+ * extended class re-runs the assignment that the patched setter
56
+ * consumes — but the setter only stashes; nothing else
57
+ * initializes the stash on the extended subclass).
58
+ *
59
+ * The mirror `EntityManager.prototype.getRepository` wrap lives
60
+ * in `entity-manager-patches.ts` so the symmetry is obvious from
61
+ * the file layout.
62
+ *
63
+ * **Design note — no revert path.** Reverting a prototype patch by
64
+ * deleting the descriptor would silently break every `Repository`
65
+ * instance constructed under the patched setter — those instances
66
+ * have no own-property `manager` (the setter only stashed the
67
+ * value); deleting the prototype descriptor leaves their
68
+ * `repo.manager` as `undefined`. Tests that need isolation should
69
+ * destroy and recreate the `DataSource` instead, which causes
70
+ * `TypeORM`'s `EntityManager`/`Repository` cache to be replaced
71
+ * along with the DataSource. The `WeakSet`-based managed-DataSource
72
+ * registry (see `managed-registry.ts`) provides the test-isolation
73
+ * lever that *is* safe to flip: after `resetManagedRegistry()`,
74
+ * cached repositories from the prior test fall through the
75
+ * patched getter to their captured original manager (autocommit) —
76
+ * never a broken `undefined`.
77
+ */
78
+ function applyRepositoryPatches() {
79
+ if (installed) {
80
+ return;
81
+ }
82
+ // (1) Repository.prototype.manager — getter/setter pair.
83
+ Object.defineProperty(typeorm_1.Repository.prototype, 'manager', {
84
+ configurable: true,
85
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
+ get() {
87
+ // The original (non-active) manager is stashed under our
88
+ // symbol by either: (a) the patched `set` below, when
89
+ // TypeORM's Repository constructor runs `this.manager = manager`,
90
+ // or (b) the `EntityManager.prototype.getRepository` wrapper
91
+ // when it stamps newly-resolved repositories.
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ const original = this[symbols_1.TYPEORM_ENTITY_MANAGER_NAME];
94
+ // Defensive: a Repository ending up here without ever having
95
+ // had its setter called (shouldn't happen in practice) yields
96
+ // `undefined`, mirroring the pre-patch behaviour for an
97
+ // unconstructed/zombie instance.
98
+ if (original === undefined) {
99
+ return undefined;
100
+ }
101
+ const ds = original.connection;
102
+ if (!(0, managed_registry_1.isManaged)(ds)) {
103
+ return original;
104
+ }
105
+ const dsName = (0, managed_registry_1.getManagedDataSourceName)(ds);
106
+ if (dsName === undefined) {
107
+ return original;
108
+ }
109
+ const active = (0, managed_registry_1.getActiveEntityManager)(dsName);
110
+ return active ?? original;
111
+ },
112
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
113
+ set(manager) {
114
+ // Constructor's `this.manager = manager` reaches us here.
115
+ // Stash under the hidden symbol so the getter has a fallback.
116
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
+ this[symbols_1.TYPEORM_ENTITY_MANAGER_NAME] = manager;
118
+ },
119
+ });
120
+ // (2) Repository.prototype.extend — wrap to preserve the stash on
121
+ // extended instances. The generic shape of the original
122
+ // `extend` resists clean typing on the wrapper; cast through
123
+ // `any` to keep the wrap a single statement (the runtime
124
+ // contract is unchanged).
125
+ originalExtend = typeorm_1.Repository.prototype.extend;
126
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
+ typeorm_1.Repository.prototype.extend = function patchedExtend(
128
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
129
+ customs) {
130
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
131
+ const result = originalExtend.call(this, customs);
132
+ // The freshly-extended Repository was constructed via the
133
+ // patched setter, so the stash is already in place. Defensive
134
+ // copy in case TypeORM's internal extension shape changes.
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
+ if (result[symbols_1.TYPEORM_ENTITY_MANAGER_NAME] === undefined) {
137
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
138
+ result[symbols_1.TYPEORM_ENTITY_MANAGER_NAME] = this[symbols_1.TYPEORM_ENTITY_MANAGER_NAME];
139
+ }
140
+ return result;
141
+ };
142
+ installed = true;
143
+ }
144
+ /**
145
+ * Predicate exported for unit tests. @internal
146
+ */
147
+ function areRepositoryPatchesApplied() {
148
+ return installed;
149
+ }
150
+ //# sourceMappingURL=repository-patches.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository-patches.js","sourceRoot":"","sources":["../../src/patching/repository-patches.ts"],"names":[],"mappings":";AAAA,2IAA2I;AAC3I,8DAA8D;AAC9D,iEAAiE;AACjE,kEAAkE;AAClE,iEAAiE;AACjE,iEAAiE;AACjE,4CAA4C;;AAyE5C,wDA2EC;AAKD,kEAEC;AAzJD,qCAAqC;AAGrC,yDAAiG;AACjG,uCAAwD;AAExD;;;;;;GAMG;AACH,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB;;;GAGG;AACH,IAAI,cAA8D,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,SAAgB,sBAAsB;IACpC,IAAI,SAAS,EAAE,CAAC;QACd,OAAO;IACT,CAAC;IAED,yDAAyD;IACzD,MAAM,CAAC,cAAc,CAAC,oBAAU,CAAC,SAAS,EAAE,SAAS,EAAE;QACrD,YAAY,EAAE,IAAI;QAClB,8DAA8D;QAC9D,GAAG;YACD,yDAAyD;YACzD,sDAAsD;YACtD,kEAAkE;YAClE,6DAA6D;YAC7D,8CAA8C;YAC9C,8DAA8D;YAC9D,MAAM,QAAQ,GAA+B,IAAY,CAAC,qCAA2B,CAAC,CAAC;YAEvF,6DAA6D;YAC7D,8DAA8D;YAC9D,wDAAwD;YACxD,iCAAiC;YACjC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,OAAO,SAAqC,CAAC;YAC/C,CAAC;YAED,MAAM,EAAE,GAAG,QAAQ,CAAC,UAAU,CAAC;YAC/B,IAAI,CAAC,IAAA,4BAAS,EAAC,EAAE,CAAC,EAAE,CAAC;gBACnB,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,MAAM,MAAM,GAAG,IAAA,2CAAwB,EAAC,EAAE,CAAC,CAAC;YAC5C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,MAAM,MAAM,GAAG,IAAA,yCAAsB,EAAC,MAAM,CAAC,CAAC;YAC9C,OAAO,MAAM,IAAI,QAAQ,CAAC;QAC5B,CAAC;QACD,8DAA8D;QAC9D,GAAG,CAAwB,OAAsB;YAC/C,0DAA0D;YAC1D,8DAA8D;YAC9D,8DAA8D;YAC7D,IAAY,CAAC,qCAA2B,CAAC,GAAG,OAAO,CAAC;QACvD,CAAC;KACF,CAAC,CAAC;IAEH,kEAAkE;IAClE,wDAAwD;IACxD,6DAA6D;IAC7D,yDAAyD;IACzD,0BAA0B;IAC1B,cAAc,GAAG,oBAAU,CAAC,SAAS,CAAC,MAAM,CAAC;IAC7C,8DAA8D;IAC7D,oBAAU,CAAC,SAAiB,CAAC,MAAM,GAAG,SAAS,aAAa;IAG3D,8DAA8D;IAC9D,OAAY;QAEZ,oEAAoE;QACpE,MAAM,MAAM,GAAG,cAAe,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACnD,0DAA0D;QAC1D,8DAA8D;QAC9D,2DAA2D;QAC3D,8DAA8D;QAC9D,IAAK,MAAc,CAAC,qCAA2B,CAAC,KAAK,SAAS,EAAE,CAAC;YAC/D,8DAA8D;YAC7D,MAAc,CAAC,qCAA2B,CAAC,GAAI,IAAY,CAAC,qCAA2B,CAAC,CAAC;QAC5F,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,SAAgB,2BAA2B;IACzC,OAAO,SAAS,CAAC;AACnB,CAAC"}