@spinajs/orm 2.0.481 → 2.0.482

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 (228) hide show
  1. package/README.md +72 -3
  2. package/lib/cjs/bootstrap.js +10 -0
  3. package/lib/cjs/bootstrap.js.map +1 -1
  4. package/lib/cjs/builders.d.ts +115 -11
  5. package/lib/cjs/builders.d.ts.map +1 -1
  6. package/lib/cjs/builders.js +342 -283
  7. package/lib/cjs/builders.js.map +1 -1
  8. package/lib/cjs/converters.d.ts +9 -2
  9. package/lib/cjs/converters.d.ts.map +1 -1
  10. package/lib/cjs/converters.js +48 -8
  11. package/lib/cjs/converters.js.map +1 -1
  12. package/lib/cjs/decorators.d.ts +26 -3
  13. package/lib/cjs/decorators.d.ts.map +1 -1
  14. package/lib/cjs/decorators.js +95 -42
  15. package/lib/cjs/decorators.js.map +1 -1
  16. package/lib/cjs/dehydrators.js +1 -1
  17. package/lib/cjs/dehydrators.js.map +1 -1
  18. package/lib/cjs/descriptor.d.ts +1 -0
  19. package/lib/cjs/descriptor.d.ts.map +1 -1
  20. package/lib/cjs/descriptor.js +16 -40
  21. package/lib/cjs/descriptor.js.map +1 -1
  22. package/lib/cjs/driver.d.ts +136 -7
  23. package/lib/cjs/driver.d.ts.map +1 -1
  24. package/lib/cjs/driver.js +279 -0
  25. package/lib/cjs/driver.js.map +1 -1
  26. package/lib/cjs/existsRelationHandlers.d.ts +29 -0
  27. package/lib/cjs/existsRelationHandlers.d.ts.map +1 -0
  28. package/lib/cjs/existsRelationHandlers.js +104 -0
  29. package/lib/cjs/existsRelationHandlers.js.map +1 -0
  30. package/lib/cjs/fp.d.ts.map +1 -1
  31. package/lib/cjs/fp.js +10 -15
  32. package/lib/cjs/fp.js.map +1 -1
  33. package/lib/cjs/hydrators.d.ts.map +1 -1
  34. package/lib/cjs/hydrators.js +29 -2
  35. package/lib/cjs/hydrators.js.map +1 -1
  36. package/lib/cjs/identity-map.d.ts +60 -0
  37. package/lib/cjs/identity-map.d.ts.map +1 -0
  38. package/lib/cjs/identity-map.js +135 -0
  39. package/lib/cjs/identity-map.js.map +1 -0
  40. package/lib/cjs/index.d.ts +12 -0
  41. package/lib/cjs/index.d.ts.map +1 -1
  42. package/lib/cjs/index.js +12 -0
  43. package/lib/cjs/index.js.map +1 -1
  44. package/lib/cjs/interfaces.d.ts +295 -9
  45. package/lib/cjs/interfaces.d.ts.map +1 -1
  46. package/lib/cjs/interfaces.js +54 -1
  47. package/lib/cjs/interfaces.js.map +1 -1
  48. package/lib/cjs/metadata.d.ts.map +1 -1
  49. package/lib/cjs/metadata.js +4 -1
  50. package/lib/cjs/metadata.js.map +1 -1
  51. package/lib/cjs/metrics.d.ts +53 -0
  52. package/lib/cjs/metrics.d.ts.map +1 -0
  53. package/lib/cjs/metrics.js +82 -0
  54. package/lib/cjs/metrics.js.map +1 -0
  55. package/lib/cjs/middlewares.d.ts +13 -1
  56. package/lib/cjs/middlewares.d.ts.map +1 -1
  57. package/lib/cjs/middlewares.js +55 -15
  58. package/lib/cjs/middlewares.js.map +1 -1
  59. package/lib/cjs/model.d.ts +129 -13
  60. package/lib/cjs/model.d.ts.map +1 -1
  61. package/lib/cjs/model.js +449 -93
  62. package/lib/cjs/model.js.map +1 -1
  63. package/lib/cjs/orm.d.ts.map +1 -1
  64. package/lib/cjs/orm.js +12 -3
  65. package/lib/cjs/orm.js.map +1 -1
  66. package/lib/cjs/orphan.d.ts +27 -0
  67. package/lib/cjs/orphan.d.ts.map +1 -0
  68. package/lib/cjs/orphan.js +55 -0
  69. package/lib/cjs/orphan.js.map +1 -0
  70. package/lib/cjs/primary-keys.d.ts +60 -0
  71. package/lib/cjs/primary-keys.d.ts.map +1 -0
  72. package/lib/cjs/primary-keys.js +243 -0
  73. package/lib/cjs/primary-keys.js.map +1 -0
  74. package/lib/cjs/relation-objects.d.ts +101 -9
  75. package/lib/cjs/relation-objects.d.ts.map +1 -1
  76. package/lib/cjs/relation-objects.js +194 -43
  77. package/lib/cjs/relation-objects.js.map +1 -1
  78. package/lib/cjs/relations.d.ts +7 -1
  79. package/lib/cjs/relations.d.ts.map +1 -1
  80. package/lib/cjs/relations.js +16 -1
  81. package/lib/cjs/relations.js.map +1 -1
  82. package/lib/cjs/resilience.d.ts +50 -0
  83. package/lib/cjs/resilience.d.ts.map +1 -0
  84. package/lib/cjs/resilience.js +70 -0
  85. package/lib/cjs/resilience.js.map +1 -0
  86. package/lib/cjs/snapshot.d.ts +58 -0
  87. package/lib/cjs/snapshot.d.ts.map +1 -0
  88. package/lib/cjs/snapshot.js +125 -0
  89. package/lib/cjs/snapshot.js.map +1 -0
  90. package/lib/cjs/statements.d.ts +11 -1
  91. package/lib/cjs/statements.d.ts.map +1 -1
  92. package/lib/cjs/statements.js +13 -2
  93. package/lib/cjs/statements.js.map +1 -1
  94. package/lib/cjs/subject-builder.d.ts +79 -0
  95. package/lib/cjs/subject-builder.d.ts.map +1 -0
  96. package/lib/cjs/subject-builder.js +296 -0
  97. package/lib/cjs/subject-builder.js.map +1 -0
  98. package/lib/cjs/subject-executor.d.ts +122 -0
  99. package/lib/cjs/subject-executor.d.ts.map +1 -0
  100. package/lib/cjs/subject-executor.js +319 -0
  101. package/lib/cjs/subject-executor.js.map +1 -0
  102. package/lib/cjs/subject-sorter.d.ts +70 -0
  103. package/lib/cjs/subject-sorter.d.ts.map +1 -0
  104. package/lib/cjs/subject-sorter.js +173 -0
  105. package/lib/cjs/subject-sorter.js.map +1 -0
  106. package/lib/cjs/subject.d.ts +100 -0
  107. package/lib/cjs/subject.d.ts.map +1 -0
  108. package/lib/cjs/subject.js +89 -0
  109. package/lib/cjs/subject.js.map +1 -0
  110. package/lib/cjs/unit-of-work.d.ts +52 -0
  111. package/lib/cjs/unit-of-work.d.ts.map +1 -0
  112. package/lib/cjs/unit-of-work.js +170 -0
  113. package/lib/cjs/unit-of-work.js.map +1 -0
  114. package/lib/mjs/bootstrap.js +10 -0
  115. package/lib/mjs/bootstrap.js.map +1 -1
  116. package/lib/mjs/builders.d.ts +115 -11
  117. package/lib/mjs/builders.d.ts.map +1 -1
  118. package/lib/mjs/builders.js +344 -285
  119. package/lib/mjs/builders.js.map +1 -1
  120. package/lib/mjs/converters.d.ts +9 -2
  121. package/lib/mjs/converters.d.ts.map +1 -1
  122. package/lib/mjs/converters.js +48 -8
  123. package/lib/mjs/converters.js.map +1 -1
  124. package/lib/mjs/decorators.d.ts +26 -3
  125. package/lib/mjs/decorators.d.ts.map +1 -1
  126. package/lib/mjs/decorators.js +96 -44
  127. package/lib/mjs/decorators.js.map +1 -1
  128. package/lib/mjs/dehydrators.js +1 -1
  129. package/lib/mjs/dehydrators.js.map +1 -1
  130. package/lib/mjs/descriptor.d.ts +1 -0
  131. package/lib/mjs/descriptor.d.ts.map +1 -1
  132. package/lib/mjs/descriptor.js +17 -39
  133. package/lib/mjs/descriptor.js.map +1 -1
  134. package/lib/mjs/driver.d.ts +136 -7
  135. package/lib/mjs/driver.d.ts.map +1 -1
  136. package/lib/mjs/driver.js +279 -0
  137. package/lib/mjs/driver.js.map +1 -1
  138. package/lib/mjs/existsRelationHandlers.d.ts +29 -0
  139. package/lib/mjs/existsRelationHandlers.d.ts.map +1 -0
  140. package/lib/mjs/existsRelationHandlers.js +100 -0
  141. package/lib/mjs/existsRelationHandlers.js.map +1 -0
  142. package/lib/mjs/fp.d.ts.map +1 -1
  143. package/lib/mjs/fp.js +10 -15
  144. package/lib/mjs/fp.js.map +1 -1
  145. package/lib/mjs/hydrators.d.ts.map +1 -1
  146. package/lib/mjs/hydrators.js +29 -2
  147. package/lib/mjs/hydrators.js.map +1 -1
  148. package/lib/mjs/identity-map.d.ts +60 -0
  149. package/lib/mjs/identity-map.d.ts.map +1 -0
  150. package/lib/mjs/identity-map.js +130 -0
  151. package/lib/mjs/identity-map.js.map +1 -0
  152. package/lib/mjs/index.d.ts +12 -0
  153. package/lib/mjs/index.d.ts.map +1 -1
  154. package/lib/mjs/index.js +12 -0
  155. package/lib/mjs/index.js.map +1 -1
  156. package/lib/mjs/interfaces.d.ts +295 -9
  157. package/lib/mjs/interfaces.d.ts.map +1 -1
  158. package/lib/mjs/interfaces.js +53 -0
  159. package/lib/mjs/interfaces.js.map +1 -1
  160. package/lib/mjs/metadata.d.ts.map +1 -1
  161. package/lib/mjs/metadata.js +4 -1
  162. package/lib/mjs/metadata.js.map +1 -1
  163. package/lib/mjs/metrics.d.ts +53 -0
  164. package/lib/mjs/metrics.d.ts.map +1 -0
  165. package/lib/mjs/metrics.js +76 -0
  166. package/lib/mjs/metrics.js.map +1 -0
  167. package/lib/mjs/middlewares.d.ts +13 -1
  168. package/lib/mjs/middlewares.d.ts.map +1 -1
  169. package/lib/mjs/middlewares.js +55 -15
  170. package/lib/mjs/middlewares.js.map +1 -1
  171. package/lib/mjs/model.d.ts +129 -13
  172. package/lib/mjs/model.d.ts.map +1 -1
  173. package/lib/mjs/model.js +451 -95
  174. package/lib/mjs/model.js.map +1 -1
  175. package/lib/mjs/orm.d.ts.map +1 -1
  176. package/lib/mjs/orm.js +12 -3
  177. package/lib/mjs/orm.js.map +1 -1
  178. package/lib/mjs/orphan.d.ts +27 -0
  179. package/lib/mjs/orphan.d.ts.map +1 -0
  180. package/lib/mjs/orphan.js +52 -0
  181. package/lib/mjs/orphan.js.map +1 -0
  182. package/lib/mjs/primary-keys.d.ts +60 -0
  183. package/lib/mjs/primary-keys.d.ts.map +1 -0
  184. package/lib/mjs/primary-keys.js +223 -0
  185. package/lib/mjs/primary-keys.js.map +1 -0
  186. package/lib/mjs/relation-objects.d.ts +101 -9
  187. package/lib/mjs/relation-objects.d.ts.map +1 -1
  188. package/lib/mjs/relation-objects.js +194 -43
  189. package/lib/mjs/relation-objects.js.map +1 -1
  190. package/lib/mjs/relations.d.ts +7 -1
  191. package/lib/mjs/relations.d.ts.map +1 -1
  192. package/lib/mjs/relations.js +16 -1
  193. package/lib/mjs/relations.js.map +1 -1
  194. package/lib/mjs/resilience.d.ts +50 -0
  195. package/lib/mjs/resilience.d.ts.map +1 -0
  196. package/lib/mjs/resilience.js +64 -0
  197. package/lib/mjs/resilience.js.map +1 -0
  198. package/lib/mjs/snapshot.d.ts +58 -0
  199. package/lib/mjs/snapshot.d.ts.map +1 -0
  200. package/lib/mjs/snapshot.js +115 -0
  201. package/lib/mjs/snapshot.js.map +1 -0
  202. package/lib/mjs/statements.d.ts +11 -1
  203. package/lib/mjs/statements.d.ts.map +1 -1
  204. package/lib/mjs/statements.js +14 -3
  205. package/lib/mjs/statements.js.map +1 -1
  206. package/lib/mjs/subject-builder.d.ts +79 -0
  207. package/lib/mjs/subject-builder.d.ts.map +1 -0
  208. package/lib/mjs/subject-builder.js +292 -0
  209. package/lib/mjs/subject-builder.js.map +1 -0
  210. package/lib/mjs/subject-executor.d.ts +122 -0
  211. package/lib/mjs/subject-executor.d.ts.map +1 -0
  212. package/lib/mjs/subject-executor.js +312 -0
  213. package/lib/mjs/subject-executor.js.map +1 -0
  214. package/lib/mjs/subject-sorter.d.ts +70 -0
  215. package/lib/mjs/subject-sorter.d.ts.map +1 -0
  216. package/lib/mjs/subject-sorter.js +168 -0
  217. package/lib/mjs/subject-sorter.js.map +1 -0
  218. package/lib/mjs/subject.d.ts +100 -0
  219. package/lib/mjs/subject.d.ts.map +1 -0
  220. package/lib/mjs/subject.js +84 -0
  221. package/lib/mjs/subject.js.map +1 -0
  222. package/lib/mjs/unit-of-work.d.ts +52 -0
  223. package/lib/mjs/unit-of-work.d.ts.map +1 -0
  224. package/lib/mjs/unit-of-work.js +166 -0
  225. package/lib/mjs/unit-of-work.js.map +1 -0
  226. package/lib/tsconfig.cjs.tsbuildinfo +1 -1
  227. package/lib/tsconfig.mjs.tsbuildinfo +1 -1
  228. package/package.json +9 -7
package/lib/mjs/driver.js CHANGED
@@ -13,9 +13,20 @@ import { ModelToSqlConverter, ObjectToSqlConverter } from './interfaces.js';
13
13
  import { SyncService, DI, Container, Autoinject } from '@spinajs/di';
14
14
  import { UpdateQueryBuilder, SelectQueryBuilder, IndexQueryBuilder, DeleteQueryBuilder, InsertQueryBuilder, SchemaQueryBuilder, TruncateTableQueryBuilder } from './builders.js';
15
15
  import { JsonValueConverter, StandardModelToSqlConverter, StandardObjectToSqlConverter, UniversalValueConverter, UuidConverter } from './converters.js';
16
+ import { OrmException } from './exceptions.js';
17
+ import { AsyncLocalStorage } from 'async_hooks';
18
+ import { backoffDelay, ConnectionState, delay, isRetryableErrorCode } from './resilience.js';
19
+ import { ORM_METRIC_KEY_ACQUIRE_SECONDS, ORM_METRIC_KEY_CONNECTION_STATE, ORM_METRIC_KEY_POOL_IN_USE, ORM_METRIC_KEY_POOL_SIZE, ORM_METRIC_KEY_POOL_WAITING, ormGauge, ormHistogram, ormMetrics } from './metrics.js';
16
20
  import './hydrators.js';
17
21
  import './dehydrators.js';
18
22
  export class OrmDriver extends SyncService {
23
+ /**
24
+ * The transaction currently in scope on this async execution path, or `undefined` outside
25
+ * a transaction.
26
+ */
27
+ get CurrentTransaction() {
28
+ return this.TransactionStorage.getStore();
29
+ }
19
30
  constructor(options) {
20
31
  super();
21
32
  /**
@@ -27,6 +38,22 @@ export class OrmDriver extends SyncService {
27
38
  Name: 'orm-driver',
28
39
  DefaultConnection: false,
29
40
  };
41
+ /**
42
+ * Ambient transaction context.
43
+ *
44
+ * Statements executed inside a `transaction()` callback must run on that transaction's
45
+ * connection. This lives on the abstract driver — rather than being a MySQL implementation
46
+ * detail, as it used to be — so the guarantee is part of the contract and every driver
47
+ * inherits it.
48
+ */
49
+ this.TransactionStorage = new AsyncLocalStorage();
50
+ /**
51
+ * Isolation levels this driver honours. Empty by default: a driver that declares nothing
52
+ * rejects every explicitly requested isolation level rather than quietly ignoring it.
53
+ */
54
+ this.SupportedIsolationLevels = [];
55
+ this._state = ConnectionState.Disconnected;
56
+ this._healthCheckTimer = null;
30
57
  this.Options = Object.assign(this.Options, options);
31
58
  }
32
59
  resolve() {
@@ -41,6 +68,207 @@ export class OrmDriver extends SyncService {
41
68
  this.Container.register(UuidConverter).as(UuidConverter);
42
69
  this.Container.register(UniversalValueConverter).as(UniversalValueConverter);
43
70
  }
71
+ /**
72
+ * Effective pool settings: `Pool.*` when given, then the deprecated `PoolLimit` for `Max`,
73
+ * then the defaults. Resolved in one place so every driver agrees on what "unset" means.
74
+ */
75
+ resolvedPoolOptions() {
76
+ const pool = this.Options.Pool ?? {};
77
+ return {
78
+ Min: pool.Min ?? 0,
79
+ Max: pool.Max ?? this.Options.PoolLimit ?? 10,
80
+ IdleTimeout: pool.IdleTimeout ?? 30000,
81
+ AcquireTimeout: pool.AcquireTimeout ?? 10000,
82
+ };
83
+ }
84
+ /**
85
+ * Current connection lifecycle state.
86
+ */
87
+ get State() {
88
+ return this._state;
89
+ }
90
+ /**
91
+ * Records a state transition and logs it. Repeat transitions to the same state are ignored.
92
+ */
93
+ setState(state) {
94
+ if (this._state === state) {
95
+ return;
96
+ }
97
+ const previous = this._state;
98
+ this._state = state;
99
+ if (state === ConnectionState.Connected) {
100
+ this.Log.info(`connection ${this.Options.Name} is ${state} (was ${previous})`);
101
+ }
102
+ else if (state === ConnectionState.Degraded || state === ConnectionState.Disconnected) {
103
+ this.Log.warn(`connection ${this.Options.Name} is ${state} (was ${previous})`);
104
+ }
105
+ else {
106
+ this.Log.trace(`connection ${this.Options.Name} is ${state} (was ${previous})`);
107
+ }
108
+ }
109
+ /**
110
+ * Effective resilience settings with defaults applied.
111
+ */
112
+ resolvedResilienceOptions() {
113
+ const r = this.Options.Resilience ?? {};
114
+ return {
115
+ HealthCheckInterval: r.HealthCheckInterval ?? 30000,
116
+ MaxRetries: r.MaxRetries ?? 5,
117
+ RetryDelay: r.RetryDelay ?? 200,
118
+ MaxRetryDelay: r.MaxRetryDelay ?? 5000,
119
+ };
120
+ }
121
+ /**
122
+ * True when the error means the transport died rather than the statement being wrong.
123
+ * Drivers override to add dialect-specific codes.
124
+ */
125
+ isRetryableError(err) {
126
+ return isRetryableErrorCode(err);
127
+ }
128
+ /**
129
+ * Runs `operation`, and on a retryable transport failure reconnects and retries with bounded
130
+ * exponential backoff. Query errors propagate on the first attempt untouched.
131
+ */
132
+ async withReconnect(operation) {
133
+ const { MaxRetries, RetryDelay, MaxRetryDelay } = this.resolvedResilienceOptions();
134
+ let lastError;
135
+ for (let attempt = 0; attempt <= MaxRetries; attempt++) {
136
+ try {
137
+ const result = await operation();
138
+ this.setState(ConnectionState.Connected);
139
+ return result;
140
+ }
141
+ catch (err) {
142
+ if (!this.isRetryableError(err)) {
143
+ throw err;
144
+ }
145
+ lastError = err;
146
+ this.setState(ConnectionState.Degraded);
147
+ if (attempt === MaxRetries) {
148
+ break;
149
+ }
150
+ const wait = backoffDelay(attempt, RetryDelay, MaxRetryDelay);
151
+ this.Log.warn(`connection ${this.Options.Name} lost (${err.message}); reconnect attempt ${attempt + 1}/${MaxRetries} in ${wait}ms`);
152
+ await delay(wait);
153
+ try {
154
+ this.setState(ConnectionState.Connecting);
155
+ await this.connect();
156
+ }
157
+ catch (reconnectErr) {
158
+ this.Log.warn(`reconnect attempt ${attempt + 1} for ${this.Options.Name} failed: ${reconnectErr.message}`);
159
+ this.setState(ConnectionState.Degraded);
160
+ }
161
+ }
162
+ }
163
+ this.setState(ConnectionState.Disconnected);
164
+ throw lastError;
165
+ }
166
+ /**
167
+ * Starts the periodic health probe. Replaces the single startup `ping()` — a connection that
168
+ * was healthy at boot tells you nothing about a pool holding sockets to a database that has
169
+ * since restarted. No-op when `Resilience.HealthCheckInterval` is 0. Idempotent.
170
+ */
171
+ startHealthCheck() {
172
+ const { HealthCheckInterval } = this.resolvedResilienceOptions();
173
+ if (HealthCheckInterval <= 0 || this._healthCheckTimer !== null) {
174
+ return;
175
+ }
176
+ this._healthCheckTimer = setInterval(() => {
177
+ void this.runHealthCheck();
178
+ }, HealthCheckInterval);
179
+ // Never hold the process open just to probe a database.
180
+ if (typeof this._healthCheckTimer.unref === 'function') {
181
+ this._healthCheckTimer.unref();
182
+ }
183
+ }
184
+ /**
185
+ * Stops the periodic health probe. Idempotent.
186
+ */
187
+ stopHealthCheck() {
188
+ if (this._healthCheckTimer !== null) {
189
+ clearInterval(this._healthCheckTimer);
190
+ this._healthCheckTimer = null;
191
+ }
192
+ }
193
+ /**
194
+ * One health probe. A failed probe degrades the driver and attempts a single reconnect; it
195
+ * never throws, because it runs on a timer with no caller to receive the error.
196
+ */
197
+ async runHealthCheck() {
198
+ this.publishPoolMetrics();
199
+ let alive = false;
200
+ try {
201
+ alive = await this.ping();
202
+ }
203
+ catch {
204
+ alive = false;
205
+ }
206
+ if (alive) {
207
+ this.setState(ConnectionState.Connected);
208
+ return;
209
+ }
210
+ this.setState(ConnectionState.Degraded);
211
+ try {
212
+ // The driver stays DEGRADED even when reconnecting succeeds: a fresh handle is not
213
+ // evidence that queries work. Only the next successful probe promotes it back to
214
+ // Connected — drivers whose own connect() verifies the link ( mysql takes and releases
215
+ // a real connection ) set the state themselves.
216
+ await this.connect();
217
+ }
218
+ catch (err) {
219
+ this.Log.warn(`health check reconnect for ${this.Options.Name} failed: ${err.message}`);
220
+ }
221
+ }
222
+ /**
223
+ * Point-in-time pool state. The base implementation reports an empty pool; drivers that own a
224
+ * real pool override it. Must never throw — it runs on the health-check timer.
225
+ */
226
+ poolMetrics() {
227
+ return { Size: 0, InUse: 0, Waiting: 0 };
228
+ }
229
+ /**
230
+ * Pushes the current pool state and connection state to the shared `Metrics` registry from
231
+ * `@spinajs/telemetry-common`. Nothing has to be wired for this to work — `Metrics` owns a
232
+ * private registry and `@spinajs/telemetry`'s `/metrics` endpoint renders that same singleton.
233
+ */
234
+ publishPoolMetrics() {
235
+ try {
236
+ const metrics = ormMetrics();
237
+ if (!metrics) {
238
+ return;
239
+ }
240
+ const labels = { connection: this.Options.Name };
241
+ const pool = this.poolMetrics();
242
+ ormGauge(metrics, ORM_METRIC_KEY_POOL_SIZE).set(labels, pool.Size);
243
+ ormGauge(metrics, ORM_METRIC_KEY_POOL_IN_USE).set(labels, pool.InUse);
244
+ ormGauge(metrics, ORM_METRIC_KEY_POOL_WAITING).set(labels, pool.Waiting);
245
+ ormGauge(metrics, ORM_METRIC_KEY_CONNECTION_STATE).set(labels, this.State === ConnectionState.Connected ? 1 : 0);
246
+ }
247
+ catch (err) {
248
+ // Telemetry must never break a connection. This runs first on every health tick, so a
249
+ // registry that throws — or a driver built outside DI, as tooling and tests do — would
250
+ // otherwise stop the probe that follows it from ever running.
251
+ this.Log?.trace(`publishing pool metrics for ${this.Options.Name} failed: ${err.message}`);
252
+ }
253
+ }
254
+ /**
255
+ * Records one pool-acquire wait, in SECONDS ( prometheus convention ). Drivers that own a real
256
+ * pool call this from the acquire callback; keeping the prom-client objects behind this method
257
+ * is what stops every driver from needing to know about `@spinajs/telemetry-common`. Never
258
+ * throws, for the same reason `publishPoolMetrics` never throws.
259
+ */
260
+ observeAcquireSeconds(seconds) {
261
+ try {
262
+ const metrics = ormMetrics();
263
+ if (!metrics) {
264
+ return;
265
+ }
266
+ ormHistogram(metrics, ORM_METRIC_KEY_ACQUIRE_SECONDS).observe({ connection: this.Options.Name }, seconds);
267
+ }
268
+ catch (err) {
269
+ this.Log?.trace(`observing pool acquire time for ${this.Options.Name} failed: ${err.message}`);
270
+ }
271
+ }
44
272
  /**
45
273
  * Creates select query builder associated with this connection.
46
274
  * This can be used to execute raw queries to db without orm model layer
@@ -91,6 +319,57 @@ export class OrmDriver extends SyncService {
91
319
  index() {
92
320
  return this.Container.resolve(IndexQueryBuilder, [this]);
93
321
  }
322
+ /**
323
+ * Runs `cb` inside a transaction and owns its whole lifecycle: commits when the callback
324
+ * resolves, rolls back when it throws, and releases the connection exactly once either way.
325
+ * Resolves with whatever the callback returned.
326
+ *
327
+ * Statements issued inside the callback run on this transaction's connection automatically —
328
+ * the context is carried through `AsyncLocalStorage`, so nothing has to be threaded through
329
+ * by hand.
330
+ *
331
+ * Calling it again while a transaction is already in scope on this async path does **not**
332
+ * open a second, independent transaction: it takes a savepoint, so a failing nested block
333
+ * rolls back only its own work.
334
+ *
335
+ * @param cb - the transaction body
336
+ * @param options - optional isolation level, validated against {@link SupportedIsolationLevels}
337
+ */
338
+ async transaction(cb, options) {
339
+ if (options?.isolation && !this.SupportedIsolationLevels.includes(options.isolation)) {
340
+ throw new OrmException(`isolation level ${options.isolation} not supported by driver ${this.Options.Driver}`);
341
+ }
342
+ const active = this.TransactionStorage.getStore();
343
+ // already inside a transaction — nest with a savepoint rather than opening a second one
344
+ if (active) {
345
+ const name = `sp_${++active.depth}`;
346
+ await this._savepoint(active, name);
347
+ try {
348
+ const result = await cb(this);
349
+ await this._releaseSavepoint(active, name);
350
+ return result;
351
+ }
352
+ catch (err) {
353
+ await this._rollbackToSavepoint(active, name);
354
+ throw err;
355
+ }
356
+ }
357
+ const ctx = await this._begin(options);
358
+ ctx.depth = 0;
359
+ try {
360
+ const result = await this.TransactionStorage.run(ctx, () => cb(this));
361
+ await this._commit(ctx);
362
+ return result;
363
+ }
364
+ catch (err) {
365
+ // swallow a rollback failure so it cannot mask whatever actually went wrong
366
+ await this._rollback(ctx).catch(() => undefined);
367
+ throw err;
368
+ }
369
+ finally {
370
+ await this._dispose(ctx);
371
+ }
372
+ }
94
373
  }
95
374
  __decorate([
96
375
  Autoinject(),
@@ -1 +1 @@
1
- {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/driver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { IRelationDescriptor, ISelectQueryBuilder, RelationType } from './interfaces.js';
2
+ import type { WhereBuilder } from './builders.js';
3
+ import { WhereFunction } from './types.js';
4
+ /**
5
+ * Strategy that builds the EXISTS / NOT EXISTS clause for one relation type.
6
+ *
7
+ * Resolved via `Array.ofType(ExistsRelationHandler)` and dispatched by `rel.Type` from
8
+ * `WhereBuilder.buildExistsClause`. Implementations either:
9
+ * - mutate the outer builder directly and return `undefined` (eg. {@link RelationType.One}
10
+ * adds `WHERE FK IS NOT NULL` plus a right-join, no EXISTS sub-query is generated), or
11
+ * - return a correlated sub-query that the caller wraps in EXISTS/NOT EXISTS.
12
+ */
13
+ export declare abstract class ExistsRelationHandler {
14
+ abstract get Type(): RelationType;
15
+ abstract apply<R>(builder: WhereBuilder<any>, rel: IRelationDescriptor, relationName: string, callback?: WhereFunction<R>): ISelectQueryBuilder | undefined;
16
+ }
17
+ export declare class OneExistsRelationHandler extends ExistsRelationHandler {
18
+ get Type(): RelationType;
19
+ apply<R>(builder: WhereBuilder<any>, rel: IRelationDescriptor, _relationName: string, callback?: WhereFunction<R>): undefined;
20
+ }
21
+ export declare class ManyExistsRelationHandler extends ExistsRelationHandler {
22
+ get Type(): RelationType;
23
+ apply<R>(builder: WhereBuilder<any>, rel: IRelationDescriptor, _relationName: string, callback?: WhereFunction<R>): ISelectQueryBuilder;
24
+ }
25
+ export declare class ManyToManyExistsRelationHandler extends ExistsRelationHandler {
26
+ get Type(): RelationType;
27
+ apply<R>(builder: WhereBuilder<any>, rel: IRelationDescriptor, _relationName: string, callback?: WhereFunction<R>): ISelectQueryBuilder;
28
+ }
29
+ //# sourceMappingURL=existsRelationHandlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"existsRelationHandlers.d.ts","sourceRoot":"","sources":["../../src/existsRelationHandlers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAgB,mBAAmB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEvG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;;GAQG;AACH,8BAAsB,qBAAqB;IACzC,aAAoB,IAAI,IAAI,YAAY,CAAC;aAEzB,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,mBAAmB,GAAG,SAAS;CACnK;AAYD,qBACa,wBAAyB,SAAQ,qBAAqB;IACjE,IAAW,IAAI,IAAI,YAAY,CAE9B;IAEM,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,SAAS;CAUrI;AAED,qBACa,yBAA0B,SAAQ,qBAAqB;IAClE,IAAW,IAAI,IAAI,YAAY,CAE9B;IAEM,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,mBAAmB;CAmB/I;AAED,qBACa,+BAAgC,SAAQ,qBAAqB;IACxE,IAAW,IAAI,IAAI,YAAY,CAE9B;IAEM,KAAK,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,mBAAmB;CAuB/I"}
@@ -0,0 +1,100 @@
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
+ /* eslint-disable prettier/prettier */
8
+ import { Injectable } from '@spinajs/di';
9
+ import { Lazy } from '@spinajs/util';
10
+ import { RelationType } from './interfaces.js';
11
+ import { RawQuery } from './builders.js';
12
+ /**
13
+ * Strategy that builds the EXISTS / NOT EXISTS clause for one relation type.
14
+ *
15
+ * Resolved via `Array.ofType(ExistsRelationHandler)` and dispatched by `rel.Type` from
16
+ * `WhereBuilder.buildExistsClause`. Implementations either:
17
+ * - mutate the outer builder directly and return `undefined` (eg. {@link RelationType.One}
18
+ * adds `WHERE FK IS NOT NULL` plus a right-join, no EXISTS sub-query is generated), or
19
+ * - return a correlated sub-query that the caller wraps in EXISTS/NOT EXISTS.
20
+ */
21
+ export class ExistsRelationHandler {
22
+ }
23
+ /**
24
+ * Resolves the correlated source primary key column reference, eg. `` `users`.`Id` ``.
25
+ * Falls back to the source descriptor's `TableName` when neither the builder nor its parent
26
+ * has an alias set, matching the legacy behaviour of `whereExist` / `whereNotExists`.
27
+ */
28
+ function sourcePKeyRef(builder, tDesc) {
29
+ const sourceAlias = builder.TableAlias ?? tDesc.TableName;
30
+ return `\`${sourceAlias}\`.\`${tDesc.PrimaryKey[0]}\``;
31
+ }
32
+ let OneExistsRelationHandler = class OneExistsRelationHandler extends ExistsRelationHandler {
33
+ get Type() {
34
+ return RelationType.One;
35
+ }
36
+ apply(builder, rel, _relationName, callback) {
37
+ builder.whereNotNull(rel.ForeignKey);
38
+ // simply use right join for condition check
39
+ if (callback) {
40
+ // TODO: cast fix
41
+ builder.rightJoin(rel.TargetModel, callback);
42
+ }
43
+ return undefined;
44
+ }
45
+ };
46
+ OneExistsRelationHandler = __decorate([
47
+ Injectable(ExistsRelationHandler)
48
+ ], OneExistsRelationHandler);
49
+ export { OneExistsRelationHandler };
50
+ let ManyExistsRelationHandler = class ManyExistsRelationHandler extends ExistsRelationHandler {
51
+ get Type() {
52
+ return RelationType.Many;
53
+ }
54
+ apply(builder, rel, _relationName, callback) {
55
+ const tDesc = builder.Model.getModelDescriptor();
56
+ const tableName = rel.TargetModel.getModelDescriptor().TableName;
57
+ // set alias to avoid conflicts in case of multiple relations to same model and to make
58
+ // sure that relation query is correct even if source query has alias
59
+ const relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
60
+ relQuery.where(Lazy.oF(function () {
61
+ relQuery.where(new RawQuery(`${rel.ForeignKey} = ${sourcePKeyRef(builder, tDesc)}`));
62
+ }));
63
+ if (callback) {
64
+ callback.apply(relQuery);
65
+ }
66
+ return relQuery;
67
+ }
68
+ };
69
+ ManyExistsRelationHandler = __decorate([
70
+ Injectable(ExistsRelationHandler)
71
+ ], ManyExistsRelationHandler);
72
+ export { ManyExistsRelationHandler };
73
+ let ManyToManyExistsRelationHandler = class ManyToManyExistsRelationHandler extends ExistsRelationHandler {
74
+ get Type() {
75
+ return RelationType.ManyToMany;
76
+ }
77
+ apply(builder, rel, _relationName, callback) {
78
+ const tDesc = builder.Model.getModelDescriptor();
79
+ const junctionModel = rel.JunctionModel;
80
+ const junctionTableName = junctionModel.getModelDescriptor().TableName;
81
+ const relQuery = junctionModel.query().setAlias(`${junctionTableName}_exists`);
82
+ relQuery.where(Lazy.oF(function () {
83
+ relQuery.where(new RawQuery(`${rel.JunctionModelSourceModelFKey_Name} = ${sourcePKeyRef(builder, tDesc)}`));
84
+ }));
85
+ if (callback) {
86
+ relQuery.rightJoin({
87
+ joinModel: rel.TargetModel,
88
+ joinTableForeignKey: rel.PrimaryKey,
89
+ sourceTablePrimaryKey: rel.JunctionModelTargetModelFKey_Name,
90
+ callback: callback,
91
+ });
92
+ }
93
+ return relQuery;
94
+ }
95
+ };
96
+ ManyToManyExistsRelationHandler = __decorate([
97
+ Injectable(ExistsRelationHandler)
98
+ ], ManyToManyExistsRelationHandler);
99
+ export { ManyToManyExistsRelationHandler };
100
+ //# sourceMappingURL=existsRelationHandlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"existsRelationHandlers.js","sourceRoot":"","sources":["../../src/existsRelationHandlers.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"fp.d.ts","sourceRoot":"","sources":["../../src/fp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAiB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,oBAAY,WAAW;IACrB,kBAAkB,IAAA;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,sBAY1G;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAUvF;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,SAAS,EAAE,SAAS,CAAC,EAAE,eAAe,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAuB9G;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,SAAS,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAU/E;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,SAAS,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAUvE"}
1
+ {"version":3,"file":"fp.d.ts","sourceRoot":"","sources":["../../src/fp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAiB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,oBAAY,WAAW;IACrB,kBAAkB,IAAA;CACnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,OAAO,sBAY1G;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,SAAS,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAMvF;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,SAAS,EAAE,SAAS,CAAC,EAAE,eAAe,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAyB9G;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,SAAS,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAK/E;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,SAAS,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAUvE"}
package/lib/mjs/fp.js CHANGED
@@ -32,12 +32,9 @@ export function _get_entity(idOrEntity, c, fresh) {
32
32
  */
33
33
  export function _update(data) {
34
34
  return (model) => {
35
- return model.update(data).then((res) => {
36
- if (res.LastInsertId <= 0 || res.RowsAffected <= 0) {
37
- return Promise.reject(new ErrorCode(E_ORM_CODES.E_NO_ROWS_AFFECTED));
38
- }
39
- return model;
40
- });
35
+ // A clean model short-circuits to { RowsAffected: 0 } - that is a no-op success,
36
+ // not a failure, so we always resolve the model.
37
+ return model.update(data).then(() => model);
41
38
  };
42
39
  }
43
40
  /**
@@ -53,14 +50,16 @@ export function _insert(behaviour) {
53
50
  return Promise.resolve(model);
54
51
  }
55
52
  return model[0].constructor.insert(model, behaviour).then((res) => {
56
- if (res.LastInsertId <= 0 || res.RowsAffected <= 0) {
53
+ // `uuid` and `assigned` primary keys report LastInsertId 0 on a successful insert, so
54
+ // only RowsAffected signals failure. See @Primary({ generated }) in the ORM docs.
55
+ if (res.RowsAffected <= 0) {
57
56
  return Promise.reject(new ErrorCode(E_ORM_CODES.E_NO_ROWS_AFFECTED));
58
57
  }
59
58
  return model;
60
59
  });
61
60
  }
62
61
  return model.insert(behaviour).then((res) => {
63
- if (res.LastInsertId <= 0 || res.RowsAffected <= 0) {
62
+ if (res.RowsAffected <= 0) {
64
63
  return Promise.reject(new ErrorCode(E_ORM_CODES.E_NO_ROWS_AFFECTED));
65
64
  }
66
65
  return model;
@@ -69,12 +68,8 @@ export function _insert(behaviour) {
69
68
  }
70
69
  export function _insertOrUpdate() {
71
70
  return (model) => {
72
- return model.insertOrUpdate().then((res) => {
73
- if (res.LastInsertId <= 0 || res.RowsAffected <= 0) {
74
- return Promise.reject(new ErrorCode(E_ORM_CODES.E_NO_ROWS_AFFECTED));
75
- }
76
- return model;
77
- });
71
+ // insertOrUpdate on a clean model is a no-op success (RowsAffected 0) - resolve the model.
72
+ return model.insertOrUpdate().then(() => model);
78
73
  };
79
74
  }
80
75
  /**
@@ -86,7 +81,7 @@ export function _insertOrUpdate() {
86
81
  export function _delete() {
87
82
  return (model) => {
88
83
  return model.destroy().then((res) => {
89
- if (res.LastInsertId <= 0 || res.RowsAffected <= 0) {
84
+ if (res.RowsAffected <= 0) {
90
85
  return Promise.reject(new ErrorCode(E_ORM_CODES.E_NO_ROWS_AFFECTED));
91
86
  }
92
87
  return model;
package/lib/mjs/fp.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fp.js","sourceRoot":"","sources":["../../src/fp.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fp.js","sourceRoot":"","sources":["../../src/fp.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"hydrators.d.ts","sourceRoot":"","sources":["../../src/hydrators.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,8BAAsB,aAAa;aACjB,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CACxD;AAED,qBACa,kBAAmB,SAAQ,aAAa;IAC5C,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CAuBrD;AAED,qBACa,qBAAsB,SAAQ,aAAa;IAC/C,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CAYrD;AAED,qBACa,yBAA0B,SAAQ,aAAa;IACnD,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CA2BrD;AAED,qBACa,wBAAyB,SAAQ,aAAa;IAClD,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CAyBrD;AAED,qBACa,6BAA8B,SAAQ,aAAa;IACvD,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CAarD"}
1
+ {"version":3,"file":"hydrators.d.ts","sourceRoot":"","sources":["../../src/hydrators.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIvC,8BAAsB,aAAa;aACjB,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CACxD;AAED,qBACa,kBAAmB,SAAQ,aAAa;IAC5C,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CAiCrD;AAED,qBACa,qBAAsB,SAAQ,aAAa;IAC/C,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CAYrD;AAED,qBACa,yBAA0B,SAAQ,aAAa;IACnD,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CA+BrD;AAED,qBACa,wBAAyB,SAAQ,aAAa;IAClD,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CAuCrD;AAED,qBACa,6BAA8B,SAAQ,aAAa;IACvD,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;CAarD"}
@@ -6,6 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  /* eslint-disable prettier/prettier */
8
8
  import { RelationType } from './interfaces.js';
9
+ import { ModelBase } from './model.js';
9
10
  import { Injectable, isConstructor } from '@spinajs/di';
10
11
  import { OneToManyRelationList, SingleRelation } from './relation-objects.js';
11
12
  export class ModelHydrator {
@@ -22,12 +23,22 @@ let DbPropertyHydrator = class DbPropertyHydrator extends ModelHydrator {
22
23
  keys.forEach((k) => {
23
24
  // skip if column is primary key & is null
24
25
  // we dont want to override pkey of target model
25
- if (k === descriptor.PrimaryKey && !values[k]) {
26
+ if (descriptor.PrimaryKey.includes(k) && (values[k] === null || values[k] === undefined)) {
26
27
  return;
27
28
  }
28
29
  const column = descriptor.Columns?.find((c) => c.Name === k);
29
30
  if (values[k] !== undefined) {
30
- target[k] = column?.Converter ? column.Converter.fromDB(values[k], values, descriptor.Converters.get(column.Name)?.Options) : values[k];
31
+ const incoming = values[k];
32
+ // A relation FK column may receive either a raw scalar id or an
33
+ // already-resolved model instance (eg. from a DTO @Relation field).
34
+ // When a model instance arrives, translate it to its primary key value
35
+ // so the FK column stores the id, not the object.
36
+ if (incoming instanceof ModelBase) {
37
+ target[k] = incoming.PrimaryKeyValue;
38
+ }
39
+ else {
40
+ target[k] = column?.Converter ? column.Converter.fromDB(incoming, values, descriptor.Converters.get(column.Name)?.Options) : incoming;
41
+ }
31
42
  }
32
43
  });
33
44
  }
@@ -72,7 +83,11 @@ let OneToManyRelationHydrator = class OneToManyRelationHydrator extends ModelHyd
72
83
  return tEntity;
73
84
  });
74
85
  const rel = new OneToManyRelationList(target, val, mapRel);
86
+ // Only reached under `values[key] != null`, i.e. only when the row actually carried
87
+ // relation data — so this never marks a relation the query did not ask for.
88
+ rel.Populated = true;
75
89
  entity[key] = rel;
90
+ target.snapshotRelation(key);
76
91
  delete target[val.ForeignKey];
77
92
  }
78
93
  }
@@ -94,6 +109,14 @@ let OneToOneRelationHydrator = class OneToOneRelationHydrator extends ModelHydra
94
109
  }
95
110
  if (values[key] != null) {
96
111
  const entity = target;
112
+ // Accept an already-resolved model instance passed under the relation
113
+ // name: attach it directly and translate its PK into the FK column,
114
+ // instead of trying to re-hydrate it from a plain data object.
115
+ if (values[key] instanceof ModelBase) {
116
+ entity[key] = new SingleRelation(target, val.TargetModel, val, values[key]);
117
+ target[val.ForeignKey] = values[key].PrimaryKeyValue;
118
+ continue;
119
+ }
97
120
  let tEntity = undefined;
98
121
  if (!Object.values(values[key]).every((x) => x === null)) {
99
122
  tEntity = !isConstructor(val.TargetModel) ? new (val.TargetModel())() : new val.TargetModel();
@@ -101,7 +124,11 @@ let OneToOneRelationHydrator = class OneToOneRelationHydrator extends ModelHydra
101
124
  tEntity['__relationKey__'] = key;
102
125
  }
103
126
  const rel = new SingleRelation(target, val.TargetModel, val, tEntity);
127
+ // Same guard as OneToManyRelationHydrator: `values[key] != null` means the query
128
+ // joined this relation in.
129
+ rel.Populated = true;
104
130
  entity[key] = rel;
131
+ target.snapshotRelation(key);
105
132
  }
106
133
  }
107
134
  }