@sasscore/database 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +281 -0
  2. package/dist/connection/index.d.ts +7 -0
  3. package/dist/connection/index.d.ts.map +1 -0
  4. package/dist/connection/index.js +7 -0
  5. package/dist/connection/index.js.map +1 -0
  6. package/dist/connection/infrastructure/connection.module.d.ts +18 -0
  7. package/dist/connection/infrastructure/connection.module.d.ts.map +1 -0
  8. package/dist/connection/infrastructure/connection.module.js +75 -0
  9. package/dist/connection/infrastructure/connection.module.js.map +1 -0
  10. package/dist/connection/infrastructure/sequelize.service.d.ts +16 -0
  11. package/dist/connection/infrastructure/sequelize.service.d.ts.map +1 -0
  12. package/dist/connection/infrastructure/sequelize.service.js +56 -0
  13. package/dist/connection/infrastructure/sequelize.service.js.map +1 -0
  14. package/dist/database.module.d.ts +25 -0
  15. package/dist/database.module.d.ts.map +1 -0
  16. package/dist/database.module.js +71 -0
  17. package/dist/database.module.js.map +1 -0
  18. package/dist/health/database-health.service.d.ts +12 -0
  19. package/dist/health/database-health.service.d.ts.map +1 -0
  20. package/dist/health/database-health.service.js +48 -0
  21. package/dist/health/database-health.service.js.map +1 -0
  22. package/dist/health/database-health.types.d.ts +8 -0
  23. package/dist/health/database-health.types.d.ts.map +1 -0
  24. package/dist/health/database-health.types.js +2 -0
  25. package/dist/health/database-health.types.js.map +1 -0
  26. package/dist/health/health.module.d.ts +3 -0
  27. package/dist/health/health.module.d.ts.map +1 -0
  28. package/dist/health/health.module.js +20 -0
  29. package/dist/health/health.module.js.map +1 -0
  30. package/dist/health/index.d.ts +12 -0
  31. package/dist/health/index.d.ts.map +1 -0
  32. package/dist/health/index.js +9 -0
  33. package/dist/health/index.js.map +1 -0
  34. package/dist/health/migration-health.service.d.ts +14 -0
  35. package/dist/health/migration-health.service.d.ts.map +1 -0
  36. package/dist/health/migration-health.service.js +43 -0
  37. package/dist/health/migration-health.service.js.map +1 -0
  38. package/dist/health/migration-health.types.d.ts +9 -0
  39. package/dist/health/migration-health.types.d.ts.map +1 -0
  40. package/dist/health/migration-health.types.js +2 -0
  41. package/dist/health/migration-health.types.js.map +1 -0
  42. package/dist/health/outbox-health.service.d.ts +14 -0
  43. package/dist/health/outbox-health.service.d.ts.map +1 -0
  44. package/dist/health/outbox-health.service.js +79 -0
  45. package/dist/health/outbox-health.service.js.map +1 -0
  46. package/dist/health/outbox-health.types.d.ts +15 -0
  47. package/dist/health/outbox-health.types.d.ts.map +1 -0
  48. package/dist/health/outbox-health.types.js +2 -0
  49. package/dist/health/outbox-health.types.js.map +1 -0
  50. package/dist/index.d.ts +26 -0
  51. package/dist/index.d.ts.map +1 -0
  52. package/dist/index.js +24 -0
  53. package/dist/index.js.map +1 -0
  54. package/dist/migrations/20260826173000-create-outbox-events.d.ts +5 -0
  55. package/dist/migrations/20260826173000-create-outbox-events.d.ts.map +1 -0
  56. package/dist/migrations/20260826173000-create-outbox-events.js +102 -0
  57. package/dist/migrations/20260826173000-create-outbox-events.js.map +1 -0
  58. package/dist/migrations/create-migration-registry.d.ts +9 -0
  59. package/dist/migrations/create-migration-registry.d.ts.map +1 -0
  60. package/dist/migrations/create-migration-registry.js +37 -0
  61. package/dist/migrations/create-migration-registry.js.map +1 -0
  62. package/dist/migrations/index.d.ts +7 -0
  63. package/dist/migrations/index.d.ts.map +1 -0
  64. package/dist/migrations/index.js +6 -0
  65. package/dist/migrations/index.js.map +1 -0
  66. package/dist/migrations/migration-registry.d.ts +4 -0
  67. package/dist/migrations/migration-registry.d.ts.map +1 -0
  68. package/dist/migrations/migration-registry.js +4 -0
  69. package/dist/migrations/migration-registry.js.map +1 -0
  70. package/dist/migrations/migration-runner.d.ts +27 -0
  71. package/dist/migrations/migration-runner.d.ts.map +1 -0
  72. package/dist/migrations/migration-runner.js +135 -0
  73. package/dist/migrations/migration-runner.js.map +1 -0
  74. package/dist/migrations/migration.helpers.d.ts +8 -0
  75. package/dist/migrations/migration.helpers.d.ts.map +1 -0
  76. package/dist/migrations/migration.helpers.js +9 -0
  77. package/dist/migrations/migration.helpers.js.map +1 -0
  78. package/dist/migrations/migration.types.d.ts +26 -0
  79. package/dist/migrations/migration.types.d.ts.map +1 -0
  80. package/dist/migrations/migration.types.js +2 -0
  81. package/dist/migrations/migration.types.js.map +1 -0
  82. package/dist/models/base.model.d.ts +17 -0
  83. package/dist/models/base.model.d.ts.map +1 -0
  84. package/dist/models/base.model.js +56 -0
  85. package/dist/models/base.model.js.map +1 -0
  86. package/dist/models/index.d.ts +2 -0
  87. package/dist/models/index.d.ts.map +1 -0
  88. package/dist/models/index.js +2 -0
  89. package/dist/models/index.js.map +1 -0
  90. package/dist/outbox/application/outbox.processor.d.ts +20 -0
  91. package/dist/outbox/application/outbox.processor.d.ts.map +1 -0
  92. package/dist/outbox/application/outbox.processor.js +66 -0
  93. package/dist/outbox/application/outbox.processor.js.map +1 -0
  94. package/dist/outbox/application/outbox.publisher.d.ts +17 -0
  95. package/dist/outbox/application/outbox.publisher.d.ts.map +1 -0
  96. package/dist/outbox/application/outbox.publisher.js +33 -0
  97. package/dist/outbox/application/outbox.publisher.js.map +1 -0
  98. package/dist/outbox/application/ports/event-dispatcher.port.d.ts +7 -0
  99. package/dist/outbox/application/ports/event-dispatcher.port.d.ts.map +1 -0
  100. package/dist/outbox/application/ports/event-dispatcher.port.js +2 -0
  101. package/dist/outbox/application/ports/event-dispatcher.port.js.map +1 -0
  102. package/dist/outbox/domain/entities/domain-event.entity.d.ts +11 -0
  103. package/dist/outbox/domain/entities/domain-event.entity.d.ts.map +1 -0
  104. package/dist/outbox/domain/entities/domain-event.entity.js +15 -0
  105. package/dist/outbox/domain/entities/domain-event.entity.js.map +1 -0
  106. package/dist/outbox/domain/entities/outbox-event.types.d.ts +21 -0
  107. package/dist/outbox/domain/entities/outbox-event.types.d.ts.map +1 -0
  108. package/dist/outbox/domain/entities/outbox-event.types.js +2 -0
  109. package/dist/outbox/domain/entities/outbox-event.types.js.map +1 -0
  110. package/dist/outbox/domain/index.d.ts +5 -0
  111. package/dist/outbox/domain/index.d.ts.map +1 -0
  112. package/dist/outbox/domain/index.js +3 -0
  113. package/dist/outbox/domain/index.js.map +1 -0
  114. package/dist/outbox/domain/repositories/outbox.repository.interface.d.ts +13 -0
  115. package/dist/outbox/domain/repositories/outbox.repository.interface.d.ts.map +1 -0
  116. package/dist/outbox/domain/repositories/outbox.repository.interface.js +2 -0
  117. package/dist/outbox/domain/repositories/outbox.repository.interface.js.map +1 -0
  118. package/dist/outbox/domain/value-objects/event-types.d.ts +19 -0
  119. package/dist/outbox/domain/value-objects/event-types.d.ts.map +1 -0
  120. package/dist/outbox/domain/value-objects/event-types.js +18 -0
  121. package/dist/outbox/domain/value-objects/event-types.js.map +1 -0
  122. package/dist/outbox/event-dispatcher.d.ts +3 -0
  123. package/dist/outbox/event-dispatcher.d.ts.map +1 -0
  124. package/dist/outbox/event-dispatcher.js +3 -0
  125. package/dist/outbox/event-dispatcher.js.map +1 -0
  126. package/dist/outbox/event.entity.d.ts +4 -0
  127. package/dist/outbox/event.entity.d.ts.map +1 -0
  128. package/dist/outbox/event.entity.js +3 -0
  129. package/dist/outbox/event.entity.js.map +1 -0
  130. package/dist/outbox/index.d.ts +19 -0
  131. package/dist/outbox/index.d.ts.map +1 -0
  132. package/dist/outbox/index.js +17 -0
  133. package/dist/outbox/index.js.map +1 -0
  134. package/dist/outbox/infrastructure/dispatch/event-dispatcher.d.ts +21 -0
  135. package/dist/outbox/infrastructure/dispatch/event-dispatcher.d.ts.map +1 -0
  136. package/dist/outbox/infrastructure/dispatch/event-dispatcher.js +55 -0
  137. package/dist/outbox/infrastructure/dispatch/event-dispatcher.js.map +1 -0
  138. package/dist/outbox/infrastructure/dispatch/flat-handler.registry.d.ts +13 -0
  139. package/dist/outbox/infrastructure/dispatch/flat-handler.registry.d.ts.map +1 -0
  140. package/dist/outbox/infrastructure/dispatch/flat-handler.registry.js +46 -0
  141. package/dist/outbox/infrastructure/dispatch/flat-handler.registry.js.map +1 -0
  142. package/dist/outbox/infrastructure/outbox.module.d.ts +13 -0
  143. package/dist/outbox/infrastructure/outbox.module.d.ts.map +1 -0
  144. package/dist/outbox/infrastructure/outbox.module.js +53 -0
  145. package/dist/outbox/infrastructure/outbox.module.js.map +1 -0
  146. package/dist/outbox/infrastructure/persistence/outbox-event.model.d.ts +106 -0
  147. package/dist/outbox/infrastructure/persistence/outbox-event.model.d.ts.map +1 -0
  148. package/dist/outbox/infrastructure/persistence/outbox-event.model.js +186 -0
  149. package/dist/outbox/infrastructure/persistence/outbox-event.model.js.map +1 -0
  150. package/dist/outbox/infrastructure/persistence/outbox-persistence.module.d.ts +9 -0
  151. package/dist/outbox/infrastructure/persistence/outbox-persistence.module.d.ts.map +1 -0
  152. package/dist/outbox/infrastructure/persistence/outbox-persistence.module.js +25 -0
  153. package/dist/outbox/infrastructure/persistence/outbox-persistence.module.js.map +1 -0
  154. package/dist/outbox/infrastructure/persistence/sequelize-outbox.repository.d.ts +24 -0
  155. package/dist/outbox/infrastructure/persistence/sequelize-outbox.repository.d.ts.map +1 -0
  156. package/dist/outbox/infrastructure/persistence/sequelize-outbox.repository.js +191 -0
  157. package/dist/outbox/infrastructure/persistence/sequelize-outbox.repository.js.map +1 -0
  158. package/dist/outbox/outbox-flat-handler.registry.d.ts +4 -0
  159. package/dist/outbox/outbox-flat-handler.registry.d.ts.map +1 -0
  160. package/dist/outbox/outbox-flat-handler.registry.js +4 -0
  161. package/dist/outbox/outbox-flat-handler.registry.js.map +1 -0
  162. package/dist/outbox/outbox.constants.d.ts +3 -0
  163. package/dist/outbox/outbox.constants.d.ts.map +1 -0
  164. package/dist/outbox/outbox.constants.js +3 -0
  165. package/dist/outbox/outbox.constants.js.map +1 -0
  166. package/dist/outbox/outbox.module.d.ts +3 -0
  167. package/dist/outbox/outbox.module.d.ts.map +1 -0
  168. package/dist/outbox/outbox.module.js +3 -0
  169. package/dist/outbox/outbox.module.js.map +1 -0
  170. package/dist/outbox/outbox.processor.d.ts +4 -0
  171. package/dist/outbox/outbox.processor.d.ts.map +1 -0
  172. package/dist/outbox/outbox.processor.js +4 -0
  173. package/dist/outbox/outbox.processor.js.map +1 -0
  174. package/dist/outbox/outbox.publisher.d.ts +3 -0
  175. package/dist/outbox/outbox.publisher.d.ts.map +1 -0
  176. package/dist/outbox/outbox.publisher.js +3 -0
  177. package/dist/outbox/outbox.publisher.js.map +1 -0
  178. package/dist/outbox/outbox.repository.d.ts +3 -0
  179. package/dist/outbox/outbox.repository.d.ts.map +1 -0
  180. package/dist/outbox/outbox.repository.js +3 -0
  181. package/dist/outbox/outbox.repository.js.map +1 -0
  182. package/dist/providers/database.providers.d.ts +4 -0
  183. package/dist/providers/database.providers.d.ts.map +1 -0
  184. package/dist/providers/database.providers.js +25 -0
  185. package/dist/providers/database.providers.js.map +1 -0
  186. package/dist/providers/database.tokens.d.ts +43 -0
  187. package/dist/providers/database.tokens.d.ts.map +1 -0
  188. package/dist/providers/database.tokens.js +43 -0
  189. package/dist/providers/database.tokens.js.map +1 -0
  190. package/dist/providers/index.d.ts +3 -0
  191. package/dist/providers/index.d.ts.map +1 -0
  192. package/dist/providers/index.js +3 -0
  193. package/dist/providers/index.js.map +1 -0
  194. package/dist/repositories/index.d.ts +5 -0
  195. package/dist/repositories/index.d.ts.map +1 -0
  196. package/dist/repositories/index.js +4 -0
  197. package/dist/repositories/index.js.map +1 -0
  198. package/dist/repositories/main.repository.d.ts +163 -0
  199. package/dist/repositories/main.repository.d.ts.map +1 -0
  200. package/dist/repositories/main.repository.js +1120 -0
  201. package/dist/repositories/main.repository.js.map +1 -0
  202. package/dist/repositories/repository-execution-mode.enum.d.ts +11 -0
  203. package/dist/repositories/repository-execution-mode.enum.d.ts.map +1 -0
  204. package/dist/repositories/repository-execution-mode.enum.js +12 -0
  205. package/dist/repositories/repository-execution-mode.enum.js.map +1 -0
  206. package/dist/repositories/repository.types.d.ts +65 -0
  207. package/dist/repositories/repository.types.d.ts.map +1 -0
  208. package/dist/repositories/repository.types.js +2 -0
  209. package/dist/repositories/repository.types.js.map +1 -0
  210. package/dist/repositories/tenant-scope-mode.enum.d.ts +13 -0
  211. package/dist/repositories/tenant-scope-mode.enum.d.ts.map +1 -0
  212. package/dist/repositories/tenant-scope-mode.enum.js +14 -0
  213. package/dist/repositories/tenant-scope-mode.enum.js.map +1 -0
  214. package/dist/tenancy/domain/ports/tenant-context.port.d.ts +22 -0
  215. package/dist/tenancy/domain/ports/tenant-context.port.d.ts.map +1 -0
  216. package/dist/tenancy/domain/ports/tenant-context.port.js +2 -0
  217. package/dist/tenancy/domain/ports/tenant-context.port.js.map +1 -0
  218. package/dist/tenancy/index.d.ts +8 -0
  219. package/dist/tenancy/index.d.ts.map +1 -0
  220. package/dist/tenancy/index.js +7 -0
  221. package/dist/tenancy/index.js.map +1 -0
  222. package/dist/tenancy/infrastructure/cls-tenant-context.adapter.d.ts +26 -0
  223. package/dist/tenancy/infrastructure/cls-tenant-context.adapter.d.ts.map +1 -0
  224. package/dist/tenancy/infrastructure/cls-tenant-context.adapter.js +73 -0
  225. package/dist/tenancy/infrastructure/cls-tenant-context.adapter.js.map +1 -0
  226. package/dist/tenancy/infrastructure/tenant-context.module.d.ts +7 -0
  227. package/dist/tenancy/infrastructure/tenant-context.module.d.ts.map +1 -0
  228. package/dist/tenancy/infrastructure/tenant-context.module.js +32 -0
  229. package/dist/tenancy/infrastructure/tenant-context.module.js.map +1 -0
  230. package/dist/tokens.d.ts +6 -0
  231. package/dist/tokens.d.ts.map +1 -0
  232. package/dist/tokens.js +6 -0
  233. package/dist/tokens.js.map +1 -0
  234. package/dist/transaction-manager/application/transaction-manager.service.d.ts +12 -0
  235. package/dist/transaction-manager/application/transaction-manager.service.d.ts.map +1 -0
  236. package/dist/transaction-manager/application/transaction-manager.service.js +29 -0
  237. package/dist/transaction-manager/application/transaction-manager.service.js.map +1 -0
  238. package/dist/transaction-manager/domain/ports/transaction-manager.port.d.ts +9 -0
  239. package/dist/transaction-manager/domain/ports/transaction-manager.port.d.ts.map +1 -0
  240. package/dist/transaction-manager/domain/ports/transaction-manager.port.js +2 -0
  241. package/dist/transaction-manager/domain/ports/transaction-manager.port.js.map +1 -0
  242. package/dist/transaction-manager/index.d.ts +7 -0
  243. package/dist/transaction-manager/index.d.ts.map +1 -0
  244. package/dist/transaction-manager/index.js +6 -0
  245. package/dist/transaction-manager/index.js.map +1 -0
  246. package/dist/types/database-module-options.d.ts +12 -0
  247. package/dist/types/database-module-options.d.ts.map +1 -0
  248. package/dist/types/database-module-options.js +2 -0
  249. package/dist/types/database-module-options.js.map +1 -0
  250. package/dist/types/index.d.ts +2 -0
  251. package/dist/types/index.d.ts.map +1 -0
  252. package/dist/types/index.js +2 -0
  253. package/dist/types/index.js.map +1 -0
  254. package/package.json +69 -0
@@ -0,0 +1,1120 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ // @ts-nocheck — legacy MainRepository port; tighten types in a follow-up.
11
+ import { Injectable } from '@nestjs/common';
12
+ import { Op } from 'sequelize';
13
+ import { TenantScopeMode } from './tenant-scope-mode.enum.js';
14
+ import { RepositoryExecutionMode } from './repository-execution-mode.enum.js';
15
+ let MainRepository = class MainRepository {
16
+ modelProvider;
17
+ _cachedModel = null;
18
+ _modelLoading = null;
19
+ /** Execution mode: TENANT (default) requires CLS; SYSTEM bypasses CLS and tenant scope. */
20
+ executionMode = RepositoryExecutionMode.TENANT;
21
+ ctx;
22
+ _logger;
23
+ /**
24
+ * Repository-level tenant policy (default-safe).
25
+ * Override in subclasses: e.g. {@link TenantScopeMode.DISABLED} for junction / auth lookups.
26
+ */
27
+ scopeMode = TenantScopeMode.REQUIRED;
28
+ constructor(modelProvider) {
29
+ this.modelProvider = modelProvider;
30
+ }
31
+ /**
32
+ * Builds a new repository instance sharing the same model provider, with {@link IRequestContext}
33
+ * set for this request (see {@link IRepository.createInstance}).
34
+ */
35
+ async createInstance(ctx) {
36
+ const Ctor = this.constructor;
37
+ const instance = new Ctor(this.modelProvider);
38
+ instance.setCtx(ctx);
39
+ (this._logger) && instance.setLogger(this._logger);
40
+ if (!this._cachedModel && !this._modelLoading)
41
+ await this.getModel();
42
+ if (this._cachedModel) {
43
+ instance._cachedModel = this._cachedModel;
44
+ instance._modelLoading = null;
45
+ }
46
+ return instance;
47
+ }
48
+ /**
49
+ * Builds a new repository instance in SYSTEM mode.
50
+ * No CLS required, no tenant scope applied — safe for admin/system operations only.
51
+ * Use instead of `createInstance({ userId: 0, companyId: 0 })`.
52
+ */
53
+ async createSystemInstance() {
54
+ const Ctor = this.constructor;
55
+ const instance = new Ctor(this.modelProvider);
56
+ instance.executionMode = RepositoryExecutionMode.SYSTEM;
57
+ instance._logger = this._logger;
58
+ instance._cachedModel = this._cachedModel;
59
+ return instance;
60
+ }
61
+ // @deprecated
62
+ setCtx(ctx) {
63
+ this.ctx = ctx;
64
+ return this;
65
+ }
66
+ /** Normalized request context; undefined if not set yet. */
67
+ getCtx() {
68
+ return this.ctx;
69
+ }
70
+ setLogger(logger) {
71
+ this._logger = logger;
72
+ }
73
+ get logger() {
74
+ return this._logger || console;
75
+ }
76
+ /**
77
+ * Tenant scope from {@link IRequestContext} (set via {@link setRequestContext} before DB calls).
78
+ */
79
+ getScope() {
80
+ const ctx = this.getCtx();
81
+ const companyId = ctx?.companyId;
82
+ if (companyId == null || !Number.isFinite(Number(companyId))) {
83
+ throw new Error('[SECURITY] Tenant scope missing: companyId not set on IRequestContext (call setRequestContext before repository use)');
84
+ }
85
+ // const rawStockOwnerId = ctx?.stockOwnerId;
86
+ // const stockOwnerId =
87
+ // rawStockOwnerId != null && Number.isFinite(Number(rawStockOwnerId))
88
+ // ? Number(rawStockOwnerId)
89
+ // : undefined;
90
+ return {
91
+ companyId: Number(companyId),
92
+ // ...(stockOwnerId !== undefined ? { stockOwnerId } : {}),
93
+ };
94
+ }
95
+ /**
96
+ * Effective tenant keys for this repository: {@link scopeMode} → CLS → {@link getScope} →
97
+ * strip keys absent on the Sequelize model → {@link assertRowLevelCompanyScope}.
98
+ *
99
+ * Requires `_cachedModel` to be set (call `getModel()` first). If the model is not loaded yet,
100
+ * returns the context-derived scope without model intersection (conservative).
101
+ */
102
+ resolveScopedTenantFields() {
103
+ if (this.executionMode === RepositoryExecutionMode.SYSTEM) {
104
+ this.logger.warn(`[SYSTEM QUERY] ${this.constructor.name}`);
105
+ return {};
106
+ }
107
+ if (this.scopeMode === TenantScopeMode.DISABLED) {
108
+ return {};
109
+ }
110
+ const companyIdRaw = this.ctx?.companyId;
111
+ const hasCompany = companyIdRaw != null && Number.isFinite(Number(companyIdRaw));
112
+ if (this.scopeMode === TenantScopeMode.OPTIONAL && !hasCompany) {
113
+ return {};
114
+ }
115
+ if (this.scopeMode === TenantScopeMode.REQUIRED && !hasCompany) {
116
+ throw new Error('[SECURITY] Tenant scope missing: companyId not set on IRequestContext (setRequestContext)');
117
+ }
118
+ let scope = { ...this.getScope() };
119
+ const model = this._cachedModel;
120
+ if (!model?.rawAttributes) {
121
+ return scope;
122
+ }
123
+ const attrs = model.rawAttributes;
124
+ if (!Object.prototype.hasOwnProperty.call(attrs, 'accountId')) {
125
+ const { accountId: _omitAccount, ...rest } = scope;
126
+ scope = rest;
127
+ }
128
+ if (!Object.prototype.hasOwnProperty.call(attrs, 'companyId')) {
129
+ const { companyId: _omitCompany, ...rest } = scope;
130
+ scope = rest;
131
+ }
132
+ this.assertRowLevelCompanyScope(scope);
133
+ return scope;
134
+ }
135
+ /**
136
+ * Rows partitioned by `company_id` must never run with an empty tenant scope (would match all
137
+ * companies like `SELECT count(*) FROM categories`). Junction / company-root models omit
138
+ * `companyId` in Sequelize attributes — they skip this check.
139
+ */
140
+ assertRowLevelCompanyScope(scope) {
141
+ const model = this._cachedModel;
142
+ if (!model?.rawAttributes) {
143
+ return;
144
+ }
145
+ if (!Object.prototype.hasOwnProperty.call(model.rawAttributes, 'companyId')) {
146
+ return;
147
+ }
148
+ const cid = scope.companyId;
149
+ if (cid == null || !Number.isFinite(Number(cid))) {
150
+ throw new Error(`[SECURITY] Model ${String(model.name)} is company-scoped but resolved tenant scope has no companyId (CLS / getScope).`);
151
+ }
152
+ }
153
+ /**
154
+ * Sequelize rejects keys with `undefined` (e.g. account_id = undefined). Strip them.
155
+ * Also cleans plain-object branches inside Op.or / Op.and arrays.
156
+ */
157
+ stripUndefinedWhere(where) {
158
+ if (where == null || typeof where !== 'object') {
159
+ return where;
160
+ }
161
+ const omitUndefinedShallow = (obj) => {
162
+ if (obj == null || typeof obj !== 'object' || Array.isArray(obj)) {
163
+ return obj;
164
+ }
165
+ const out = { ...obj };
166
+ for (const key of Object.keys(out)) {
167
+ if (out[key] === undefined) {
168
+ delete out[key];
169
+ }
170
+ }
171
+ return out;
172
+ };
173
+ const result = omitUndefinedShallow(where);
174
+ for (const sym of [Op.or, Op.and]) {
175
+ const group = result[sym];
176
+ if (!Array.isArray(group))
177
+ continue;
178
+ result[sym] = group.map((entry) => {
179
+ if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
180
+ return omitUndefinedShallow(entry);
181
+ }
182
+ return entry;
183
+ });
184
+ }
185
+ return result;
186
+ }
187
+ /**
188
+ * Merge CLS tenant scope into a write payload (`create` / `defaults` / `bulkCreate`).
189
+ * `companyId` from scope still applies last (isolation). If the row sets explicit `accountId`
190
+ * (e.g. a channel row for a specific account), do not let CLS "current account" overwrite it.
191
+ */
192
+ mergeRowWithTenantScope(row, _scope) {
193
+ // TEMP: CLS tenant scope merge disabled — uncomment block below to restore isolation
194
+ // const s: Record<string, unknown> = { ...(_scope as Record<string, unknown>) };
195
+ // if (row.accountId != null && row.accountId !== undefined) {
196
+ // delete s.accountId;
197
+ // }
198
+ // return this.stripUndefinedWhere({ ...row, ...s });
199
+ return this.stripUndefinedWhere({ ...row });
200
+ }
201
+ /**
202
+ * Merge caller `where` with tenant scope. Policy: {@link scopeMode}; keys: CLS via
203
+ * {@link resolveScopedTenantFields} (never ad-hoc per method).
204
+ *
205
+ * TEMP: scope merge disabled — uncomment to restore CLS isolation on reads/writes.
206
+ */
207
+ applyScopeToWhere(where = {}) {
208
+ const scope = { ...this.resolveScopedTenantFields() };
209
+ return this.stripUndefinedWhere({
210
+ ...where,
211
+ ...scope,
212
+ });
213
+ }
214
+ // Реалізація абстрактного геттера для моделі
215
+ // Оскільки ARepository очікує синхронний геттер, але ми маємо асинхронний modelProvider,
216
+ // ми повертаємо кешовану модель або заглушку
217
+ get model() {
218
+ return this._cachedModel || null;
219
+ }
220
+ // Асинхронний метод для отримання моделі з кешуванням
221
+ async getModel() {
222
+ try {
223
+ // Якщо модель вже кешована, повертаємо її
224
+ if (this._cachedModel) {
225
+ return this._cachedModel;
226
+ }
227
+ // Якщо модель вже завантажується, чекаємо завершення
228
+ if (this._modelLoading) {
229
+ return await this._modelLoading;
230
+ }
231
+ this._modelLoading = this.modelProvider();
232
+ const model = await this._modelLoading;
233
+ if (!model) {
234
+ console.error(`❌ [MainRepository] Model provider returned null!`);
235
+ this._modelLoading = null;
236
+ throw new Error('Model is null - tenant connection failed');
237
+ }
238
+ if (!model.sequelize) {
239
+ throw new Error(`[MainRepository] Model "${model.name}" is not bound to Sequelize. Ensure it is registered in models.registry.ts.`);
240
+ }
241
+ // Кешуємо модель
242
+ this._cachedModel = model;
243
+ this._modelLoading = null; // Завантаження завершено
244
+ // Get and log Sequelize instance associated with this model
245
+ const sequelize = this._cachedModel.sequelize;
246
+ if (process.env.TENANCY_STRATEGY === 'shared' &&
247
+ sequelize &&
248
+ sequelize.config &&
249
+ sequelize.config.database !== process.env.SHARED_DB_NAME) {
250
+ throw new Error('!!CRITICAL: USE SHARED DATABASE IS NOT CORRECT!!');
251
+ }
252
+ return model;
253
+ }
254
+ catch (error) {
255
+ console.error(`❌ [MainRepository] Error getting model:`, error.message);
256
+ this._modelLoading = null; // Скидаємо стан завантаження при помилці
257
+ throw error;
258
+ }
259
+ }
260
+ /*
261
+ * Only for checking model is loaded
262
+ */
263
+ async checkModel() {
264
+ const model = await this.getModel();
265
+ return model;
266
+ }
267
+ // Метод для скидання кешу (корисно для тестування або зміни тенанта)
268
+ clearModelCache() {
269
+ this._cachedModel = null;
270
+ this._modelLoading = null;
271
+ }
272
+ async create(item, options) {
273
+ const model = await this.getModel();
274
+ if (!model) {
275
+ const error = new Error('Model is undefined in MainRepository.create');
276
+ throw error;
277
+ }
278
+ // const scope = this.resolveScopedTenantFields();
279
+ const itemWithScope = this.mergeRowWithTenantScope(item, {});
280
+ const result = await model.create(itemWithScope, options);
281
+ return result;
282
+ }
283
+ async update(id, item, options) {
284
+ const model = await this.getModel();
285
+ if (!model) {
286
+ console.error("Model is undefined in MainRepository.update");
287
+ return { affectedCount: 0, rows: [] };
288
+ }
289
+ const scopedWhere = this.applyScopeToWhere({ id: id });
290
+ const [affectedCount, rows] = await model.update(item, {
291
+ where: scopedWhere,
292
+ ...options,
293
+ returning: true,
294
+ });
295
+ return { affectedCount, rows };
296
+ }
297
+ // async update(
298
+ // id: string | number,
299
+ // item: Partial<T>,
300
+ // options?: IOptions,
301
+ // ): Promise<T[]> {
302
+ // try {
303
+ // const model = await this.getModel();
304
+ // if (!model) {
305
+ // console.error('Model is undefined in MainRepository.update');
306
+ // return [];
307
+ // }
308
+ // const [affectedCount, rows] = await model.update(item, {
309
+ // where: { id: id as any },
310
+ // ...options,
311
+ // returning: true,
312
+ // });
313
+ // return rows;
314
+ // } catch (error) {
315
+ // console.error(`Error updating item:`, error);
316
+ // return [];
317
+ // }
318
+ // }
319
+ /**
320
+ * Оновлений метод delete з автоматичним фільтруванням по tenantId
321
+ */
322
+ async delete(id) {
323
+ const model = await this.getModel();
324
+ if (!model) {
325
+ console.error("Model is undefined in MainRepository.delete");
326
+ return false;
327
+ }
328
+ const scopedWhere = this.applyScopeToWhere({ id: id });
329
+ const affectedCount = await model.destroy({ where: scopedWhere });
330
+ return affectedCount > 0;
331
+ }
332
+ async hardDelete(id) {
333
+ return await this.delete(id);
334
+ }
335
+ async deleteWithTransaction(id, transaction) {
336
+ const model = await this.getModel();
337
+ const scopedWhere = this.applyScopeToWhere({ id: id });
338
+ const affectedCount = await model.destroy({
339
+ where: scopedWhere,
340
+ transaction,
341
+ });
342
+ return affectedCount > 0;
343
+ }
344
+ /**
345
+ * Оновлений метод findById з автоматичним фільтруванням по tenantId
346
+ */
347
+ async findById(id, companyId, options) {
348
+ const model = await this.getModel();
349
+ if (!model) {
350
+ console.error("Model is undefined in MainRepository.findById");
351
+ return null;
352
+ }
353
+ if (companyId) {
354
+ return await model.findOne({
355
+ where: this.applyScopeToWhere({
356
+ id: id,
357
+ companyId,
358
+ }),
359
+ ...options,
360
+ });
361
+ }
362
+ return await model.findOne({
363
+ where: this.applyScopeToWhere({ id: id }),
364
+ ...options,
365
+ });
366
+ }
367
+ async findByIdWithTransaction(id, transaction) {
368
+ try {
369
+ const model = await this.getModel();
370
+ return await model.findByPk(id, { transaction });
371
+ }
372
+ catch (error) {
373
+ console.error(`Error finding item by id with transaction:`, error);
374
+ return null;
375
+ }
376
+ }
377
+ async findAllWithTransaction(transaction) {
378
+ try {
379
+ const model = await this.getModel();
380
+ return await model.findAll({ transaction });
381
+ }
382
+ catch (error) {
383
+ console.error(`Error finding all items with transaction:`, error);
384
+ return [];
385
+ }
386
+ }
387
+ async findOne(where, options) {
388
+ try {
389
+ const model = await this.getModel();
390
+ if (!model) {
391
+ console.error('Model is undefined in MainRepository.findOne');
392
+ return null;
393
+ }
394
+ const scopedWhere = this.applyScopeToWhere(where);
395
+ return await model.findOne({ where: scopedWhere, ...options });
396
+ }
397
+ catch (error) {
398
+ console.error(`Error finding one item:`, error);
399
+ return null;
400
+ }
401
+ }
402
+ async findOneWithTransaction(where, transaction) {
403
+ try {
404
+ const model = await this.getModel();
405
+ return await model.findOne({ where, transaction });
406
+ }
407
+ catch (error) {
408
+ console.error(`Error finding one item with transaction:`, error);
409
+ return null;
410
+ }
411
+ }
412
+ /**
413
+ * @deprecated Use findAll instead
414
+ */
415
+ async findMany(where, include, order) {
416
+ try {
417
+ const model = await this.getModel();
418
+ if (!model) {
419
+ return [];
420
+ }
421
+ const options = { where };
422
+ if (include)
423
+ options.include = include;
424
+ if (order)
425
+ options.order = order;
426
+ return await model.findAll(options);
427
+ }
428
+ catch (error) {
429
+ console.error(`Error finding many items:`, error);
430
+ return [];
431
+ }
432
+ }
433
+ /**
434
+ * Find all items with automatic isolation by companyId and accountId
435
+ */
436
+ async findAll(where = {}, options) {
437
+ try {
438
+ const model = await this.getModel();
439
+ if (!model)
440
+ return [];
441
+ const scopedWhere = this.applyScopeToWhere(where);
442
+ return await model.findAll({
443
+ where: scopedWhere,
444
+ ...(options || {}),
445
+ });
446
+ }
447
+ catch (error) {
448
+ console.error(`Error finding many items:`, error);
449
+ return [];
450
+ }
451
+ }
452
+ /**
453
+ * Find all items with pagination and automatic isolation
454
+ */
455
+ async findAllWithPagination(where = {}, pagination, options) {
456
+ const model = await this.getModel();
457
+ if (!model)
458
+ throw new Error('Model is null in findAllWithPagination');
459
+ const scopedWhere = this.applyScopeToWhere(where);
460
+ const countOptions = {
461
+ where: scopedWhere,
462
+ distinct: true,
463
+ };
464
+ if (options?.include) {
465
+ countOptions.include = options.include;
466
+ }
467
+ const limit = pagination.limit || 10;
468
+ const page = pagination.page || 1;
469
+ const offset = (page - 1) * limit;
470
+ let orderFields = [];
471
+ if (pagination.order) {
472
+ if (Array.isArray(pagination.order)) {
473
+ orderFields = pagination.order;
474
+ }
475
+ else if (typeof pagination.order === 'string') {
476
+ orderFields = [pagination.order];
477
+ }
478
+ }
479
+ const [list, total] = await Promise.all([
480
+ model.findAll({
481
+ where: scopedWhere,
482
+ ...(options || {}),
483
+ limit,
484
+ offset,
485
+ order: orderFields.length > 0
486
+ ? orderFields.map((field) => [
487
+ field,
488
+ pagination.orderDirection || 'DESC',
489
+ ])
490
+ : [],
491
+ }),
492
+ model.count(countOptions),
493
+ ]);
494
+ return {
495
+ list,
496
+ total: Number(total),
497
+ page,
498
+ limit,
499
+ totalPages: Math.ceil(Number(total) / limit),
500
+ };
501
+ }
502
+ async findManyWithTransaction(where, transaction, include, order) {
503
+ try {
504
+ const model = await this.getModel();
505
+ const options = { where, transaction };
506
+ if (include)
507
+ options.include = include;
508
+ if (order)
509
+ options.order = order;
510
+ return await model.findAll(options);
511
+ }
512
+ catch (error) {
513
+ console.error(`Error finding many items with transaction:`, error);
514
+ return [];
515
+ }
516
+ }
517
+ /**
518
+ * Count with automatic isolation by companyId and accountId
519
+ */
520
+ async count(where, tenantId) {
521
+ try {
522
+ const model = await this.getModel();
523
+ if (!model) {
524
+ console.error('Model is undefined in MainRepository.count');
525
+ return 0;
526
+ }
527
+ const scopedWhere = this.applyScopeToWhere(where);
528
+ const options = { where: scopedWhere };
529
+ const result = await model.count(options);
530
+ return result;
531
+ }
532
+ catch (error) {
533
+ console.error(`Error counting item:`, error);
534
+ return 0;
535
+ }
536
+ }
537
+ async countWithTransaction(where, transaction) {
538
+ try {
539
+ const model = await this.getModel();
540
+ const result = await model.count({ where, transaction });
541
+ return result;
542
+ }
543
+ catch (error) {
544
+ console.error(`Error counting item with transaction:`, error);
545
+ return 0;
546
+ }
547
+ }
548
+ /**
549
+ * Оновлений метод exists з автоматичним фільтруванням по tenantId
550
+ */
551
+ async exists(where) {
552
+ try {
553
+ const model = await this.getModel();
554
+ if (!model) {
555
+ console.error('Model is undefined in MainRepository.exists');
556
+ return false;
557
+ }
558
+ const scopedWhere = this.applyScopeToWhere(where);
559
+ const count = await model.count({ where: scopedWhere });
560
+ return count > 0;
561
+ }
562
+ catch (error) {
563
+ console.error(`Error checking existence of item:`, error);
564
+ return false;
565
+ }
566
+ }
567
+ async existsWithTransaction(where, transaction) {
568
+ try {
569
+ const model = await this.getModel();
570
+ const count = await model.count({ where, transaction });
571
+ return count > 0;
572
+ }
573
+ catch (error) {
574
+ console.error(`Error checking existence of item with transaction:`, error);
575
+ return false;
576
+ }
577
+ }
578
+ async bulkCreate(items) {
579
+ try {
580
+ const model = await this.getModel();
581
+ if (!model) {
582
+ console.error('Model is undefined in MainRepository.bulkCreate');
583
+ return [];
584
+ }
585
+ // const scope = this.resolveScopedTenantFields();
586
+ const rows = (items || []).map((row) => this.mergeRowWithTenantScope(row, {}));
587
+ return await model.bulkCreate(rows);
588
+ }
589
+ catch (error) {
590
+ console.error(`Error bulk creating item:`, error);
591
+ return [];
592
+ }
593
+ }
594
+ async bulkCreateWithTransaction(items, transaction) {
595
+ try {
596
+ const model = await this.getModel();
597
+ return await model.bulkCreate(items, { transaction });
598
+ }
599
+ catch (error) {
600
+ console.error(`Error bulk creating item with transaction:`, error);
601
+ return [];
602
+ }
603
+ }
604
+ async bulkUpdate(where, updates) {
605
+ try {
606
+ const model = await this.getModel();
607
+ if (!model) {
608
+ console.error('Model is undefined in MainRepository.bulkUpdate');
609
+ return 0;
610
+ }
611
+ const scopedWhere = this.applyScopeToWhere(where);
612
+ const [affectedCount] = await model.update(updates, { where: scopedWhere });
613
+ return affectedCount;
614
+ }
615
+ catch (error) {
616
+ console.error(`Error bulk updating item:`, error);
617
+ return 0;
618
+ }
619
+ }
620
+ async bulkUpdateWithTransaction(where, updates, transaction) {
621
+ try {
622
+ const model = await this.getModel();
623
+ const scopedWhere = this.applyScopeToWhere(where);
624
+ const [affectedCount] = await model.update(updates, {
625
+ where: scopedWhere,
626
+ transaction,
627
+ });
628
+ return affectedCount;
629
+ }
630
+ catch (error) {
631
+ console.error(`Error bulk updating item with transaction:`, error);
632
+ return 0;
633
+ }
634
+ }
635
+ async bulkDelete(where) {
636
+ try {
637
+ const model = await this.getModel();
638
+ if (!model) {
639
+ console.error('Model is undefined in MainRepository.bulkDelete');
640
+ return 0;
641
+ }
642
+ const scopedWhere = this.applyScopeToWhere(where);
643
+ return await model.destroy({ where: scopedWhere });
644
+ }
645
+ catch (error) {
646
+ console.error(`Error bulk deleting item:`, error);
647
+ return 0;
648
+ }
649
+ }
650
+ async bulkDeleteWithTransaction(where, transaction) {
651
+ try {
652
+ const model = await this.getModel();
653
+ const scopedWhere = this.applyScopeToWhere(where);
654
+ return await model.destroy({ where: scopedWhere, transaction });
655
+ }
656
+ catch (error) {
657
+ console.error(`Error bulk deleting item with transaction:`, error);
658
+ return 0;
659
+ }
660
+ }
661
+ async findAndCountAll(options) {
662
+ try {
663
+ const model = await this.getModel();
664
+ if (!model) {
665
+ console.error('Model is undefined in MainRepository.findAndCountAll');
666
+ return { rows: [], count: 0 };
667
+ }
668
+ return await model.findAndCountAll(options);
669
+ }
670
+ catch (error) {
671
+ console.error(`Error finding and counting item:`, error);
672
+ return { rows: [], count: 0 };
673
+ }
674
+ }
675
+ async findAndCountAllWithTransaction(options, transaction) {
676
+ try {
677
+ const model = await this.getModel();
678
+ return await model.findAndCountAll({ ...options, transaction });
679
+ }
680
+ catch (error) {
681
+ console.error(`Error finding and counting item with transaction:`, error);
682
+ return { rows: [], count: 0 };
683
+ }
684
+ }
685
+ async findOrCreate(where, defaults) {
686
+ try {
687
+ const model = await this.getModel();
688
+ if (!model) {
689
+ console.error('Model is undefined in MainRepository.findOrCreate');
690
+ return [null, false];
691
+ }
692
+ // const scope = this.resolveScopedTenantFields();
693
+ return await model.findOrCreate({
694
+ where: this.applyScopeToWhere(where),
695
+ defaults: this.mergeRowWithTenantScope(defaults, {}),
696
+ });
697
+ }
698
+ catch (error) {
699
+ console.error(`Error finding or creating item:`, error);
700
+ return [null, false];
701
+ }
702
+ }
703
+ async findOrCreateWithTransaction(where, defaults, transaction) {
704
+ try {
705
+ const model = await this.getModel();
706
+ // const scope = this.resolveScopedTenantFields();
707
+ return await model.findOrCreate({
708
+ where: this.applyScopeToWhere(where),
709
+ defaults: this.mergeRowWithTenantScope(defaults, {}),
710
+ transaction,
711
+ });
712
+ }
713
+ catch (error) {
714
+ console.error(`Error finding or creating item with transaction:`, error);
715
+ return [null, false];
716
+ }
717
+ }
718
+ async upsert(values, where) {
719
+ try {
720
+ const model = await this.getModel();
721
+ if (!model) {
722
+ console.error('Model is undefined in MainRepository.upsert');
723
+ return null;
724
+ }
725
+ // Sequelize upsert не приймає where в options, а використовує values для пошуку
726
+ const [instance] = await model.upsert(values);
727
+ return instance;
728
+ }
729
+ catch (error) {
730
+ console.error(`Error upserting item:`, error);
731
+ return null;
732
+ }
733
+ }
734
+ async upsertWithTransaction(values, where, transaction) {
735
+ try {
736
+ const model = await this.getModel();
737
+ // Sequelize upsert не приймає where в options, а використовує values для пошуку
738
+ const [instance] = await model.upsert(values, {
739
+ transaction,
740
+ });
741
+ return instance;
742
+ }
743
+ catch (error) {
744
+ console.error(`Error upserting item with transaction:`, error);
745
+ return null;
746
+ }
747
+ }
748
+ async increment(where, fields, by = 1) {
749
+ try {
750
+ const model = await this.getModel();
751
+ if (!model) {
752
+ console.error('Model is undefined in MainRepository.increment');
753
+ return 0;
754
+ }
755
+ const result = await model.increment(fields, { where, by });
756
+ return result[fields[0]] || 0;
757
+ }
758
+ catch (error) {
759
+ console.error(`Error incrementing item:`, error);
760
+ return 0;
761
+ }
762
+ }
763
+ async incrementWithTransaction(where, fields, by, transaction) {
764
+ try {
765
+ const model = await this.getModel();
766
+ const result = await model.increment(fields, {
767
+ where,
768
+ by,
769
+ transaction,
770
+ });
771
+ return result[fields[0]] || 0;
772
+ }
773
+ catch (error) {
774
+ console.error(`Error incrementing item with transaction:`, error);
775
+ return 0;
776
+ }
777
+ }
778
+ async decrement(where, fields, by = 1) {
779
+ try {
780
+ const model = await this.getModel();
781
+ if (!model) {
782
+ console.error('Model is undefined in MainRepository.decrement');
783
+ return 0;
784
+ }
785
+ const result = await model.decrement(fields, { where, by });
786
+ return result[fields[0]] || 0;
787
+ }
788
+ catch (error) {
789
+ console.error(`Error decrementing item:`, error);
790
+ return 0;
791
+ }
792
+ }
793
+ async decrementWithTransaction(where, fields, by, transaction) {
794
+ try {
795
+ const model = await this.getModel();
796
+ const result = await model.decrement(fields, {
797
+ where,
798
+ by,
799
+ transaction,
800
+ });
801
+ return result[fields[0]] || 0;
802
+ }
803
+ catch (error) {
804
+ console.error(`Error decrementing item with transaction:`, error);
805
+ return 0;
806
+ }
807
+ }
808
+ async max(field, where) {
809
+ try {
810
+ const model = await this.getModel();
811
+ if (!model) {
812
+ console.error('Model is undefined in MainRepository.max');
813
+ return 0;
814
+ }
815
+ const options = { attributes: [[field, 'max']] };
816
+ if (where)
817
+ options.where = where;
818
+ const result = await model.findOne(options);
819
+ return result?.getDataValue('max') || 0;
820
+ }
821
+ catch (error) {
822
+ console.error(`Error getting max ${field} from item:`, error);
823
+ return 0;
824
+ }
825
+ }
826
+ async min(field, where) {
827
+ try {
828
+ const model = await this.getModel();
829
+ if (!model) {
830
+ console.error('Model is undefined in MainRepository.min');
831
+ return 0;
832
+ }
833
+ const options = { attributes: [[field, 'min']] };
834
+ if (where)
835
+ options.where = where;
836
+ const result = await model.findOne(options);
837
+ return result?.getDataValue('min') || 0;
838
+ }
839
+ catch (error) {
840
+ console.error(`Error getting min ${field} from item:`, error);
841
+ return 0;
842
+ }
843
+ }
844
+ async sum(field, where) {
845
+ try {
846
+ const model = await this.getModel();
847
+ if (!model) {
848
+ console.error('Model is undefined in MainRepository.sum');
849
+ return 0;
850
+ }
851
+ const options = { attributes: [[field, 'sum']] };
852
+ if (where)
853
+ options.where = where;
854
+ const result = await model.findOne(options);
855
+ return result?.getDataValue('sum') || 0;
856
+ }
857
+ catch (error) {
858
+ console.error(`Error getting sum ${field} from item:`, error);
859
+ return 0;
860
+ }
861
+ }
862
+ async average(field, where) {
863
+ try {
864
+ const model = await this.getModel();
865
+ if (!model) {
866
+ console.error('Model is undefined in MainRepository.average');
867
+ return 0;
868
+ }
869
+ const options = { attributes: [[field, 'avg']] };
870
+ if (where)
871
+ options.where = where;
872
+ const result = await model.findOne(options);
873
+ return result?.getDataValue('avg') || 0;
874
+ }
875
+ catch (error) {
876
+ console.error(`Error getting average ${field} from item:`, error);
877
+ return 0;
878
+ }
879
+ }
880
+ async rawQuery(sql, replacements) {
881
+ try {
882
+ const model = await this.getModel();
883
+ if (!model) {
884
+ console.error('Model is undefined in MainRepository.rawQuery');
885
+ return [];
886
+ }
887
+ return await model.sequelize.query(sql, { replacements });
888
+ }
889
+ catch (error) {
890
+ console.error(`Error executing raw query on item:`, error);
891
+ return [];
892
+ }
893
+ }
894
+ async rawQueryWithTransaction(sql, transaction, replacements) {
895
+ try {
896
+ const model = await this.getModel();
897
+ return await model.sequelize.query(sql, {
898
+ replacements,
899
+ transaction,
900
+ });
901
+ }
902
+ catch (error) {
903
+ console.error(`Error executing raw query on item with transaction:`, error);
904
+ return [];
905
+ }
906
+ }
907
+ async findFirst(where, options) {
908
+ try {
909
+ const model = await this.getModel();
910
+ if (!model)
911
+ return null;
912
+ const scopedWhere = this.applyScopeToWhere(where || {});
913
+ return await model.findOne({
914
+ where: scopedWhere,
915
+ ...options,
916
+ });
917
+ }
918
+ catch (error) {
919
+ console.error(`Error finding first item:`, error);
920
+ return null;
921
+ }
922
+ }
923
+ async findAndCount(where, include, order) {
924
+ try {
925
+ const model = await this.getModel();
926
+ if (!model) {
927
+ console.error('Model is undefined in MainRepository.findAndCount');
928
+ return { count: 0, rows: [] };
929
+ }
930
+ const scopedWhere = this.applyScopeToWhere(where || {});
931
+ const options = { where: scopedWhere };
932
+ if (include)
933
+ options.include = include;
934
+ if (order)
935
+ options.order = order;
936
+ return await model.findAndCountAll(options);
937
+ }
938
+ catch (error) {
939
+ console.error(`Error finding and counting items:`, error);
940
+ return { count: 0, rows: [] };
941
+ }
942
+ }
943
+ async updateMany(where, data, options) {
944
+ try {
945
+ const model = await this.getModel();
946
+ if (!model) {
947
+ console.error('Model is undefined in MainRepository.updateMany');
948
+ return options.returning ? [] : 0;
949
+ }
950
+ const scopedWhere = this.applyScopeToWhere(where);
951
+ if (options && options?.returning) {
952
+ const [affectedCount, rows] = await model.update(data, {
953
+ where: scopedWhere,
954
+ ...options,
955
+ returning: true,
956
+ });
957
+ return rows;
958
+ }
959
+ const [affectedCount] = await model.update(data, {
960
+ where: scopedWhere,
961
+ ...options,
962
+ returning: false,
963
+ });
964
+ return affectedCount;
965
+ }
966
+ catch (error) {
967
+ console.error(`Error updating many items:`, error);
968
+ return options.returning ? [] : 0;
969
+ }
970
+ }
971
+ async updateManyWithTransaction(where, data, transaction) {
972
+ try {
973
+ const model = await this.getModel();
974
+ const scopedWhere = this.applyScopeToWhere(where);
975
+ const [affectedCount] = await model.update(data, {
976
+ where: scopedWhere,
977
+ transaction,
978
+ });
979
+ return affectedCount;
980
+ }
981
+ catch (error) {
982
+ console.error(`Error updating many items with transaction:`, error);
983
+ return 0;
984
+ }
985
+ }
986
+ async softDelete(id, companyId) {
987
+ try {
988
+ const model = await this.getModel();
989
+ if (!model)
990
+ return false;
991
+ // Check if model has is_delete property
992
+ const hasIsDeleteField = model.getAttributes && 'is_delete' in model.getAttributes();
993
+ if (hasIsDeleteField) {
994
+ const scopedWhere = this.applyScopeToWhere({ id: id });
995
+ const [affectedCount] = await model.update({ deletedAt: new Date() }, { where: scopedWhere });
996
+ return affectedCount > 0;
997
+ }
998
+ else {
999
+ const scopedWhere = this.applyScopeToWhere({ id: id });
1000
+ const affectedCount = await model.destroy({ where: scopedWhere });
1001
+ return affectedCount > 0;
1002
+ }
1003
+ }
1004
+ catch (error) {
1005
+ return false;
1006
+ }
1007
+ }
1008
+ async deleteMany(where) {
1009
+ try {
1010
+ const model = await this.getModel();
1011
+ if (!model) {
1012
+ console.error('Model is undefined in MainRepository.deleteMany');
1013
+ return 0;
1014
+ }
1015
+ const scopedWhere = this.applyScopeToWhere(where);
1016
+ return await model.destroy({ where: scopedWhere });
1017
+ }
1018
+ catch (error) {
1019
+ console.error(`Error deleting many items:`, error);
1020
+ return 0;
1021
+ }
1022
+ }
1023
+ async deleteManyWithTransaction(where, transaction) {
1024
+ try {
1025
+ const model = await this.getModel();
1026
+ const scopedWhere = this.applyScopeToWhere(where);
1027
+ return await model.destroy({ where: scopedWhere, transaction });
1028
+ }
1029
+ catch (error) {
1030
+ console.error(`Error deleting many items with transaction:`, error);
1031
+ return 0;
1032
+ }
1033
+ }
1034
+ /**
1035
+ * 🔍 Debug: Log query info before each operation
1036
+ */
1037
+ logQueryInfo(method, params) {
1038
+ try {
1039
+ const model = this._cachedModel;
1040
+ if (model && model.sequelize) {
1041
+ const config = model.sequelize.config;
1042
+ const strategy = process.env.TENANCY_STRATEGY || 'separate';
1043
+ const sharedDbName = process.env.SHARED_DB_NAME || 'not_set';
1044
+ console.log(`🔍 [${this.constructor.name}.${method}] Database Info:`);
1045
+ console.log(` 📊 Database: ${config.database || 'unknown'}`);
1046
+ console.log(` 🌐 Host: ${config.host || 'unknown'}`);
1047
+ console.log(` 🔌 Port: ${config.port || 'unknown'}`);
1048
+ console.log(` 👤 Username: ${config.username || 'unknown'}`);
1049
+ console.log(` 🎯 Strategy: ${strategy}`);
1050
+ console.log(` 🔗 SHARED_DB_NAME: ${sharedDbName}`);
1051
+ console.log(` 📝 Model: ${model.name}`);
1052
+ if (params) {
1053
+ console.log(` 📋 Params:`, params);
1054
+ }
1055
+ // Додаткова інформація про стратегію
1056
+ if (strategy === 'shared') {
1057
+ console.log(` ✅ Using SHARED strategy - connecting to: ${sharedDbName}`);
1058
+ }
1059
+ else {
1060
+ console.log(` 🏗️ Using SEPARATE strategy - tenant-specific connection`);
1061
+ }
1062
+ console.log(` ─`.repeat(50));
1063
+ }
1064
+ else {
1065
+ console.log(`🔍 [${this.constructor.name}.${method}] Model not loaded yet`);
1066
+ console.log(` 📊 Strategy: ${process.env.TENANCY_STRATEGY || 'separate'}`);
1067
+ console.log(` 🔗 SHARED_DB_NAME: ${process.env.SHARED_DB_NAME || 'not_set'}`);
1068
+ console.log(` ─`.repeat(50));
1069
+ }
1070
+ }
1071
+ catch (error) {
1072
+ console.log(`🔍 [${this.constructor.name}.${method}] Error logging query info:`, error.message);
1073
+ }
1074
+ }
1075
+ /**
1076
+ * Створити новий запис з транзакцією
1077
+ */
1078
+ async createWithTransaction(item, transaction) {
1079
+ try {
1080
+ const model = await this.getModel();
1081
+ return await model.create(item, { transaction });
1082
+ }
1083
+ catch (error) {
1084
+ console.error(`Error creating item with transaction:`, error);
1085
+ throw error;
1086
+ }
1087
+ }
1088
+ async updateWithTransaction(id, item, transaction) {
1089
+ try {
1090
+ const model = await this.getModel();
1091
+ const [affectedCount] = await model.update(item, {
1092
+ where: { id: id },
1093
+ transaction,
1094
+ });
1095
+ return affectedCount > 0;
1096
+ }
1097
+ catch (error) {
1098
+ console.error(`Error updating item with transaction:`, error);
1099
+ return false;
1100
+ }
1101
+ }
1102
+ checkModelInfo(model) {
1103
+ console.log('Check model info');
1104
+ console.log('Model:', model);
1105
+ console.log('Model name:', model.name);
1106
+ console.log('Model tableName:', model.tableName);
1107
+ console.log('Model sequelize:', model.sequelize);
1108
+ if (model?.sequelize?.config) {
1109
+ console.log('Model sequelize config:', model.sequelize.config);
1110
+ console.log('Model sequelize config database:', model.sequelize.config.database);
1111
+ console.log('Model sequelize config host:', model.sequelize.config.host);
1112
+ }
1113
+ }
1114
+ };
1115
+ MainRepository = __decorate([
1116
+ Injectable(),
1117
+ __metadata("design:paramtypes", [Function])
1118
+ ], MainRepository);
1119
+ export { MainRepository };
1120
+ //# sourceMappingURL=main.repository.js.map