@spinajs/orm 2.0.481 → 2.0.484

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 +127 -11
  5. package/lib/cjs/builders.d.ts.map +1 -1
  6. package/lib/cjs/builders.js +368 -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 +138 -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 +300 -10
  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 +127 -11
  117. package/lib/mjs/builders.d.ts.map +1 -1
  118. package/lib/mjs/builders.js +370 -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 +134 -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 +300 -10
  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 +10 -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,104 @@ 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
+ //
77
+ // Awaited in sequence, not `forEach`ed: a hook that needs a database round-trip to
78
+ // decide an rbac rule whose ownership lives in another table — could otherwise only
79
+ // return a promise into the void, and the query would execute anyway. For a security
80
+ // check that is the difference between enforcing and pretending to.
81
+ for (const middleware of this._queryMiddlewares) {
82
+ await middleware.beforeQueryExecution(this);
83
+ }
84
+ const result = (await this._driver.execute(this));
85
+ // Snapshot the pipeline once, *after* the driver call: compiling the query is what
86
+ // registers the relation middlewares (the driver calls `toDB()`), so this is the first
87
+ // point at which the list is complete. Everything below runs against this immutable copy,
88
+ // so a middleware registered later cannot change the pipeline mid-flight, and — crucially —
89
+ // we never call `Array.prototype.reverse()` on `this._middlewares`, which mutates in place
90
+ // and used to flip `modelCreation` resolution order on every execution ( B8 ).
91
+ const middlewares = [...this._middlewares];
92
+ const creationOrder = [...middlewares].reverse();
93
+ if (this._asRaw) {
94
+ return result;
95
+ }
96
+ const transformedResult = result;
97
+ // if we have something to transform ...
98
+ if (transformedResult) {
99
+ middlewares.forEach((m) => {
100
+ Object.assign(transformedResult, m.afterQuery(transformedResult));
101
+ });
102
+ }
103
+ if (this._model && !this._nonSelect) {
104
+ // TODO: rething this casting
105
+ const models = transformedResult.map((r) => {
106
+ let model = null;
107
+ for (const middleware of creationOrder) {
108
+ model = middleware.modelCreation(r);
109
+ if (model !== null) {
110
+ break;
108
111
  }
109
112
  }
110
- else {
111
- onfulfilled?.(transformedResult);
113
+ if (model === null) {
114
+ model = di_1.DI.resolve('__orm_model_factory__', [this._model]);
112
115
  }
113
- }
114
- catch (err) {
115
- if (onrejected) {
116
- onrejected(err);
117
- }
118
- else {
119
- throw err;
116
+ model.hydrate(r);
117
+ model.IsDirty = false;
118
+ // This is the one point at which the instance's columns hold exactly what the
119
+ // database returned, so it is the diff baseline for `save()`. Relation members
120
+ // are attached later by the afterHydration middlewares below, which record their
121
+ // own member keys into this same snapshot.
122
+ model.takeSnapshot();
123
+ // Nested relation data that arrived on the row itself ( belongsTo LEFT JOIN,
124
+ // and hasMany arrays passed straight to hydrate ) was attached by the hydrators
125
+ // inside `hydrate()` above — i.e. before the snapshot existed, so their own
126
+ // `snapshotRelation` calls no-opped. Record those relations now.
127
+ for (const name of (model.ModelDescriptor?.Relations ?? new Map()).keys()) {
128
+ if (model[name]?.Populated) {
129
+ model.snapshotRelation(name);
130
+ }
120
131
  }
132
+ return model;
133
+ });
134
+ if (middlewares.length > 0) {
135
+ await Promise.all(middlewares.map((m) => m.afterHydration(models)));
121
136
  }
122
- })
123
- .catch((err) => {
124
- if (onrejected) {
125
- onrejected(err);
126
- }
127
- else {
128
- throw err;
129
- }
130
- });
137
+ return models;
138
+ }
139
+ return transformedResult;
140
+ }
141
+ /**
142
+ * Executes the query. The single entry point for execution — `then()` delegates here.
143
+ * The underlying work runs exactly once per builder instance; subsequent calls resolve
144
+ * with the memoized result.
145
+ */
146
+ execute() {
147
+ if (!this._executionPromise) {
148
+ this._executionPromise = this._run();
149
+ }
150
+ return this._executionPromise;
151
+ }
152
+ then(onfulfilled, onrejected) {
153
+ return this.execute().then(onfulfilled, onrejected);
154
+ }
155
+ catch(onrejected) {
156
+ return this.execute().catch(onrejected);
157
+ }
158
+ finally(onfinally) {
159
+ return this.execute().finally(onfinally);
131
160
  }
132
161
  middleware(middleware) {
133
162
  this._middlewares.push(middleware);
@@ -199,7 +228,7 @@ let QueryBuilder = class QueryBuilder extends Builder {
199
228
  *
200
229
  */
201
230
  setTable(table, alias) {
202
- if (!table.trim()) {
231
+ if ((0, util_1.isNullOrWhitespace)(table)) {
203
232
  throw new exceptions_1.InvalidArgument('table name is empty');
204
233
  }
205
234
  this._table = table;
@@ -236,9 +265,7 @@ let LimitBuilder = class LimitBuilder {
236
265
  };
237
266
  }
238
267
  take(count) {
239
- if (count <= 0) {
240
- throw new exceptions_1.InvalidArgument(`take count cannot be negative number`);
241
- }
268
+ (0, util_1._check_arg)((0, util_1._positive)())(count, 'take count');
242
269
  this._limit.limit = count;
243
270
  return this;
244
271
  }
@@ -264,7 +291,7 @@ let LimitBuilder = class LimitBuilder {
264
291
  const result = (await this);
265
292
  if (result === undefined || (Array.isArray(result) && result.length === 0)) {
266
293
  if (typeof error === 'function') {
267
- error = error(this.toDB());
294
+ throw error(this.toDB());
268
295
  }
269
296
  else
270
297
  throw error;
@@ -293,10 +320,7 @@ exports.LimitBuilder = LimitBuilder = __decorate([
293
320
  ], LimitBuilder);
294
321
  let OrderByBuilder = class OrderByBuilder {
295
322
  constructor() {
296
- this._sort = {
297
- column: '',
298
- order: enums_js_1.SortOrder.ASC,
299
- };
323
+ this._sorts = [];
300
324
  }
301
325
  order(column, direction) {
302
326
  if (!column) {
@@ -309,10 +333,10 @@ let OrderByBuilder = class OrderByBuilder {
309
333
  });
310
334
  return this;
311
335
  }
312
- this._sort = {
336
+ this._sorts.push({
313
337
  column,
314
338
  order: direction,
315
- };
339
+ });
316
340
  return this;
317
341
  }
318
342
  orderBy(column) {
@@ -323,10 +347,10 @@ let OrderByBuilder = class OrderByBuilder {
323
347
  });
324
348
  return this;
325
349
  }
326
- this._sort = {
350
+ this._sorts.push({
327
351
  column,
328
352
  order: enums_js_1.SortOrder.ASC,
329
- };
353
+ });
330
354
  return this;
331
355
  }
332
356
  orderByDescending(column) {
@@ -337,14 +361,25 @@ let OrderByBuilder = class OrderByBuilder {
337
361
  });
338
362
  return this;
339
363
  }
340
- this._sort = {
364
+ this._sorts.push({
341
365
  column,
342
366
  order: enums_js_1.SortOrder.DESC,
343
- };
367
+ });
344
368
  return this;
345
369
  }
370
+ /**
371
+ * Returns the FIRST sort entry (or null) for backward compat with dialect
372
+ * packages that emit a single ORDER BY column. Use getSorts() for all entries.
373
+ */
346
374
  getSort() {
347
- return this._sort.column.trim() !== '' ? this._sort : null;
375
+ const sort = this._sorts.find((s) => s.column.trim() !== '');
376
+ return sort ?? null;
377
+ }
378
+ /**
379
+ * Returns all sort entries (multi-column ORDER BY), skipping empty columns.
380
+ */
381
+ getSorts() {
382
+ return this._sorts.filter((s) => s.column.trim() !== '');
348
383
  }
349
384
  };
350
385
  exports.OrderByBuilder = OrderByBuilder;
@@ -556,6 +591,23 @@ let WithRecursiveBuilder = class WithRecursiveBuilder {
556
591
  this._cteStatement = this._container.resolve(statements_js_1.WithRecursiveStatement, ['cte', this, rcKeyName, pkName]);
557
592
  return this;
558
593
  }
594
+ /**
595
+ * Drops the recursive CTE from this builder.
596
+ *
597
+ * `WithRecursiveStatement.build()` compiles two CLONES of the owning query — the anchor
598
+ * member and the recursive member of the CTE. `clone()` copies `_cteStatement`, so each
599
+ * clone was still marked recursive and compiling it re-entered `build()`, which cloned
600
+ * again: an unbounded mutual recursion between `toDB()` and the recursive compiler that
601
+ * ended in a stack overflow rather than a query. The two member queries are by definition
602
+ * not themselves recursive, so the statement clears the flag on its clones.
603
+ *
604
+ * Named alongside `clearJoins()` / `clearWhere()`, which `build()` already uses to strip the
605
+ * parts of the parent query each member must not inherit.
606
+ */
607
+ clearRecursive() {
608
+ this._cteStatement = undefined;
609
+ return this;
610
+ }
559
611
  };
560
612
  exports.WithRecursiveBuilder = WithRecursiveBuilder;
561
613
  exports.WithRecursiveBuilder = WithRecursiveBuilder = __decorate([
@@ -590,12 +642,31 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
590
642
  clone(_parent) {
591
643
  // TODO: fix this cast
592
644
  const builder = new WhereBuilder_1(_parent);
593
- builder._statements = this._statements.map((s) => s.clone(builder));
645
+ builder._statements = this._statements.map((s) => {
646
+ const cloned = s.clone(builder);
647
+ // preserve the per-statement boolean connector (clone() rebuilds the
648
+ // statement from scratch and would otherwise reset it to the AND default)
649
+ cloned.Boolean = s.Boolean;
650
+ return cloned;
651
+ });
594
652
  builder._boolean = this._boolean;
595
653
  builder._model = this._model;
596
654
  builder._tableAlias = this.TableAlias;
597
655
  return builder;
598
656
  }
657
+ /**
658
+ * Pushes a statement onto this builder, stamping it with the currently pending
659
+ * boolean connector (set by {@link orWhere}/{@link andWhere}). The pending
660
+ * connector applies to the NEXT pushed statement only and resets to AND
661
+ * afterwards, so `where(a).where(b).orWhere(c).where(d)` compiles to
662
+ * `a AND b OR c AND d` rather than rewriting the whole clause.
663
+ */
664
+ pushStatement(statement) {
665
+ statement.Boolean = this._boolean;
666
+ this._statements.push(statement);
667
+ this._boolean = enums_js_1.WhereBoolean.AND;
668
+ return this;
669
+ }
599
670
  when(condition, callback, callbackElse) {
600
671
  if (condition) {
601
672
  if (callback)
@@ -640,18 +711,18 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
640
711
  return this.where(RawQuery.create(column ? 'TRUE' : 'FALSE'));
641
712
  }
642
713
  if (column instanceof RawQuery) {
643
- this.Statements.push(this._container.resolve(statements_js_1.RawQueryStatement, [column.Query, column.Bindings, self.TableAlias]));
714
+ this.pushStatement(this._container.resolve(statements_js_1.RawQueryStatement, [column.Query, column.Bindings, self.TableAlias]));
644
715
  return this;
645
716
  }
646
717
  // handle nested where's
647
718
  if (lodash_1.default.isFunction(column)) {
648
719
  const builder = new WhereBuilder_1(this);
649
720
  column.call(builder);
650
- self.Statements.push(this._container.resolve(statements_js_1.WhereQueryStatement, [builder, self.TableAlias]));
721
+ self.pushStatement(this._container.resolve(statements_js_1.WhereQueryStatement, [builder, self.TableAlias]));
651
722
  return this;
652
723
  }
653
724
  if (column instanceof util_1.Lazy) {
654
- this.Statements.push(this._container.resolve(statements_js_1.LazyQueryStatement, [column, this]));
725
+ this.pushStatement(this._container.resolve(statements_js_1.LazyQueryStatement, [column, this]));
655
726
  return this;
656
727
  }
657
728
  // handle simple key = object[key] AND ....
@@ -680,7 +751,7 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
680
751
  if (sVal === null) {
681
752
  return this.whereNull(c);
682
753
  }
683
- self._statements.push(self._container.resolve(statements_js_1.WhereStatement, [c, enums_js_1.SqlOperator.EQ, sVal, this]));
754
+ self.pushStatement(self._container.resolve(statements_js_1.WhereStatement, [c, enums_js_1.SqlOperator.EQ, sVal, this]));
684
755
  return self;
685
756
  }
686
757
  /**
@@ -699,12 +770,17 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
699
770
  throw new exceptions_1.InvalidArgument(`value cannot be NaN`);
700
771
  }
701
772
  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);
773
+ const op = String(o).toLowerCase();
774
+ // where(col, '=', null) => IS NULL ; where(col, '!=' / '<>', null) => IS NOT NULL
775
+ if (op === enums_js_1.SqlOperator.EQ) {
776
+ return this.whereNull(c);
777
+ }
778
+ if (op === enums_js_1.SqlOperator.NOT || op === enums_js_1.SqlOperator.NOT_2) {
779
+ return this.whereNotNull(c);
780
+ }
781
+ throw new exceptions_1.InvalidArgument(`operator ${o} cannot be used with null value ( only =, !=, <> are allowed )`);
706
782
  }
707
- self._statements.push(self._container.resolve(statements_js_1.WhereStatement, [c, o, sVal, self]));
783
+ self.pushStatement(self._container.resolve(statements_js_1.WhereStatement, [c, o, sVal, self]));
708
784
  return this;
709
785
  }
710
786
  }
@@ -721,9 +797,9 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
721
797
  for (const key of Object.keys(obj).filter((x) => obj[x] !== undefined)) {
722
798
  const val = obj[key];
723
799
  if (Array.isArray(val)) {
724
- if (val.length !== 0) {
725
- this.whereIn(key, val);
726
- }
800
+ // empty array => SQL `IN ()` semantics => match nothing (FALSE),
801
+ // never "no condition" (which would match everything)
802
+ this.whereIn(key, val);
727
803
  }
728
804
  else if (val === null) {
729
805
  this.whereNull(key);
@@ -734,165 +810,82 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
734
810
  return this;
735
811
  }
736
812
  whereNotNull(column) {
737
- this._statements.push(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NOT_NULL, null, this]));
813
+ this.pushStatement(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NOT_NULL, null, this]));
738
814
  return this;
739
815
  }
740
816
  whereNull(column) {
741
- this._statements.push(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NULL, null, this]));
817
+ this.pushStatement(this._container.resolve(statements_js_1.WhereStatement, [column, enums_js_1.SqlOperator.NULL, null, this]));
742
818
  return this;
743
819
  }
744
820
  whereNot(column, val) {
745
821
  return this.where(column, enums_js_1.SqlOperator.NOT, val);
746
822
  }
747
823
  whereIn(column, val) {
748
- this._statements.push(this._container.resolve(statements_js_1.InStatement, [column, val, false, this]));
824
+ // `IN ()` matches nothing in SQL; compile an empty set to FALSE rather than
825
+ // emitting no condition (which would silently match every row).
826
+ if (Array.isArray(val) && val.length === 0) {
827
+ this.where(false);
828
+ return this;
829
+ }
830
+ this.pushStatement(this._container.resolve(statements_js_1.InStatement, [column, val, false, this]));
749
831
  return this;
750
832
  }
751
833
  whereNotIn(column, val) {
752
- this._statements.push(this._container.resolve(statements_js_1.InStatement, [column, val, true, this]));
834
+ this.pushStatement(this._container.resolve(statements_js_1.InStatement, [column, val, true, this]));
753
835
  return this;
754
836
  }
755
837
  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;
838
+ return this.buildExistsClause(query, false, callback);
819
839
  }
820
840
  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
- }
841
+ return this.buildExistsClause(query, true, callback);
842
+ }
843
+ /**
844
+ * Shared implementation for {@link whereExist} / {@link whereNotExists}.
845
+ *
846
+ * For a ready sub-query it pushes an {@link ExistsQueryStatement} directly. For a relation
847
+ * name it resolves the matching {@link ExistsRelationHandler} from the container (one per
848
+ * {@link RelationType}) and lets it either mutate this builder or return a correlated
849
+ * sub-query that we then wrap in EXISTS / NOT EXISTS.
850
+ *
851
+ * @param query relation name or a ready sub-query
852
+ * @param negated `true` for NOT EXISTS, `false` for EXISTS
853
+ * @param callback optional where-callback applied to the relation sub-query
854
+ */
855
+ buildExistsClause(query, negated, callback) {
856
+ if (typeof query !== 'string') {
857
+ this.pushStatement(this._container.resolve(statements_js_1.ExistsQueryStatement, [query, negated]));
858
+ return this;
876
859
  }
877
- else {
878
- this._statements.push(this._container.resolve(statements_js_1.ExistsQueryStatement, [query, true]));
860
+ const rel = this._model.getRelationDescriptor(query);
861
+ if (!rel) {
862
+ throw new exceptions_js_1.OrmException(`relation ${query} not found in model ${this.constructor.name}`);
863
+ }
864
+ const handlers = this._container.resolve(Array.ofType(existsRelationHandlers_js_1.ExistsRelationHandler));
865
+ const handler = handlers.find((h) => h.Type === rel.Type);
866
+ if (!handler) {
867
+ throw new exceptions_js_1.OrmException(`no ExistsRelationHandler registered for relation type ${rel.Type} (relation ${query} on ${this.constructor.name})`);
868
+ }
869
+ const subquery = handler.apply(this, rel, query, callback);
870
+ if (subquery) {
871
+ this.pushStatement(this._container.resolve(statements_js_1.ExistsQueryStatement, [subquery, negated]));
879
872
  }
880
873
  return this;
881
874
  }
882
875
  whereBetween(column, val) {
883
- this._statements.push(this._container.resolve(statements_js_1.BetweenStatement, [column, val, false, this.TableAlias]));
876
+ this.pushStatement(this._container.resolve(statements_js_1.BetweenStatement, [column, val, false, this.TableAlias]));
884
877
  return this;
885
878
  }
886
879
  whereNotBetween(column, val) {
887
- this._statements.push(this._container.resolve(statements_js_1.BetweenStatement, [column, val, true, this.TableAlias]));
880
+ this.pushStatement(this._container.resolve(statements_js_1.BetweenStatement, [column, val, true, this.TableAlias]));
888
881
  return this;
889
882
  }
890
883
  whereInSet(column, val) {
891
- this._statements.push(this._container.resolve(statements_js_1.InSetStatement, [column, val, false, this.TableAlias]));
884
+ this.pushStatement(this._container.resolve(statements_js_1.InSetStatement, [column, val, false, this.TableAlias]));
892
885
  return this;
893
886
  }
894
887
  whereNotInSet(column, val) {
895
- this._statements.push(this._container.resolve(statements_js_1.InSetStatement, [column, val, true, this.TableAlias]));
888
+ this.pushStatement(this._container.resolve(statements_js_1.InSetStatement, [column, val, true, this.TableAlias]));
896
889
  return this;
897
890
  }
898
891
  clearWhere() {
@@ -932,10 +925,7 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
932
925
  this._distinct = false;
933
926
  this._method = enums_js_1.QueryMethod.SELECT;
934
927
  this._boolean = enums_js_1.WhereBoolean.AND;
935
- this._sort = {
936
- column: '',
937
- order: enums_js_1.SortOrder.NONE,
938
- };
928
+ this._sorts = [];
939
929
  this._first = false;
940
930
  this._limit = {
941
931
  limit: -1,
@@ -950,13 +940,12 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
950
940
  return (await this);
951
941
  }
952
942
  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));
943
+ // isNullOrWhitespace is not a type predicate; a false result guarantees a non-blank string
944
+ const resolvedAlias = (0, util_1.isNullOrWhitespace)(alias) ? `${this._driver.Options.AliasSeparator}${this._table}${this._driver.Options.AliasSeparator}` : alias;
945
+ this._tableAlias = resolvedAlias;
946
+ this._columns.forEach((c) => (c.TableAlias = resolvedAlias));
947
+ this._joinStatements.forEach((c) => (c.TableAlias = resolvedAlias));
948
+ this._statements.forEach((c) => (c.TableAlias = resolvedAlias));
960
949
  return this;
961
950
  }
962
951
  clone() {
@@ -967,11 +956,18 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
967
956
  builder._joinStatements = this._joinStatements.map(c => c.clone(builder));
968
957
  // Clone statements with mapped WhereBuilder references
969
958
  builder._statements = this._statements.map(c => c.clone(builder));
959
+ // Clone group-by statements (previously dropped, silently losing GROUP BY
960
+ // on a cloned query — eg. orm-api count-pagination clones the builder).
961
+ builder._groupStatements = this._groupStatements.map(c => c.clone(builder));
962
+ // Carry relations and result middlewares over. These hold live objects and
963
+ // are shared the same way mergeRelations()/mergeBuilder() already share them.
964
+ builder._relations = [...this._relations];
965
+ builder._middlewares = [...this._middlewares];
970
966
  /**
971
967
  * ------------------------------------------------------------------
972
968
  */
973
969
  builder._limit = { ...this._limit };
974
- builder._sort = { ...this._sort };
970
+ builder._sorts = this._sorts.map((s) => ({ ...s }));
975
971
  builder._boolean = this._boolean;
976
972
  builder._distinct = this._distinct;
977
973
  builder._table = this._table;
@@ -1070,15 +1066,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
1070
1066
  this._getRelationInstance(relation).execute(callback);
1071
1067
  return this;
1072
1068
  }
1073
- mergeBuilder(builder) {
1069
+ mergeBuilder(builder, includeStatements = true) {
1074
1070
  this._columns = this._columns.concat(builder._columns);
1075
1071
  this._cteStatement = builder._cteStatement;
1076
1072
  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);
1073
+ // Fold the merged builder's sorts into this query's sorts (multi-column
1074
+ // ORDER BY). If the merged builder has no sorts, this keeps our own.
1075
+ this._sorts = this._sorts.concat(builder._sorts.map((s) => ({ ...s })));
1076
+ // `includeStatements: false` is used by JoinStatement so that a join
1077
+ // callback's WHERE conditions are NOT folded into the main query's WHERE
1078
+ // (which silently turns a LEFT JOIN into an inner filter). The join emits
1079
+ // those conditions in its own ON clause instead. Columns/sort are still
1080
+ // merged so join-callback selects (e.g. extra joined columns) keep working.
1081
+ if (includeStatements) {
1082
+ this.mergeStatements(builder);
1083
+ }
1082
1084
  }
1083
1085
  mergeRelations(builder) {
1084
1086
  this._relations = this._relations.concat(builder._relations);
@@ -1089,6 +1091,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
1089
1091
  this._joinStatements = this._joinStatements.concat(builder._joinStatements);
1090
1092
  this._statements = this._statements.concat(stms);
1091
1093
  }
1094
+ /**
1095
+ * Includes soft-deleted rows in the result set by removing the default
1096
+ * `DeletedAt IS NULL` filter added by createQuery for @SoftDelete models.
1097
+ */
1098
+ withDeleted() {
1099
+ const descriptor = (0, descriptor_js_1.extractModelDescriptor)(this._model);
1100
+ const deletedAt = descriptor?.SoftDelete?.DeletedAt;
1101
+ if (!deletedAt) {
1102
+ return this;
1103
+ }
1104
+ this._statements = this._statements.filter((s) => {
1105
+ return !(s instanceof statements_js_1.WhereStatement && s.Column === deletedAt && s.Operator === enums_js_1.SqlOperator.NULL);
1106
+ });
1107
+ return this;
1108
+ }
1092
1109
  min(column, as) {
1093
1110
  this._columns.push(this._container.resolve(statements_js_1.ColumnMethodStatement, [column, enums_js_1.ColumnMethods.MIN, as, this._tableAlias]));
1094
1111
  return this;
@@ -1130,44 +1147,26 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
1130
1147
  return compiler.compile();
1131
1148
  }
1132
1149
  async all() {
1133
- return await this;
1150
+ return await this.execute();
1134
1151
  }
1135
1152
  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);
1153
+ const res = await this.execute();
1154
+ if (Array.isArray(res)) {
1155
+ return res.length > 0;
1156
+ }
1157
+ return res !== undefined && res !== null;
1168
1158
  }
1169
- async execute() {
1170
- return (await this);
1159
+ /**
1160
+ * Overrides the engine rather than `execute()` so that the `takeFirst()` unwrapping
1161
+ * happens *inside* the memo. `beforeQueryExecution` is dispatched by `Builder._run()`
1162
+ * for every builder type, so it still fires once per builder rather than once per await.
1163
+ */
1164
+ async _run() {
1165
+ const result = await super._run();
1166
+ if (this._first && Array.isArray(result)) {
1167
+ return (result.length !== 0 ? result[0] : undefined);
1168
+ }
1169
+ return result;
1171
1170
  }
1172
1171
  };
1173
1172
  exports.SelectQueryBuilder = SelectQueryBuilder;
@@ -1193,6 +1192,10 @@ let DeleteQueryBuilder = class DeleteQueryBuilder extends QueryBuilder {
1193
1192
  offset: -1,
1194
1193
  };
1195
1194
  this.QueryContext = interfaces_js_1.QueryContext.Delete;
1195
+ // Query middlewares (e.g. rbac ownership enforcement) must run for deletes
1196
+ // too, not only selects — otherwise :own permission constraints are never
1197
+ // applied and any row can be deleted.
1198
+ this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
1196
1199
  }
1197
1200
  toDB() {
1198
1201
  return this._container.resolve(interfaces_js_1.DeleteQueryCompiler, [this]).compile();
@@ -1232,6 +1235,9 @@ let OnDuplicateQueryBuilder = class OnDuplicateQueryBuilder {
1232
1235
  this._columnsToUpdate = columns;
1233
1236
  return this;
1234
1237
  }
1238
+ execute() {
1239
+ return this._parent.execute();
1240
+ }
1235
1241
  then(onfulfilled, onrejected) {
1236
1242
  return this._parent.then(onfulfilled, onrejected);
1237
1243
  }
@@ -1255,6 +1261,10 @@ let UpdateQueryBuilder = class UpdateQueryBuilder extends QueryBuilder {
1255
1261
  this._boolean = enums_js_1.WhereBoolean.AND;
1256
1262
  this._statements = [];
1257
1263
  this.QueryContext = interfaces_js_1.QueryContext.Update;
1264
+ // Query middlewares (e.g. rbac ownership enforcement) must run for updates
1265
+ // too, not only selects — otherwise :own permission constraints are never
1266
+ // applied and any row can be updated.
1267
+ this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
1258
1268
  }
1259
1269
  in(name) {
1260
1270
  this.setTable(name);
@@ -1290,6 +1300,10 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
1290
1300
  get Replace() {
1291
1301
  return this._replace;
1292
1302
  }
1303
+ /** Columns requested via {@link returning}. Empty when no RETURNING clause was asked for. */
1304
+ get Returning() {
1305
+ return this._returning ?? [];
1306
+ }
1293
1307
  constructor(container, driver, model) {
1294
1308
  super(container, driver, model);
1295
1309
  this._update = false;
@@ -1298,6 +1312,52 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
1298
1312
  this._columns = [];
1299
1313
  this._values = [];
1300
1314
  this.QueryContext = interfaces_js_1.QueryContext.Insert;
1315
+ // Inserts get the same middleware pass as the other three builders. Note that a
1316
+ // middleware which needs to see or amend the row payload must use
1317
+ // `beforeQueryExecution` instead — at construction time `values()` has not been called.
1318
+ this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
1319
+ }
1320
+ /**
1321
+ * The value of `column` on every row about to be inserted, in row order. Empty when the
1322
+ * payload does not carry that column at all.
1323
+ *
1324
+ * The read counterpart of {@link forceColumn}, and the only sane way for a
1325
+ * `beforeQueryExecution` hook to inspect what is being written: the payload lives in two
1326
+ * parallel structures ( `_columns` and one array per row ), so every caller would
1327
+ * otherwise repeat the same index lookup.
1328
+ *
1329
+ * @param column - column to read off each row
1330
+ */
1331
+ getColumnValues(column) {
1332
+ const index = this._columns.findIndex((c) => !(c.Column instanceof RawQuery) && c.Column === column);
1333
+ if (index === -1) {
1334
+ return [];
1335
+ }
1336
+ // eslint-disable-next-line security/detect-object-injection
1337
+ return this._values.map((row) => row[index]);
1338
+ }
1339
+ /**
1340
+ * Forces `column` to `value` on every row of the payload, overwriting whatever the caller
1341
+ * supplied.
1342
+ *
1343
+ * This is the write path for a policy that must not be negotiable — rbac's `createOwn`
1344
+ * ownership stamp. `values()` cannot serve: it appends rows and takes the column list from
1345
+ * its own argument, so calling it a second time adds a row rather than amending the
1346
+ * existing ones.
1347
+ *
1348
+ * @param column - column to overwrite on every row
1349
+ * @param value - value to force
1350
+ */
1351
+ forceColumn(column, value) {
1352
+ const existing = this._columns.findIndex((c) => !(c.Column instanceof RawQuery) && c.Column === column);
1353
+ if (existing === -1) {
1354
+ this.columns([...this._columns.filter((c) => !(c.Column instanceof RawQuery)).map((c) => c.Column), column]);
1355
+ this._values.forEach((row) => row.push(value));
1356
+ return this;
1357
+ }
1358
+ // eslint-disable-next-line security/detect-object-injection
1359
+ this._values.forEach((row) => (row[existing] = value));
1360
+ return this;
1301
1361
  }
1302
1362
  /**
1303
1363
  * Sets insert to ignore on duplicate
@@ -1310,8 +1370,21 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
1310
1370
  this._replace = true;
1311
1371
  return this;
1312
1372
  }
1373
+ /**
1374
+ * Asks the dialect to echo the given columns of every inserted row back.
1375
+ *
1376
+ * @throws NotSupported on drivers whose `supportedFeatures().insertReturning` is false —
1377
+ * silently doing nothing is how this API was a no-op on MySQL and MSSQL for years.
1378
+ */
1313
1379
  returning(columns) {
1380
+ if (!this.Driver.supportedFeatures().insertReturning) {
1381
+ throw new exceptions_1.NotSupported(`driver ${this.Driver.Options.Driver} does not support RETURNING on INSERT`);
1382
+ }
1314
1383
  this._returning = columns;
1384
+ // onDuplicate() sets Upsert unconditionally and wins if it runs afterwards.
1385
+ if (this.QueryContext === interfaces_js_1.QueryContext.Insert) {
1386
+ this.QueryContext = interfaces_js_1.QueryContext.InsertReturning;
1387
+ }
1315
1388
  return this;
1316
1389
  }
1317
1390
  values(data) {
@@ -1473,6 +1546,7 @@ let ColumnQueryBuilder = class ColumnQueryBuilder {
1473
1546
  this.Comment = '';
1474
1547
  this.Unique = false;
1475
1548
  this.Unsigned = false;
1549
+ this.InlinePrimaryKey = true;
1476
1550
  this.Args.push(...args);
1477
1551
  }
1478
1552
  notNull() {
@@ -1643,10 +1717,11 @@ exports.AlterTableQueryBuilder = AlterTableQueryBuilder = __decorate([
1643
1717
  ], AlterTableQueryBuilder);
1644
1718
  let TableQueryBuilder = class TableQueryBuilder extends QueryBuilder {
1645
1719
  /**
1646
- * Alias for string(name, 36 )
1720
+ * Alias for binary(name, 16 ) - uuids are stored as 16-byte BINARY to match
1721
+ * the UuidConverter ( which writes a dashed uuid as a 16-byte buffer ).
1647
1722
  */
1648
1723
  uuid(name) {
1649
- return this.string(name, 36);
1724
+ return this.binary(name, 16);
1650
1725
  }
1651
1726
  ifExists() {
1652
1727
  this._checkExists = true;
@@ -2053,6 +2128,16 @@ function createQuery(model, query, injectModel = true) {
2053
2128
  }
2054
2129
  qr.middleware(new discrimination_middleware_js_1.DiscriminationMapMiddleware(dsc));
2055
2130
  qr.setTable(dsc.TableName);
2131
+ // Soft-delete read filtering: by default exclude rows that have been soft
2132
+ // deleted (DeletedAt IS NOT NULL). SelectQueryBuilder.withDeleted() removes
2133
+ // this default statement to include soft-deleted rows again.
2134
+ // Guarded on the DeletedAt column actually being present in the model's
2135
+ // reflected columns — a filter on a column the schema does not expose is
2136
+ // impossible anyway, and the guard keeps queries working when table info
2137
+ // has not (yet) surfaced the column.
2138
+ if (qr instanceof SelectQueryBuilder && dsc.SoftDelete?.DeletedAt && dsc.Columns?.some((c) => c.Name === dsc.SoftDelete.DeletedAt)) {
2139
+ qr.whereNull(dsc.SoftDelete.DeletedAt);
2140
+ }
2056
2141
  if (driver.Options.Database) {
2057
2142
  qr.database(driver.Options.Database);
2058
2143
  }