@spinajs/orm 2.0.480 → 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
@@ -29,6 +29,7 @@ const relations_js_1 = require("./relations.js");
29
29
  const util_1 = require("@spinajs/util");
30
30
  const discrimination_middleware_js_1 = require("./discrimination-middleware.js");
31
31
  const descriptor_js_1 = require("./descriptor.js");
32
+ const existsRelationHandlers_js_1 = require("./existsRelationHandlers.js");
32
33
  function isWhereOperator(val) {
33
34
  return lodash_1.default.isString(val) && Object.values(enums_js_1.SqlOperator).includes(val.toLowerCase());
34
35
  }
@@ -45,6 +46,12 @@ let Builder = class Builder {
45
46
  constructor(container, driver, model) {
46
47
  this._middlewares = [];
47
48
  this._queryMiddlewares = [];
49
+ /**
50
+ * Memoized result of {@link execute}. A builder executes at most once; awaiting
51
+ * it again resolves with the same value instead of re-running the query.
52
+ * Call `clone()` first if you genuinely need a second round-trip.
53
+ */
54
+ this._executionPromise = null;
48
55
  this._driver = driver;
49
56
  this._container = container;
50
57
  this._model = model;
@@ -52,82 +59,97 @@ let Builder = class Builder {
52
59
  this._asRaw = false;
53
60
  this._queryMiddlewares = di_1.DI.resolve(Array.ofType(interfaces_js_1.QueryMiddleware));
54
61
  }
55
- then(onfulfilled, onrejected) {
56
- return this._driver
57
- .execute(this)
58
- .then((result) => {
59
- try {
60
- if (this._asRaw) {
61
- onfulfilled?.(result);
62
- return;
63
- }
64
- let transformedResult = result;
65
- // if we have something to transform ...
66
- if (transformedResult) {
67
- this._middlewares.forEach((m) => {
68
- Object.assign(transformedResult, m.afterQuery(transformedResult));
69
- });
70
- }
71
- if (this._model && !this._nonSelect) {
72
- // TODO: rething this casting
73
- const models = transformedResult.map((r) => {
74
- let model = null;
75
- for (const middleware of this._middlewares.reverse()) {
76
- model = middleware.modelCreation(r);
77
- if (model !== null) {
78
- break;
79
- }
80
- }
81
- if (model === null) {
82
- model = di_1.DI.resolve('__orm_model_factory__', [this._model]);
83
- }
84
- model.hydrate(r);
85
- model.IsDirty = false;
86
- return model;
87
- });
88
- const afterMiddlewarePromises = this._middlewares.reduce((prev, current) => {
89
- return prev.concat([current.afterHydration(models)]);
90
- }, []);
91
- if (this._middlewares.length > 0) {
92
- Promise.all(afterMiddlewarePromises).then(() => {
93
- try {
94
- onfulfilled?.(models);
95
- }
96
- catch (err) {
97
- if (onrejected) {
98
- onrejected(err);
99
- }
100
- else {
101
- throw err;
102
- }
103
- }
104
- }, onrejected);
105
- }
106
- else {
107
- onfulfilled?.(models);
62
+ /**
63
+ * The execution engine. Sends the compiled query to the driver, applies the
64
+ * result middlewares, hydrates models and awaits the post-hydration middlewares.
65
+ *
66
+ * Always *returns* its value — the caller's promise chain is what propagates it.
67
+ * Subclasses override this (not `execute()`) so that their extra work also lands
68
+ * inside the memo.
69
+ */
70
+ async _run() {
71
+ // Fires for EVERY builder type, not just SELECT. This used to live on
72
+ // `SelectQueryBuilder._run`, so an INSERT/UPDATE/DELETE never reached a
73
+ // `beforeQueryExecution` hook at all — which silently disabled any middleware that
74
+ // needs the finished query (rbac's insert-ownership check is exactly that: the payload
75
+ // does not exist until `values()` has been called, long after construction).
76
+ this._queryMiddlewares.forEach((x) => x.beforeQueryExecution(this));
77
+ const result = (await this._driver.execute(this));
78
+ // Snapshot the pipeline once, *after* the driver call: compiling the query is what
79
+ // registers the relation middlewares (the driver calls `toDB()`), so this is the first
80
+ // point at which the list is complete. Everything below runs against this immutable copy,
81
+ // so a middleware registered later cannot change the pipeline mid-flight, and — crucially —
82
+ // we never call `Array.prototype.reverse()` on `this._middlewares`, which mutates in place
83
+ // and used to flip `modelCreation` resolution order on every execution ( B8 ).
84
+ const middlewares = [...this._middlewares];
85
+ const creationOrder = [...middlewares].reverse();
86
+ if (this._asRaw) {
87
+ return result;
88
+ }
89
+ const transformedResult = result;
90
+ // if we have something to transform ...
91
+ if (transformedResult) {
92
+ middlewares.forEach((m) => {
93
+ Object.assign(transformedResult, m.afterQuery(transformedResult));
94
+ });
95
+ }
96
+ if (this._model && !this._nonSelect) {
97
+ // TODO: rething this casting
98
+ const models = transformedResult.map((r) => {
99
+ let model = null;
100
+ for (const middleware of creationOrder) {
101
+ model = middleware.modelCreation(r);
102
+ if (model !== null) {
103
+ break;
108
104
  }
109
105
  }
110
- else {
111
- onfulfilled?.(transformedResult);
106
+ if (model === null) {
107
+ model = di_1.DI.resolve('__orm_model_factory__', [this._model]);
112
108
  }
113
- }
114
- catch (err) {
115
- if (onrejected) {
116
- onrejected(err);
117
- }
118
- else {
119
- throw err;
109
+ model.hydrate(r);
110
+ model.IsDirty = false;
111
+ // This is the one point at which the instance's columns hold exactly what the
112
+ // database returned, so it is the diff baseline for `save()`. Relation members
113
+ // are attached later by the afterHydration middlewares below, which record their
114
+ // own member keys into this same snapshot.
115
+ model.takeSnapshot();
116
+ // Nested relation data that arrived on the row itself ( belongsTo LEFT JOIN,
117
+ // and hasMany arrays passed straight to hydrate ) was attached by the hydrators
118
+ // inside `hydrate()` above — i.e. before the snapshot existed, so their own
119
+ // `snapshotRelation` calls no-opped. Record those relations now.
120
+ for (const name of (model.ModelDescriptor?.Relations ?? new Map()).keys()) {
121
+ if (model[name]?.Populated) {
122
+ model.snapshotRelation(name);
123
+ }
120
124
  }
125
+ return model;
126
+ });
127
+ if (middlewares.length > 0) {
128
+ await Promise.all(middlewares.map((m) => m.afterHydration(models)));
121
129
  }
122
- })
123
- .catch((err) => {
124
- if (onrejected) {
125
- onrejected(err);
126
- }
127
- else {
128
- throw err;
129
- }
130
- });
130
+ return models;
131
+ }
132
+ return transformedResult;
133
+ }
134
+ /**
135
+ * Executes the query. The single entry point for execution — `then()` delegates here.
136
+ * The underlying work runs exactly once per builder instance; subsequent calls resolve
137
+ * with the memoized result.
138
+ */
139
+ execute() {
140
+ if (!this._executionPromise) {
141
+ this._executionPromise = this._run();
142
+ }
143
+ return this._executionPromise;
144
+ }
145
+ then(onfulfilled, onrejected) {
146
+ return this.execute().then(onfulfilled, onrejected);
147
+ }
148
+ catch(onrejected) {
149
+ return this.execute().catch(onrejected);
150
+ }
151
+ finally(onfinally) {
152
+ return this.execute().finally(onfinally);
131
153
  }
132
154
  middleware(middleware) {
133
155
  this._middlewares.push(middleware);
@@ -199,7 +221,7 @@ let QueryBuilder = class QueryBuilder extends Builder {
199
221
  *
200
222
  */
201
223
  setTable(table, alias) {
202
- if (!table.trim()) {
224
+ if ((0, util_1.isNullOrWhitespace)(table)) {
203
225
  throw new exceptions_1.InvalidArgument('table name is empty');
204
226
  }
205
227
  this._table = table;
@@ -236,9 +258,7 @@ let LimitBuilder = class LimitBuilder {
236
258
  };
237
259
  }
238
260
  take(count) {
239
- if (count <= 0) {
240
- throw new exceptions_1.InvalidArgument(`take count cannot be negative number`);
241
- }
261
+ (0, util_1._check_arg)((0, util_1._positive)())(count, 'take count');
242
262
  this._limit.limit = count;
243
263
  return this;
244
264
  }
@@ -264,7 +284,7 @@ let LimitBuilder = class LimitBuilder {
264
284
  const result = (await this);
265
285
  if (result === undefined || (Array.isArray(result) && result.length === 0)) {
266
286
  if (typeof error === 'function') {
267
- error = error(this.toDB());
287
+ throw error(this.toDB());
268
288
  }
269
289
  else
270
290
  throw error;
@@ -293,10 +313,7 @@ exports.LimitBuilder = LimitBuilder = __decorate([
293
313
  ], LimitBuilder);
294
314
  let OrderByBuilder = class OrderByBuilder {
295
315
  constructor() {
296
- this._sort = {
297
- column: '',
298
- order: enums_js_1.SortOrder.ASC,
299
- };
316
+ this._sorts = [];
300
317
  }
301
318
  order(column, direction) {
302
319
  if (!column) {
@@ -309,10 +326,10 @@ let OrderByBuilder = class OrderByBuilder {
309
326
  });
310
327
  return this;
311
328
  }
312
- this._sort = {
329
+ this._sorts.push({
313
330
  column,
314
331
  order: direction,
315
- };
332
+ });
316
333
  return this;
317
334
  }
318
335
  orderBy(column) {
@@ -323,10 +340,10 @@ let OrderByBuilder = class OrderByBuilder {
323
340
  });
324
341
  return this;
325
342
  }
326
- this._sort = {
343
+ this._sorts.push({
327
344
  column,
328
345
  order: enums_js_1.SortOrder.ASC,
329
- };
346
+ });
330
347
  return this;
331
348
  }
332
349
  orderByDescending(column) {
@@ -337,14 +354,25 @@ let OrderByBuilder = class OrderByBuilder {
337
354
  });
338
355
  return this;
339
356
  }
340
- this._sort = {
357
+ this._sorts.push({
341
358
  column,
342
359
  order: enums_js_1.SortOrder.DESC,
343
- };
360
+ });
344
361
  return this;
345
362
  }
363
+ /**
364
+ * Returns the FIRST sort entry (or null) for backward compat with dialect
365
+ * packages that emit a single ORDER BY column. Use getSorts() for all entries.
366
+ */
346
367
  getSort() {
347
- return this._sort.column.trim() !== '' ? this._sort : null;
368
+ const sort = this._sorts.find((s) => s.column.trim() !== '');
369
+ return sort ?? null;
370
+ }
371
+ /**
372
+ * Returns all sort entries (multi-column ORDER BY), skipping empty columns.
373
+ */
374
+ getSorts() {
375
+ return this._sorts.filter((s) => s.column.trim() !== '');
348
376
  }
349
377
  };
350
378
  exports.OrderByBuilder = OrderByBuilder;
@@ -556,6 +584,23 @@ let WithRecursiveBuilder = class WithRecursiveBuilder {
556
584
  this._cteStatement = this._container.resolve(statements_js_1.WithRecursiveStatement, ['cte', this, rcKeyName, pkName]);
557
585
  return this;
558
586
  }
587
+ /**
588
+ * Drops the recursive CTE from this builder.
589
+ *
590
+ * `WithRecursiveStatement.build()` compiles two CLONES of the owning query — the anchor
591
+ * member and the recursive member of the CTE. `clone()` copies `_cteStatement`, so each
592
+ * clone was still marked recursive and compiling it re-entered `build()`, which cloned
593
+ * again: an unbounded mutual recursion between `toDB()` and the recursive compiler that
594
+ * ended in a stack overflow rather than a query. The two member queries are by definition
595
+ * not themselves recursive, so the statement clears the flag on its clones.
596
+ *
597
+ * Named alongside `clearJoins()` / `clearWhere()`, which `build()` already uses to strip the
598
+ * parts of the parent query each member must not inherit.
599
+ */
600
+ clearRecursive() {
601
+ this._cteStatement = undefined;
602
+ return this;
603
+ }
559
604
  };
560
605
  exports.WithRecursiveBuilder = WithRecursiveBuilder;
561
606
  exports.WithRecursiveBuilder = WithRecursiveBuilder = __decorate([
@@ -590,12 +635,31 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
590
635
  clone(_parent) {
591
636
  // TODO: fix this cast
592
637
  const builder = new WhereBuilder_1(_parent);
593
- builder._statements = this._statements.map((s) => s.clone(builder));
638
+ builder._statements = this._statements.map((s) => {
639
+ const cloned = s.clone(builder);
640
+ // preserve the per-statement boolean connector (clone() rebuilds the
641
+ // statement from scratch and would otherwise reset it to the AND default)
642
+ cloned.Boolean = s.Boolean;
643
+ return cloned;
644
+ });
594
645
  builder._boolean = this._boolean;
595
646
  builder._model = this._model;
596
647
  builder._tableAlias = this.TableAlias;
597
648
  return builder;
598
649
  }
650
+ /**
651
+ * Pushes a statement onto this builder, stamping it with the currently pending
652
+ * boolean connector (set by {@link orWhere}/{@link andWhere}). The pending
653
+ * connector applies to the NEXT pushed statement only and resets to AND
654
+ * afterwards, so `where(a).where(b).orWhere(c).where(d)` compiles to
655
+ * `a AND b OR c AND d` rather than rewriting the whole clause.
656
+ */
657
+ pushStatement(statement) {
658
+ statement.Boolean = this._boolean;
659
+ this._statements.push(statement);
660
+ this._boolean = enums_js_1.WhereBoolean.AND;
661
+ return this;
662
+ }
599
663
  when(condition, callback, callbackElse) {
600
664
  if (condition) {
601
665
  if (callback)
@@ -640,18 +704,18 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
640
704
  return this.where(RawQuery.create(column ? 'TRUE' : 'FALSE'));
641
705
  }
642
706
  if (column instanceof RawQuery) {
643
- this.Statements.push(this._container.resolve(statements_js_1.RawQueryStatement, [column.Query, column.Bindings, self.TableAlias]));
707
+ this.pushStatement(this._container.resolve(statements_js_1.RawQueryStatement, [column.Query, column.Bindings, self.TableAlias]));
644
708
  return this;
645
709
  }
646
710
  // handle nested where's
647
711
  if (lodash_1.default.isFunction(column)) {
648
712
  const builder = new WhereBuilder_1(this);
649
713
  column.call(builder);
650
- self.Statements.push(this._container.resolve(statements_js_1.WhereQueryStatement, [builder, self.TableAlias]));
714
+ self.pushStatement(this._container.resolve(statements_js_1.WhereQueryStatement, [builder, self.TableAlias]));
651
715
  return this;
652
716
  }
653
717
  if (column instanceof util_1.Lazy) {
654
- this.Statements.push(this._container.resolve(statements_js_1.LazyQueryStatement, [column, this]));
718
+ this.pushStatement(this._container.resolve(statements_js_1.LazyQueryStatement, [column, this]));
655
719
  return this;
656
720
  }
657
721
  // handle simple key = object[key] AND ....
@@ -680,7 +744,7 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
680
744
  if (sVal === null) {
681
745
  return this.whereNull(c);
682
746
  }
683
- self._statements.push(self._container.resolve(statements_js_1.WhereStatement, [c, enums_js_1.SqlOperator.EQ, sVal, this]));
747
+ self.pushStatement(self._container.resolve(statements_js_1.WhereStatement, [c, enums_js_1.SqlOperator.EQ, sVal, this]));
684
748
  return self;
685
749
  }
686
750
  /**
@@ -699,12 +763,17 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
699
763
  throw new exceptions_1.InvalidArgument(`value cannot be NaN`);
700
764
  }
701
765
  if (sVal === null) {
702
- return this.whereNull(c);
703
- }
704
- if (sVal === null) {
705
- return o === enums_js_1.SqlOperator.NOT_NULL ? this.whereNotNull(c) : this.whereNull(c);
766
+ const op = String(o).toLowerCase();
767
+ // where(col, '=', null) => IS NULL ; where(col, '!=' / '<>', null) => IS NOT NULL
768
+ if (op === enums_js_1.SqlOperator.EQ) {
769
+ return this.whereNull(c);
770
+ }
771
+ if (op === enums_js_1.SqlOperator.NOT || op === enums_js_1.SqlOperator.NOT_2) {
772
+ return this.whereNotNull(c);
773
+ }
774
+ throw new exceptions_1.InvalidArgument(`operator ${o} cannot be used with null value ( only =, !=, <> are allowed )`);
706
775
  }
707
- self._statements.push(self._container.resolve(statements_js_1.WhereStatement, [c, o, sVal, self]));
776
+ self.pushStatement(self._container.resolve(statements_js_1.WhereStatement, [c, o, sVal, self]));
708
777
  return this;
709
778
  }
710
779
  }
@@ -721,9 +790,9 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
721
790
  for (const key of Object.keys(obj).filter((x) => obj[x] !== undefined)) {
722
791
  const val = obj[key];
723
792
  if (Array.isArray(val)) {
724
- if (val.length !== 0) {
725
- this.whereIn(key, val);
726
- }
793
+ // empty array => SQL `IN ()` semantics => match nothing (FALSE),
794
+ // never "no condition" (which would match everything)
795
+ this.whereIn(key, val);
727
796
  }
728
797
  else if (val === null) {
729
798
  this.whereNull(key);
@@ -734,165 +803,82 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
734
803
  return this;
735
804
  }
736
805
  whereNotNull(column) {
737
- this._statements.push(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NOT_NULL, null, this]));
806
+ this.pushStatement(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NOT_NULL, null, this]));
738
807
  return this;
739
808
  }
740
809
  whereNull(column) {
741
- this._statements.push(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NULL, null, this]));
810
+ this.pushStatement(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NULL, null, this]));
742
811
  return this;
743
812
  }
744
813
  whereNot(column, val) {
745
814
  return this.where(column, enums_js_1.SqlOperator.NOT, val);
746
815
  }
747
816
  whereIn(column, val) {
748
- this._statements.push(this._container.resolve(statements_js_1.InStatement, [column, val, false, this]));
817
+ // `IN ()` matches nothing in SQL; compile an empty set to FALSE rather than
818
+ // emitting no condition (which would silently match every row).
819
+ if (Array.isArray(val) && val.length === 0) {
820
+ this.where(false);
821
+ return this;
822
+ }
823
+ this.pushStatement(this._container.resolve(statements_js_1.InStatement, [column, val, false, this]));
749
824
  return this;
750
825
  }
751
826
  whereNotIn(column, val) {
752
- this._statements.push(this._container.resolve(statements_js_1.InStatement, [column, val, true, this]));
827
+ this.pushStatement(this._container.resolve(statements_js_1.InStatement, [column, val, true, this]));
753
828
  return this;
754
829
  }
755
830
  whereExist(query, callback) {
756
- // TODO: refactor and remove code duplication with whereNotExists
757
- // TODO: move relation handling to separate DI service for every exists relation type
758
- let relQuery;
759
- let sourcePKey = '';
760
- const self = this;
761
- let tableName = '';
762
- let tDesc;
763
- if (typeof query === 'string') {
764
- const rel = this._model.getRelationDescriptor(query);
765
- if (!rel) {
766
- throw new exceptions_js_1.OrmException(`relation ${query} not found in model ${this.constructor.name}`);
767
- }
768
- switch (rel.Type) {
769
- case interfaces_js_1.RelationType.One:
770
- this.whereNotNull(rel.ForeignKey);
771
- // simply use right join for condition check
772
- if (callback) {
773
- // TODO: cast fix
774
- this.rightJoin(rel.TargetModel, callback.bind(query));
775
- }
776
- break;
777
- case interfaces_js_1.RelationType.Many:
778
- tableName = rel.TargetModel.getModelDescriptor().TableName;
779
- tDesc = self._model.getModelDescriptor();
780
- // set alias to avoid conflicts in case of multiple relations to same model and to make sure that relation query is correct even if source query has alias
781
- relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
782
- relQuery.where(util_1.Lazy.oF(function () {
783
- const sourceAlias = self._tableAlias ?? (self._parent ? self._parent.TableAlias : tDesc.TableName);
784
- sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
785
- // relQuery is guaranteed assigned above before this lazy callback executes
786
- relQuery.where(new RawQuery(`${rel.ForeignKey} = ${sourcePKey}`));
787
- }));
788
- if (callback) {
789
- callback.apply(relQuery);
790
- }
791
- this.whereExist(relQuery);
792
- break;
793
- case interfaces_js_1.RelationType.ManyToMany:
794
- relQuery = rel.JunctionModel.query();
795
- tableName = rel.TargetModel.getModelDescriptor().TableName;
796
- tDesc = self._model.getModelDescriptor();
797
- relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
798
- relQuery.where(util_1.Lazy.oF(function () {
799
- const sourceAlias = self._tableAlias ?? (self._parent ? self._parent.TableAlias : tDesc.TableName);
800
- sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
801
- // relQuery is guaranteed assigned above before this lazy callback executes
802
- relQuery.where(new RawQuery(`${rel.JunctionModelSourceModelFKey_Name} = ${sourcePKey}`));
803
- }));
804
- this.setAlias();
805
- relQuery.rightJoin({
806
- joinModel: rel.TargetModel,
807
- joinTableForeignKey: rel.ForeignKey,
808
- sourceTablePrimaryKey: rel.JunctionModelTargetModelFKey_Name,
809
- callback: callback,
810
- });
811
- this.whereExist(relQuery);
812
- break;
813
- }
814
- }
815
- else {
816
- this._statements.push(this._container.resolve(statements_js_1.ExistsQueryStatement, [query, false]));
817
- }
818
- return this;
831
+ return this.buildExistsClause(query, false, callback);
819
832
  }
820
833
  whereNotExists(query, callback) {
821
- let relQuery;
822
- let sourcePKey = '';
823
- const self = this;
824
- let tableName = '';
825
- let tDesc;
826
- if (typeof query === 'string') {
827
- const rel = this._model.getRelationDescriptor(query);
828
- if (!rel) {
829
- throw new exceptions_js_1.OrmException(`relation ${query} not found in model ${this.constructor.name}`);
830
- }
831
- switch (rel.Type) {
832
- case interfaces_js_1.RelationType.One:
833
- this.whereNotNull(rel.ForeignKey);
834
- // simply use right join for condition check
835
- if (callback) {
836
- // TODO: cast fix
837
- this.rightJoin(rel.TargetModel, callback.bind(query));
838
- }
839
- break;
840
- case interfaces_js_1.RelationType.Many:
841
- tableName = rel.TargetModel.getModelDescriptor().TableName;
842
- tDesc = self._model.getModelDescriptor();
843
- // set alias to avoid conflicts in case of multiple relations to same model and to make sure that relation query is correct even if source query has alias
844
- relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
845
- relQuery.where(util_1.Lazy.oF(function () {
846
- const sourceAlias = self._tableAlias || (self._parent ? self._parent.TableAlias : tDesc.TableName);
847
- sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
848
- // relQuery is guaranteed assigned above before this lazy callback executes
849
- relQuery.where(new RawQuery(`${rel.ForeignKey} = ${sourcePKey}`));
850
- }));
851
- if (callback) {
852
- callback.apply(relQuery);
853
- }
854
- this.whereNotExists(relQuery);
855
- break;
856
- case interfaces_js_1.RelationType.ManyToMany:
857
- relQuery = rel.JunctionModel.query();
858
- tableName = rel.TargetModel.getModelDescriptor().TableName;
859
- tDesc = self._model.getModelDescriptor();
860
- relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
861
- relQuery.where(util_1.Lazy.oF(function () {
862
- const sourceAlias = self._tableAlias || (self._parent ? self._parent.TableAlias : tDesc.TableName);
863
- sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
864
- // relQuery is guaranteed assigned above before this lazy callback executes
865
- relQuery.where(new RawQuery(`${rel.JunctionModelSourceModelFKey_Name} = ${sourcePKey}`));
866
- }));
867
- relQuery.rightJoin({
868
- joinModel: rel.TargetModel,
869
- joinTableForeignKey: rel.PrimaryKey,
870
- sourceTablePrimaryKey: rel.ForeignKey,
871
- callback: callback,
872
- });
873
- this.whereNotExists(relQuery);
874
- break;
875
- }
834
+ return this.buildExistsClause(query, true, callback);
835
+ }
836
+ /**
837
+ * Shared implementation for {@link whereExist} / {@link whereNotExists}.
838
+ *
839
+ * For a ready sub-query it pushes an {@link ExistsQueryStatement} directly. For a relation
840
+ * name it resolves the matching {@link ExistsRelationHandler} from the container (one per
841
+ * {@link RelationType}) and lets it either mutate this builder or return a correlated
842
+ * sub-query that we then wrap in EXISTS / NOT EXISTS.
843
+ *
844
+ * @param query relation name or a ready sub-query
845
+ * @param negated `true` for NOT EXISTS, `false` for EXISTS
846
+ * @param callback optional where-callback applied to the relation sub-query
847
+ */
848
+ buildExistsClause(query, negated, callback) {
849
+ if (typeof query !== 'string') {
850
+ this.pushStatement(this._container.resolve(statements_js_1.ExistsQueryStatement, [query, negated]));
851
+ return this;
876
852
  }
877
- else {
878
- this._statements.push(this._container.resolve(statements_js_1.ExistsQueryStatement, [query, true]));
853
+ const rel = this._model.getRelationDescriptor(query);
854
+ if (!rel) {
855
+ throw new exceptions_js_1.OrmException(`relation ${query} not found in model ${this.constructor.name}`);
856
+ }
857
+ const handlers = this._container.resolve(Array.ofType(existsRelationHandlers_js_1.ExistsRelationHandler));
858
+ const handler = handlers.find((h) => h.Type === rel.Type);
859
+ if (!handler) {
860
+ throw new exceptions_js_1.OrmException(`no ExistsRelationHandler registered for relation type ${rel.Type} (relation ${query} on ${this.constructor.name})`);
861
+ }
862
+ const subquery = handler.apply(this, rel, query, callback);
863
+ if (subquery) {
864
+ this.pushStatement(this._container.resolve(statements_js_1.ExistsQueryStatement, [subquery, negated]));
879
865
  }
880
866
  return this;
881
867
  }
882
868
  whereBetween(column, val) {
883
- this._statements.push(this._container.resolve(statements_js_1.BetweenStatement, [column, val, false, this.TableAlias]));
869
+ this.pushStatement(this._container.resolve(statements_js_1.BetweenStatement, [column, val, false, this.TableAlias]));
884
870
  return this;
885
871
  }
886
872
  whereNotBetween(column, val) {
887
- this._statements.push(this._container.resolve(statements_js_1.BetweenStatement, [column, val, true, this.TableAlias]));
873
+ this.pushStatement(this._container.resolve(statements_js_1.BetweenStatement, [column, val, true, this.TableAlias]));
888
874
  return this;
889
875
  }
890
876
  whereInSet(column, val) {
891
- this._statements.push(this._container.resolve(statements_js_1.InSetStatement, [column, val, false, this.TableAlias]));
877
+ this.pushStatement(this._container.resolve(statements_js_1.InSetStatement, [column, val, false, this.TableAlias]));
892
878
  return this;
893
879
  }
894
880
  whereNotInSet(column, val) {
895
- this._statements.push(this._container.resolve(statements_js_1.InSetStatement, [column, val, true, this.TableAlias]));
881
+ this.pushStatement(this._container.resolve(statements_js_1.InSetStatement, [column, val, true, this.TableAlias]));
896
882
  return this;
897
883
  }
898
884
  clearWhere() {
@@ -932,10 +918,7 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
932
918
  this._distinct = false;
933
919
  this._method = enums_js_1.QueryMethod.SELECT;
934
920
  this._boolean = enums_js_1.WhereBoolean.AND;
935
- this._sort = {
936
- column: '',
937
- order: enums_js_1.SortOrder.NONE,
938
- };
921
+ this._sorts = [];
939
922
  this._first = false;
940
923
  this._limit = {
941
924
  limit: -1,
@@ -950,13 +933,12 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
950
933
  return (await this);
951
934
  }
952
935
  setAlias(alias) {
953
- if (!alias || alias.trim() === '') {
954
- alias = `${this._driver.Options.AliasSeparator}${this._table}${this._driver.Options.AliasSeparator}`;
955
- }
956
- this._tableAlias = alias;
957
- this._columns.forEach((c) => (c.TableAlias = alias));
958
- this._joinStatements.forEach((c) => (c.TableAlias = alias));
959
- this._statements.forEach((c) => (c.TableAlias = alias));
936
+ // isNullOrWhitespace is not a type predicate; a false result guarantees a non-blank string
937
+ const resolvedAlias = (0, util_1.isNullOrWhitespace)(alias) ? `${this._driver.Options.AliasSeparator}${this._table}${this._driver.Options.AliasSeparator}` : alias;
938
+ this._tableAlias = resolvedAlias;
939
+ this._columns.forEach((c) => (c.TableAlias = resolvedAlias));
940
+ this._joinStatements.forEach((c) => (c.TableAlias = resolvedAlias));
941
+ this._statements.forEach((c) => (c.TableAlias = resolvedAlias));
960
942
  return this;
961
943
  }
962
944
  clone() {
@@ -967,11 +949,18 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
967
949
  builder._joinStatements = this._joinStatements.map(c => c.clone(builder));
968
950
  // Clone statements with mapped WhereBuilder references
969
951
  builder._statements = this._statements.map(c => c.clone(builder));
952
+ // Clone group-by statements (previously dropped, silently losing GROUP BY
953
+ // on a cloned query — eg. orm-api count-pagination clones the builder).
954
+ builder._groupStatements = this._groupStatements.map(c => c.clone(builder));
955
+ // Carry relations and result middlewares over. These hold live objects and
956
+ // are shared the same way mergeRelations()/mergeBuilder() already share them.
957
+ builder._relations = [...this._relations];
958
+ builder._middlewares = [...this._middlewares];
970
959
  /**
971
960
  * ------------------------------------------------------------------
972
961
  */
973
962
  builder._limit = { ...this._limit };
974
- builder._sort = { ...this._sort };
963
+ builder._sorts = this._sorts.map((s) => ({ ...s }));
975
964
  builder._boolean = this._boolean;
976
965
  builder._distinct = this._distinct;
977
966
  builder._table = this._table;
@@ -1070,15 +1059,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
1070
1059
  this._getRelationInstance(relation).execute(callback);
1071
1060
  return this;
1072
1061
  }
1073
- mergeBuilder(builder) {
1062
+ mergeBuilder(builder, includeStatements = true) {
1074
1063
  this._columns = this._columns.concat(builder._columns);
1075
1064
  this._cteStatement = builder._cteStatement;
1076
1065
  this._distinct = builder._distinct;
1077
- this._sort = {
1078
- column: builder._sort.column !== '' ? builder._sort.column : this._sort.column,
1079
- order: builder._sort.order !== '' ? builder._sort.order : this._sort.order,
1080
- };
1081
- this.mergeStatements(builder);
1066
+ // Fold the merged builder's sorts into this query's sorts (multi-column
1067
+ // ORDER BY). If the merged builder has no sorts, this keeps our own.
1068
+ this._sorts = this._sorts.concat(builder._sorts.map((s) => ({ ...s })));
1069
+ // `includeStatements: false` is used by JoinStatement so that a join
1070
+ // callback's WHERE conditions are NOT folded into the main query's WHERE
1071
+ // (which silently turns a LEFT JOIN into an inner filter). The join emits
1072
+ // those conditions in its own ON clause instead. Columns/sort are still
1073
+ // merged so join-callback selects (e.g. extra joined columns) keep working.
1074
+ if (includeStatements) {
1075
+ this.mergeStatements(builder);
1076
+ }
1082
1077
  }
1083
1078
  mergeRelations(builder) {
1084
1079
  this._relations = this._relations.concat(builder._relations);
@@ -1089,6 +1084,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
1089
1084
  this._joinStatements = this._joinStatements.concat(builder._joinStatements);
1090
1085
  this._statements = this._statements.concat(stms);
1091
1086
  }
1087
+ /**
1088
+ * Includes soft-deleted rows in the result set by removing the default
1089
+ * `DeletedAt IS NULL` filter added by createQuery for @SoftDelete models.
1090
+ */
1091
+ withDeleted() {
1092
+ const descriptor = (0, descriptor_js_1.extractModelDescriptor)(this._model);
1093
+ const deletedAt = descriptor?.SoftDelete?.DeletedAt;
1094
+ if (!deletedAt) {
1095
+ return this;
1096
+ }
1097
+ this._statements = this._statements.filter((s) => {
1098
+ return !(s instanceof statements_js_1.WhereStatement && s.Column === deletedAt && s.Operator === enums_js_1.SqlOperator.NULL);
1099
+ });
1100
+ return this;
1101
+ }
1092
1102
  min(column, as) {
1093
1103
  this._columns.push(this._container.resolve(statements_js_1.ColumnMethodStatement, [column, enums_js_1.ColumnMethods.MIN, as, this._tableAlias]));
1094
1104
  return this;
@@ -1130,44 +1140,26 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
1130
1140
  return compiler.compile();
1131
1141
  }
1132
1142
  async all() {
1133
- return await this;
1143
+ return await this.execute();
1134
1144
  }
1135
1145
  async resultExists() {
1136
- return this.then((res) => {
1137
- if (Array.isArray(res)) {
1138
- return res.length > 0;
1139
- }
1140
- return res !== undefined && res !== null;
1141
- });
1142
- }
1143
- then(onfulfilled, onrejected) {
1144
- this._queryMiddlewares.forEach((x) => x.beforeQueryExecution(this));
1145
- return super.then((result) => {
1146
- if (this._first) {
1147
- if (Array.isArray(result)) {
1148
- if (result.length !== 0) {
1149
- return onfulfilled?.(result[0]);
1150
- }
1151
- else {
1152
- try {
1153
- return onfulfilled?.(undefined);
1154
- }
1155
- catch (err) {
1156
- onrejected?.(err);
1157
- }
1158
- }
1159
- }
1160
- else {
1161
- return onfulfilled?.(result);
1162
- }
1163
- }
1164
- else {
1165
- return onfulfilled?.(result);
1166
- }
1167
- }, onrejected);
1146
+ const res = await this.execute();
1147
+ if (Array.isArray(res)) {
1148
+ return res.length > 0;
1149
+ }
1150
+ return res !== undefined && res !== null;
1168
1151
  }
1169
- async execute() {
1170
- return (await this);
1152
+ /**
1153
+ * Overrides the engine rather than `execute()` so that the `takeFirst()` unwrapping
1154
+ * happens *inside* the memo. `beforeQueryExecution` is dispatched by `Builder._run()`
1155
+ * for every builder type, so it still fires once per builder rather than once per await.
1156
+ */
1157
+ async _run() {
1158
+ const result = await super._run();
1159
+ if (this._first && Array.isArray(result)) {
1160
+ return (result.length !== 0 ? result[0] : undefined);
1161
+ }
1162
+ return result;
1171
1163
  }
1172
1164
  };
1173
1165
  exports.SelectQueryBuilder = SelectQueryBuilder;
@@ -1193,6 +1185,10 @@ let DeleteQueryBuilder = class DeleteQueryBuilder extends QueryBuilder {
1193
1185
  offset: -1,
1194
1186
  };
1195
1187
  this.QueryContext = interfaces_js_1.QueryContext.Delete;
1188
+ // Query middlewares (e.g. rbac ownership enforcement) must run for deletes
1189
+ // too, not only selects — otherwise :own permission constraints are never
1190
+ // applied and any row can be deleted.
1191
+ this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
1196
1192
  }
1197
1193
  toDB() {
1198
1194
  return this._container.resolve(interfaces_js_1.DeleteQueryCompiler, [this]).compile();
@@ -1232,6 +1228,9 @@ let OnDuplicateQueryBuilder = class OnDuplicateQueryBuilder {
1232
1228
  this._columnsToUpdate = columns;
1233
1229
  return this;
1234
1230
  }
1231
+ execute() {
1232
+ return this._parent.execute();
1233
+ }
1235
1234
  then(onfulfilled, onrejected) {
1236
1235
  return this._parent.then(onfulfilled, onrejected);
1237
1236
  }
@@ -1255,6 +1254,10 @@ let UpdateQueryBuilder = class UpdateQueryBuilder extends QueryBuilder {
1255
1254
  this._boolean = enums_js_1.WhereBoolean.AND;
1256
1255
  this._statements = [];
1257
1256
  this.QueryContext = interfaces_js_1.QueryContext.Update;
1257
+ // Query middlewares (e.g. rbac ownership enforcement) must run for updates
1258
+ // too, not only selects — otherwise :own permission constraints are never
1259
+ // applied and any row can be updated.
1260
+ this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
1258
1261
  }
1259
1262
  in(name) {
1260
1263
  this.setTable(name);
@@ -1290,6 +1293,10 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
1290
1293
  get Replace() {
1291
1294
  return this._replace;
1292
1295
  }
1296
+ /** Columns requested via {@link returning}. Empty when no RETURNING clause was asked for. */
1297
+ get Returning() {
1298
+ return this._returning ?? [];
1299
+ }
1293
1300
  constructor(container, driver, model) {
1294
1301
  super(container, driver, model);
1295
1302
  this._update = false;
@@ -1298,6 +1305,33 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
1298
1305
  this._columns = [];
1299
1306
  this._values = [];
1300
1307
  this.QueryContext = interfaces_js_1.QueryContext.Insert;
1308
+ // Inserts get the same middleware pass as the other three builders. Note that a
1309
+ // middleware which needs to see or amend the row payload must use
1310
+ // `beforeQueryExecution` instead — at construction time `values()` has not been called.
1311
+ this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
1312
+ }
1313
+ /**
1314
+ * Forces `column` to `value` on every row of the payload, overwriting whatever the caller
1315
+ * supplied.
1316
+ *
1317
+ * This is the write path for a policy that must not be negotiable — rbac's `createOwn`
1318
+ * ownership stamp. `values()` cannot serve: it appends rows and takes the column list from
1319
+ * its own argument, so calling it a second time adds a row rather than amending the
1320
+ * existing ones.
1321
+ *
1322
+ * @param column - column to overwrite on every row
1323
+ * @param value - value to force
1324
+ */
1325
+ forceColumn(column, value) {
1326
+ const existing = this._columns.findIndex((c) => !(c.Column instanceof RawQuery) && c.Column === column);
1327
+ if (existing === -1) {
1328
+ this.columns([...this._columns.filter((c) => !(c.Column instanceof RawQuery)).map((c) => c.Column), column]);
1329
+ this._values.forEach((row) => row.push(value));
1330
+ return this;
1331
+ }
1332
+ // eslint-disable-next-line security/detect-object-injection
1333
+ this._values.forEach((row) => (row[existing] = value));
1334
+ return this;
1301
1335
  }
1302
1336
  /**
1303
1337
  * Sets insert to ignore on duplicate
@@ -1310,8 +1344,21 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
1310
1344
  this._replace = true;
1311
1345
  return this;
1312
1346
  }
1347
+ /**
1348
+ * Asks the dialect to echo the given columns of every inserted row back.
1349
+ *
1350
+ * @throws NotSupported on drivers whose `supportedFeatures().insertReturning` is false —
1351
+ * silently doing nothing is how this API was a no-op on MySQL and MSSQL for years.
1352
+ */
1313
1353
  returning(columns) {
1354
+ if (!this.Driver.supportedFeatures().insertReturning) {
1355
+ throw new exceptions_1.NotSupported(`driver ${this.Driver.Options.Driver} does not support RETURNING on INSERT`);
1356
+ }
1314
1357
  this._returning = columns;
1358
+ // onDuplicate() sets Upsert unconditionally and wins if it runs afterwards.
1359
+ if (this.QueryContext === interfaces_js_1.QueryContext.Insert) {
1360
+ this.QueryContext = interfaces_js_1.QueryContext.InsertReturning;
1361
+ }
1315
1362
  return this;
1316
1363
  }
1317
1364
  values(data) {
@@ -1473,6 +1520,7 @@ let ColumnQueryBuilder = class ColumnQueryBuilder {
1473
1520
  this.Comment = '';
1474
1521
  this.Unique = false;
1475
1522
  this.Unsigned = false;
1523
+ this.InlinePrimaryKey = true;
1476
1524
  this.Args.push(...args);
1477
1525
  }
1478
1526
  notNull() {
@@ -1643,10 +1691,11 @@ exports.AlterTableQueryBuilder = AlterTableQueryBuilder = __decorate([
1643
1691
  ], AlterTableQueryBuilder);
1644
1692
  let TableQueryBuilder = class TableQueryBuilder extends QueryBuilder {
1645
1693
  /**
1646
- * Alias for string(name, 36 )
1694
+ * Alias for binary(name, 16 ) - uuids are stored as 16-byte BINARY to match
1695
+ * the UuidConverter ( which writes a dashed uuid as a 16-byte buffer ).
1647
1696
  */
1648
1697
  uuid(name) {
1649
- return this.string(name, 36);
1698
+ return this.binary(name, 16);
1650
1699
  }
1651
1700
  ifExists() {
1652
1701
  this._checkExists = true;
@@ -2053,6 +2102,16 @@ function createQuery(model, query, injectModel = true) {
2053
2102
  }
2054
2103
  qr.middleware(new discrimination_middleware_js_1.DiscriminationMapMiddleware(dsc));
2055
2104
  qr.setTable(dsc.TableName);
2105
+ // Soft-delete read filtering: by default exclude rows that have been soft
2106
+ // deleted (DeletedAt IS NOT NULL). SelectQueryBuilder.withDeleted() removes
2107
+ // this default statement to include soft-deleted rows again.
2108
+ // Guarded on the DeletedAt column actually being present in the model's
2109
+ // reflected columns — a filter on a column the schema does not expose is
2110
+ // impossible anyway, and the guard keeps queries working when table info
2111
+ // has not (yet) surfaced the column.
2112
+ if (qr instanceof SelectQueryBuilder && dsc.SoftDelete?.DeletedAt && dsc.Columns?.some((c) => c.Name === dsc.SoftDelete.DeletedAt)) {
2113
+ qr.whereNull(dsc.SoftDelete.DeletedAt);
2114
+ }
2056
2115
  if (driver.Options.Database) {
2057
2116
  qr.database(driver.Options.Database);
2058
2117
  }