@spinajs/orm 2.0.481 → 2.0.482

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/README.md +72 -3
  2. package/lib/cjs/bootstrap.js +10 -0
  3. package/lib/cjs/bootstrap.js.map +1 -1
  4. package/lib/cjs/builders.d.ts +115 -11
  5. package/lib/cjs/builders.d.ts.map +1 -1
  6. package/lib/cjs/builders.js +342 -283
  7. package/lib/cjs/builders.js.map +1 -1
  8. package/lib/cjs/converters.d.ts +9 -2
  9. package/lib/cjs/converters.d.ts.map +1 -1
  10. package/lib/cjs/converters.js +48 -8
  11. package/lib/cjs/converters.js.map +1 -1
  12. package/lib/cjs/decorators.d.ts +26 -3
  13. package/lib/cjs/decorators.d.ts.map +1 -1
  14. package/lib/cjs/decorators.js +95 -42
  15. package/lib/cjs/decorators.js.map +1 -1
  16. package/lib/cjs/dehydrators.js +1 -1
  17. package/lib/cjs/dehydrators.js.map +1 -1
  18. package/lib/cjs/descriptor.d.ts +1 -0
  19. package/lib/cjs/descriptor.d.ts.map +1 -1
  20. package/lib/cjs/descriptor.js +16 -40
  21. package/lib/cjs/descriptor.js.map +1 -1
  22. package/lib/cjs/driver.d.ts +136 -7
  23. package/lib/cjs/driver.d.ts.map +1 -1
  24. package/lib/cjs/driver.js +279 -0
  25. package/lib/cjs/driver.js.map +1 -1
  26. package/lib/cjs/existsRelationHandlers.d.ts +29 -0
  27. package/lib/cjs/existsRelationHandlers.d.ts.map +1 -0
  28. package/lib/cjs/existsRelationHandlers.js +104 -0
  29. package/lib/cjs/existsRelationHandlers.js.map +1 -0
  30. package/lib/cjs/fp.d.ts.map +1 -1
  31. package/lib/cjs/fp.js +10 -15
  32. package/lib/cjs/fp.js.map +1 -1
  33. package/lib/cjs/hydrators.d.ts.map +1 -1
  34. package/lib/cjs/hydrators.js +29 -2
  35. package/lib/cjs/hydrators.js.map +1 -1
  36. package/lib/cjs/identity-map.d.ts +60 -0
  37. package/lib/cjs/identity-map.d.ts.map +1 -0
  38. package/lib/cjs/identity-map.js +135 -0
  39. package/lib/cjs/identity-map.js.map +1 -0
  40. package/lib/cjs/index.d.ts +12 -0
  41. package/lib/cjs/index.d.ts.map +1 -1
  42. package/lib/cjs/index.js +12 -0
  43. package/lib/cjs/index.js.map +1 -1
  44. package/lib/cjs/interfaces.d.ts +295 -9
  45. package/lib/cjs/interfaces.d.ts.map +1 -1
  46. package/lib/cjs/interfaces.js +54 -1
  47. package/lib/cjs/interfaces.js.map +1 -1
  48. package/lib/cjs/metadata.d.ts.map +1 -1
  49. package/lib/cjs/metadata.js +4 -1
  50. package/lib/cjs/metadata.js.map +1 -1
  51. package/lib/cjs/metrics.d.ts +53 -0
  52. package/lib/cjs/metrics.d.ts.map +1 -0
  53. package/lib/cjs/metrics.js +82 -0
  54. package/lib/cjs/metrics.js.map +1 -0
  55. package/lib/cjs/middlewares.d.ts +13 -1
  56. package/lib/cjs/middlewares.d.ts.map +1 -1
  57. package/lib/cjs/middlewares.js +55 -15
  58. package/lib/cjs/middlewares.js.map +1 -1
  59. package/lib/cjs/model.d.ts +129 -13
  60. package/lib/cjs/model.d.ts.map +1 -1
  61. package/lib/cjs/model.js +449 -93
  62. package/lib/cjs/model.js.map +1 -1
  63. package/lib/cjs/orm.d.ts.map +1 -1
  64. package/lib/cjs/orm.js +12 -3
  65. package/lib/cjs/orm.js.map +1 -1
  66. package/lib/cjs/orphan.d.ts +27 -0
  67. package/lib/cjs/orphan.d.ts.map +1 -0
  68. package/lib/cjs/orphan.js +55 -0
  69. package/lib/cjs/orphan.js.map +1 -0
  70. package/lib/cjs/primary-keys.d.ts +60 -0
  71. package/lib/cjs/primary-keys.d.ts.map +1 -0
  72. package/lib/cjs/primary-keys.js +243 -0
  73. package/lib/cjs/primary-keys.js.map +1 -0
  74. package/lib/cjs/relation-objects.d.ts +101 -9
  75. package/lib/cjs/relation-objects.d.ts.map +1 -1
  76. package/lib/cjs/relation-objects.js +194 -43
  77. package/lib/cjs/relation-objects.js.map +1 -1
  78. package/lib/cjs/relations.d.ts +7 -1
  79. package/lib/cjs/relations.d.ts.map +1 -1
  80. package/lib/cjs/relations.js +16 -1
  81. package/lib/cjs/relations.js.map +1 -1
  82. package/lib/cjs/resilience.d.ts +50 -0
  83. package/lib/cjs/resilience.d.ts.map +1 -0
  84. package/lib/cjs/resilience.js +70 -0
  85. package/lib/cjs/resilience.js.map +1 -0
  86. package/lib/cjs/snapshot.d.ts +58 -0
  87. package/lib/cjs/snapshot.d.ts.map +1 -0
  88. package/lib/cjs/snapshot.js +125 -0
  89. package/lib/cjs/snapshot.js.map +1 -0
  90. package/lib/cjs/statements.d.ts +11 -1
  91. package/lib/cjs/statements.d.ts.map +1 -1
  92. package/lib/cjs/statements.js +13 -2
  93. package/lib/cjs/statements.js.map +1 -1
  94. package/lib/cjs/subject-builder.d.ts +79 -0
  95. package/lib/cjs/subject-builder.d.ts.map +1 -0
  96. package/lib/cjs/subject-builder.js +296 -0
  97. package/lib/cjs/subject-builder.js.map +1 -0
  98. package/lib/cjs/subject-executor.d.ts +122 -0
  99. package/lib/cjs/subject-executor.d.ts.map +1 -0
  100. package/lib/cjs/subject-executor.js +319 -0
  101. package/lib/cjs/subject-executor.js.map +1 -0
  102. package/lib/cjs/subject-sorter.d.ts +70 -0
  103. package/lib/cjs/subject-sorter.d.ts.map +1 -0
  104. package/lib/cjs/subject-sorter.js +173 -0
  105. package/lib/cjs/subject-sorter.js.map +1 -0
  106. package/lib/cjs/subject.d.ts +100 -0
  107. package/lib/cjs/subject.d.ts.map +1 -0
  108. package/lib/cjs/subject.js +89 -0
  109. package/lib/cjs/subject.js.map +1 -0
  110. package/lib/cjs/unit-of-work.d.ts +52 -0
  111. package/lib/cjs/unit-of-work.d.ts.map +1 -0
  112. package/lib/cjs/unit-of-work.js +170 -0
  113. package/lib/cjs/unit-of-work.js.map +1 -0
  114. package/lib/mjs/bootstrap.js +10 -0
  115. package/lib/mjs/bootstrap.js.map +1 -1
  116. package/lib/mjs/builders.d.ts +115 -11
  117. package/lib/mjs/builders.d.ts.map +1 -1
  118. package/lib/mjs/builders.js +344 -285
  119. package/lib/mjs/builders.js.map +1 -1
  120. package/lib/mjs/converters.d.ts +9 -2
  121. package/lib/mjs/converters.d.ts.map +1 -1
  122. package/lib/mjs/converters.js +48 -8
  123. package/lib/mjs/converters.js.map +1 -1
  124. package/lib/mjs/decorators.d.ts +26 -3
  125. package/lib/mjs/decorators.d.ts.map +1 -1
  126. package/lib/mjs/decorators.js +96 -44
  127. package/lib/mjs/decorators.js.map +1 -1
  128. package/lib/mjs/dehydrators.js +1 -1
  129. package/lib/mjs/dehydrators.js.map +1 -1
  130. package/lib/mjs/descriptor.d.ts +1 -0
  131. package/lib/mjs/descriptor.d.ts.map +1 -1
  132. package/lib/mjs/descriptor.js +17 -39
  133. package/lib/mjs/descriptor.js.map +1 -1
  134. package/lib/mjs/driver.d.ts +136 -7
  135. package/lib/mjs/driver.d.ts.map +1 -1
  136. package/lib/mjs/driver.js +279 -0
  137. package/lib/mjs/driver.js.map +1 -1
  138. package/lib/mjs/existsRelationHandlers.d.ts +29 -0
  139. package/lib/mjs/existsRelationHandlers.d.ts.map +1 -0
  140. package/lib/mjs/existsRelationHandlers.js +100 -0
  141. package/lib/mjs/existsRelationHandlers.js.map +1 -0
  142. package/lib/mjs/fp.d.ts.map +1 -1
  143. package/lib/mjs/fp.js +10 -15
  144. package/lib/mjs/fp.js.map +1 -1
  145. package/lib/mjs/hydrators.d.ts.map +1 -1
  146. package/lib/mjs/hydrators.js +29 -2
  147. package/lib/mjs/hydrators.js.map +1 -1
  148. package/lib/mjs/identity-map.d.ts +60 -0
  149. package/lib/mjs/identity-map.d.ts.map +1 -0
  150. package/lib/mjs/identity-map.js +130 -0
  151. package/lib/mjs/identity-map.js.map +1 -0
  152. package/lib/mjs/index.d.ts +12 -0
  153. package/lib/mjs/index.d.ts.map +1 -1
  154. package/lib/mjs/index.js +12 -0
  155. package/lib/mjs/index.js.map +1 -1
  156. package/lib/mjs/interfaces.d.ts +295 -9
  157. package/lib/mjs/interfaces.d.ts.map +1 -1
  158. package/lib/mjs/interfaces.js +53 -0
  159. package/lib/mjs/interfaces.js.map +1 -1
  160. package/lib/mjs/metadata.d.ts.map +1 -1
  161. package/lib/mjs/metadata.js +4 -1
  162. package/lib/mjs/metadata.js.map +1 -1
  163. package/lib/mjs/metrics.d.ts +53 -0
  164. package/lib/mjs/metrics.d.ts.map +1 -0
  165. package/lib/mjs/metrics.js +76 -0
  166. package/lib/mjs/metrics.js.map +1 -0
  167. package/lib/mjs/middlewares.d.ts +13 -1
  168. package/lib/mjs/middlewares.d.ts.map +1 -1
  169. package/lib/mjs/middlewares.js +55 -15
  170. package/lib/mjs/middlewares.js.map +1 -1
  171. package/lib/mjs/model.d.ts +129 -13
  172. package/lib/mjs/model.d.ts.map +1 -1
  173. package/lib/mjs/model.js +451 -95
  174. package/lib/mjs/model.js.map +1 -1
  175. package/lib/mjs/orm.d.ts.map +1 -1
  176. package/lib/mjs/orm.js +12 -3
  177. package/lib/mjs/orm.js.map +1 -1
  178. package/lib/mjs/orphan.d.ts +27 -0
  179. package/lib/mjs/orphan.d.ts.map +1 -0
  180. package/lib/mjs/orphan.js +52 -0
  181. package/lib/mjs/orphan.js.map +1 -0
  182. package/lib/mjs/primary-keys.d.ts +60 -0
  183. package/lib/mjs/primary-keys.d.ts.map +1 -0
  184. package/lib/mjs/primary-keys.js +223 -0
  185. package/lib/mjs/primary-keys.js.map +1 -0
  186. package/lib/mjs/relation-objects.d.ts +101 -9
  187. package/lib/mjs/relation-objects.d.ts.map +1 -1
  188. package/lib/mjs/relation-objects.js +194 -43
  189. package/lib/mjs/relation-objects.js.map +1 -1
  190. package/lib/mjs/relations.d.ts +7 -1
  191. package/lib/mjs/relations.d.ts.map +1 -1
  192. package/lib/mjs/relations.js +16 -1
  193. package/lib/mjs/relations.js.map +1 -1
  194. package/lib/mjs/resilience.d.ts +50 -0
  195. package/lib/mjs/resilience.d.ts.map +1 -0
  196. package/lib/mjs/resilience.js +64 -0
  197. package/lib/mjs/resilience.js.map +1 -0
  198. package/lib/mjs/snapshot.d.ts +58 -0
  199. package/lib/mjs/snapshot.d.ts.map +1 -0
  200. package/lib/mjs/snapshot.js +115 -0
  201. package/lib/mjs/snapshot.js.map +1 -0
  202. package/lib/mjs/statements.d.ts +11 -1
  203. package/lib/mjs/statements.d.ts.map +1 -1
  204. package/lib/mjs/statements.js +14 -3
  205. package/lib/mjs/statements.js.map +1 -1
  206. package/lib/mjs/subject-builder.d.ts +79 -0
  207. package/lib/mjs/subject-builder.d.ts.map +1 -0
  208. package/lib/mjs/subject-builder.js +292 -0
  209. package/lib/mjs/subject-builder.js.map +1 -0
  210. package/lib/mjs/subject-executor.d.ts +122 -0
  211. package/lib/mjs/subject-executor.d.ts.map +1 -0
  212. package/lib/mjs/subject-executor.js +312 -0
  213. package/lib/mjs/subject-executor.js.map +1 -0
  214. package/lib/mjs/subject-sorter.d.ts +70 -0
  215. package/lib/mjs/subject-sorter.d.ts.map +1 -0
  216. package/lib/mjs/subject-sorter.js +168 -0
  217. package/lib/mjs/subject-sorter.js.map +1 -0
  218. package/lib/mjs/subject.d.ts +100 -0
  219. package/lib/mjs/subject.d.ts.map +1 -0
  220. package/lib/mjs/subject.js +84 -0
  221. package/lib/mjs/subject.js.map +1 -0
  222. package/lib/mjs/unit-of-work.d.ts +52 -0
  223. package/lib/mjs/unit-of-work.d.ts.map +1 -0
  224. package/lib/mjs/unit-of-work.js +166 -0
  225. package/lib/mjs/unit-of-work.js.map +1 -0
  226. package/lib/tsconfig.cjs.tsbuildinfo +1 -1
  227. package/lib/tsconfig.mjs.tsbuildinfo +1 -1
  228. package/package.json +9 -7
@@ -10,7 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  var RawQuery_1, WhereBuilder_1, SelectQueryBuilder_1;
11
11
  /* eslint-disable prettier/prettier */
12
12
  import { Container, Inject, NewInstance, DI, Injectable, isConstructor } from '@spinajs/di';
13
- import { InvalidArgument, MethodNotImplemented, InvalidOperation } from '@spinajs/exceptions';
13
+ import { InvalidArgument, MethodNotImplemented, InvalidOperation, NotSupported } from '@spinajs/exceptions';
14
14
  import { OrmException, OrmNotFoundException } from './exceptions.js';
15
15
  import _ from 'lodash';
16
16
  import { use } from 'typescript-mix';
@@ -18,9 +18,10 @@ import { ColumnMethods, ColumnType, QueryMethod, SortOrder, WhereBoolean, SqlOpe
18
18
  import { DeleteQueryCompiler, InsertQueryCompiler, SelectQueryCompiler, TruncateTableQueryCompiler, TableQueryCompiler, AlterTableQueryCompiler, UpdateQueryCompiler, QueryContext, IndexQueryCompiler, RelationType, ReferentialAction, DefaultValueBuilder, ColumnAlterationType, TableExistsCompiler, DropViewCompiler, DropTableCompiler, TableCloneQueryCompiler, QueryMiddleware, DropEventQueryCompiler, EventQueryCompiler, RawSchemaQueryCompiler } from './interfaces.js';
19
19
  import { BetweenStatement, ColumnMethodStatement, ColumnStatement, ExistsQueryStatement, InSetStatement, InStatement, RawQueryStatement, WhereQueryStatement, WhereStatement, ColumnRawStatement, JoinStatement, WithRecursiveStatement, GroupByStatement, Wrap, LazyQueryStatement } from './statements.js';
20
20
  import { BelongsToRelation, OneToManyRelation, ManyToManyRelation, BelongsToRecursiveRelation, QueryRelation, VirtualRelation } from './relations.js';
21
- import { Lazy } from '@spinajs/util';
21
+ import { Lazy, isNullOrWhitespace, _check_arg, _positive } from '@spinajs/util';
22
22
  import { DiscriminationMapMiddleware } from './discrimination-middleware.js';
23
23
  import { extractModelDescriptor } from './descriptor.js';
24
+ import { ExistsRelationHandler } from './existsRelationHandlers.js';
24
25
  function isWhereOperator(val) {
25
26
  return _.isString(val) && Object.values(SqlOperator).includes(val.toLowerCase());
26
27
  }
@@ -37,6 +38,12 @@ let Builder = class Builder {
37
38
  constructor(container, driver, model) {
38
39
  this._middlewares = [];
39
40
  this._queryMiddlewares = [];
41
+ /**
42
+ * Memoized result of {@link execute}. A builder executes at most once; awaiting
43
+ * it again resolves with the same value instead of re-running the query.
44
+ * Call `clone()` first if you genuinely need a second round-trip.
45
+ */
46
+ this._executionPromise = null;
40
47
  this._driver = driver;
41
48
  this._container = container;
42
49
  this._model = model;
@@ -44,82 +51,97 @@ let Builder = class Builder {
44
51
  this._asRaw = false;
45
52
  this._queryMiddlewares = DI.resolve(Array.ofType(QueryMiddleware));
46
53
  }
47
- then(onfulfilled, onrejected) {
48
- return this._driver
49
- .execute(this)
50
- .then((result) => {
51
- try {
52
- if (this._asRaw) {
53
- onfulfilled?.(result);
54
- return;
55
- }
56
- let transformedResult = result;
57
- // if we have something to transform ...
58
- if (transformedResult) {
59
- this._middlewares.forEach((m) => {
60
- Object.assign(transformedResult, m.afterQuery(transformedResult));
61
- });
62
- }
63
- if (this._model && !this._nonSelect) {
64
- // TODO: rething this casting
65
- const models = transformedResult.map((r) => {
66
- let model = null;
67
- for (const middleware of this._middlewares.reverse()) {
68
- model = middleware.modelCreation(r);
69
- if (model !== null) {
70
- break;
71
- }
72
- }
73
- if (model === null) {
74
- model = DI.resolve('__orm_model_factory__', [this._model]);
75
- }
76
- model.hydrate(r);
77
- model.IsDirty = false;
78
- return model;
79
- });
80
- const afterMiddlewarePromises = this._middlewares.reduce((prev, current) => {
81
- return prev.concat([current.afterHydration(models)]);
82
- }, []);
83
- if (this._middlewares.length > 0) {
84
- Promise.all(afterMiddlewarePromises).then(() => {
85
- try {
86
- onfulfilled?.(models);
87
- }
88
- catch (err) {
89
- if (onrejected) {
90
- onrejected(err);
91
- }
92
- else {
93
- throw err;
94
- }
95
- }
96
- }, onrejected);
97
- }
98
- else {
99
- onfulfilled?.(models);
54
+ /**
55
+ * The execution engine. Sends the compiled query to the driver, applies the
56
+ * result middlewares, hydrates models and awaits the post-hydration middlewares.
57
+ *
58
+ * Always *returns* its value — the caller's promise chain is what propagates it.
59
+ * Subclasses override this (not `execute()`) so that their extra work also lands
60
+ * inside the memo.
61
+ */
62
+ async _run() {
63
+ // Fires for EVERY builder type, not just SELECT. This used to live on
64
+ // `SelectQueryBuilder._run`, so an INSERT/UPDATE/DELETE never reached a
65
+ // `beforeQueryExecution` hook at all — which silently disabled any middleware that
66
+ // needs the finished query (rbac's insert-ownership check is exactly that: the payload
67
+ // does not exist until `values()` has been called, long after construction).
68
+ this._queryMiddlewares.forEach((x) => x.beforeQueryExecution(this));
69
+ const result = (await this._driver.execute(this));
70
+ // Snapshot the pipeline once, *after* the driver call: compiling the query is what
71
+ // registers the relation middlewares (the driver calls `toDB()`), so this is the first
72
+ // point at which the list is complete. Everything below runs against this immutable copy,
73
+ // so a middleware registered later cannot change the pipeline mid-flight, and — crucially —
74
+ // we never call `Array.prototype.reverse()` on `this._middlewares`, which mutates in place
75
+ // and used to flip `modelCreation` resolution order on every execution ( B8 ).
76
+ const middlewares = [...this._middlewares];
77
+ const creationOrder = [...middlewares].reverse();
78
+ if (this._asRaw) {
79
+ return result;
80
+ }
81
+ const transformedResult = result;
82
+ // if we have something to transform ...
83
+ if (transformedResult) {
84
+ middlewares.forEach((m) => {
85
+ Object.assign(transformedResult, m.afterQuery(transformedResult));
86
+ });
87
+ }
88
+ if (this._model && !this._nonSelect) {
89
+ // TODO: rething this casting
90
+ const models = transformedResult.map((r) => {
91
+ let model = null;
92
+ for (const middleware of creationOrder) {
93
+ model = middleware.modelCreation(r);
94
+ if (model !== null) {
95
+ break;
100
96
  }
101
97
  }
102
- else {
103
- onfulfilled?.(transformedResult);
98
+ if (model === null) {
99
+ model = DI.resolve('__orm_model_factory__', [this._model]);
104
100
  }
105
- }
106
- catch (err) {
107
- if (onrejected) {
108
- onrejected(err);
109
- }
110
- else {
111
- throw err;
101
+ model.hydrate(r);
102
+ model.IsDirty = false;
103
+ // This is the one point at which the instance's columns hold exactly what the
104
+ // database returned, so it is the diff baseline for `save()`. Relation members
105
+ // are attached later by the afterHydration middlewares below, which record their
106
+ // own member keys into this same snapshot.
107
+ model.takeSnapshot();
108
+ // Nested relation data that arrived on the row itself ( belongsTo LEFT JOIN,
109
+ // and hasMany arrays passed straight to hydrate ) was attached by the hydrators
110
+ // inside `hydrate()` above — i.e. before the snapshot existed, so their own
111
+ // `snapshotRelation` calls no-opped. Record those relations now.
112
+ for (const name of (model.ModelDescriptor?.Relations ?? new Map()).keys()) {
113
+ if (model[name]?.Populated) {
114
+ model.snapshotRelation(name);
115
+ }
112
116
  }
117
+ return model;
118
+ });
119
+ if (middlewares.length > 0) {
120
+ await Promise.all(middlewares.map((m) => m.afterHydration(models)));
113
121
  }
114
- })
115
- .catch((err) => {
116
- if (onrejected) {
117
- onrejected(err);
118
- }
119
- else {
120
- throw err;
121
- }
122
- });
122
+ return models;
123
+ }
124
+ return transformedResult;
125
+ }
126
+ /**
127
+ * Executes the query. The single entry point for execution — `then()` delegates here.
128
+ * The underlying work runs exactly once per builder instance; subsequent calls resolve
129
+ * with the memoized result.
130
+ */
131
+ execute() {
132
+ if (!this._executionPromise) {
133
+ this._executionPromise = this._run();
134
+ }
135
+ return this._executionPromise;
136
+ }
137
+ then(onfulfilled, onrejected) {
138
+ return this.execute().then(onfulfilled, onrejected);
139
+ }
140
+ catch(onrejected) {
141
+ return this.execute().catch(onrejected);
142
+ }
143
+ finally(onfinally) {
144
+ return this.execute().finally(onfinally);
123
145
  }
124
146
  middleware(middleware) {
125
147
  this._middlewares.push(middleware);
@@ -191,7 +213,7 @@ let QueryBuilder = class QueryBuilder extends Builder {
191
213
  *
192
214
  */
193
215
  setTable(table, alias) {
194
- if (!table.trim()) {
216
+ if (isNullOrWhitespace(table)) {
195
217
  throw new InvalidArgument('table name is empty');
196
218
  }
197
219
  this._table = table;
@@ -228,9 +250,7 @@ let LimitBuilder = class LimitBuilder {
228
250
  };
229
251
  }
230
252
  take(count) {
231
- if (count <= 0) {
232
- throw new InvalidArgument(`take count cannot be negative number`);
233
- }
253
+ _check_arg(_positive())(count, 'take count');
234
254
  this._limit.limit = count;
235
255
  return this;
236
256
  }
@@ -256,7 +276,7 @@ let LimitBuilder = class LimitBuilder {
256
276
  const result = (await this);
257
277
  if (result === undefined || (Array.isArray(result) && result.length === 0)) {
258
278
  if (typeof error === 'function') {
259
- error = error(this.toDB());
279
+ throw error(this.toDB());
260
280
  }
261
281
  else
262
282
  throw error;
@@ -285,10 +305,7 @@ LimitBuilder = __decorate([
285
305
  export { LimitBuilder };
286
306
  let OrderByBuilder = class OrderByBuilder {
287
307
  constructor() {
288
- this._sort = {
289
- column: '',
290
- order: SortOrder.ASC,
291
- };
308
+ this._sorts = [];
292
309
  }
293
310
  order(column, direction) {
294
311
  if (!column) {
@@ -301,10 +318,10 @@ let OrderByBuilder = class OrderByBuilder {
301
318
  });
302
319
  return this;
303
320
  }
304
- this._sort = {
321
+ this._sorts.push({
305
322
  column,
306
323
  order: direction,
307
- };
324
+ });
308
325
  return this;
309
326
  }
310
327
  orderBy(column) {
@@ -315,10 +332,10 @@ let OrderByBuilder = class OrderByBuilder {
315
332
  });
316
333
  return this;
317
334
  }
318
- this._sort = {
335
+ this._sorts.push({
319
336
  column,
320
337
  order: SortOrder.ASC,
321
- };
338
+ });
322
339
  return this;
323
340
  }
324
341
  orderByDescending(column) {
@@ -329,14 +346,25 @@ let OrderByBuilder = class OrderByBuilder {
329
346
  });
330
347
  return this;
331
348
  }
332
- this._sort = {
349
+ this._sorts.push({
333
350
  column,
334
351
  order: SortOrder.DESC,
335
- };
352
+ });
336
353
  return this;
337
354
  }
355
+ /**
356
+ * Returns the FIRST sort entry (or null) for backward compat with dialect
357
+ * packages that emit a single ORDER BY column. Use getSorts() for all entries.
358
+ */
338
359
  getSort() {
339
- return this._sort.column.trim() !== '' ? this._sort : null;
360
+ const sort = this._sorts.find((s) => s.column.trim() !== '');
361
+ return sort ?? null;
362
+ }
363
+ /**
364
+ * Returns all sort entries (multi-column ORDER BY), skipping empty columns.
365
+ */
366
+ getSorts() {
367
+ return this._sorts.filter((s) => s.column.trim() !== '');
340
368
  }
341
369
  };
342
370
  OrderByBuilder = __decorate([
@@ -548,6 +576,23 @@ let WithRecursiveBuilder = class WithRecursiveBuilder {
548
576
  this._cteStatement = this._container.resolve(WithRecursiveStatement, ['cte', this, rcKeyName, pkName]);
549
577
  return this;
550
578
  }
579
+ /**
580
+ * Drops the recursive CTE from this builder.
581
+ *
582
+ * `WithRecursiveStatement.build()` compiles two CLONES of the owning query — the anchor
583
+ * member and the recursive member of the CTE. `clone()` copies `_cteStatement`, so each
584
+ * clone was still marked recursive and compiling it re-entered `build()`, which cloned
585
+ * again: an unbounded mutual recursion between `toDB()` and the recursive compiler that
586
+ * ended in a stack overflow rather than a query. The two member queries are by definition
587
+ * not themselves recursive, so the statement clears the flag on its clones.
588
+ *
589
+ * Named alongside `clearJoins()` / `clearWhere()`, which `build()` already uses to strip the
590
+ * parts of the parent query each member must not inherit.
591
+ */
592
+ clearRecursive() {
593
+ this._cteStatement = undefined;
594
+ return this;
595
+ }
551
596
  };
552
597
  WithRecursiveBuilder = __decorate([
553
598
  NewInstance()
@@ -582,12 +627,31 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
582
627
  clone(_parent) {
583
628
  // TODO: fix this cast
584
629
  const builder = new WhereBuilder_1(_parent);
585
- builder._statements = this._statements.map((s) => s.clone(builder));
630
+ builder._statements = this._statements.map((s) => {
631
+ const cloned = s.clone(builder);
632
+ // preserve the per-statement boolean connector (clone() rebuilds the
633
+ // statement from scratch and would otherwise reset it to the AND default)
634
+ cloned.Boolean = s.Boolean;
635
+ return cloned;
636
+ });
586
637
  builder._boolean = this._boolean;
587
638
  builder._model = this._model;
588
639
  builder._tableAlias = this.TableAlias;
589
640
  return builder;
590
641
  }
642
+ /**
643
+ * Pushes a statement onto this builder, stamping it with the currently pending
644
+ * boolean connector (set by {@link orWhere}/{@link andWhere}). The pending
645
+ * connector applies to the NEXT pushed statement only and resets to AND
646
+ * afterwards, so `where(a).where(b).orWhere(c).where(d)` compiles to
647
+ * `a AND b OR c AND d` rather than rewriting the whole clause.
648
+ */
649
+ pushStatement(statement) {
650
+ statement.Boolean = this._boolean;
651
+ this._statements.push(statement);
652
+ this._boolean = WhereBoolean.AND;
653
+ return this;
654
+ }
591
655
  when(condition, callback, callbackElse) {
592
656
  if (condition) {
593
657
  if (callback)
@@ -632,18 +696,18 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
632
696
  return this.where(RawQuery.create(column ? 'TRUE' : 'FALSE'));
633
697
  }
634
698
  if (column instanceof RawQuery) {
635
- this.Statements.push(this._container.resolve(RawQueryStatement, [column.Query, column.Bindings, self.TableAlias]));
699
+ this.pushStatement(this._container.resolve(RawQueryStatement, [column.Query, column.Bindings, self.TableAlias]));
636
700
  return this;
637
701
  }
638
702
  // handle nested where's
639
703
  if (_.isFunction(column)) {
640
704
  const builder = new WhereBuilder_1(this);
641
705
  column.call(builder);
642
- self.Statements.push(this._container.resolve(WhereQueryStatement, [builder, self.TableAlias]));
706
+ self.pushStatement(this._container.resolve(WhereQueryStatement, [builder, self.TableAlias]));
643
707
  return this;
644
708
  }
645
709
  if (column instanceof Lazy) {
646
- this.Statements.push(this._container.resolve(LazyQueryStatement, [column, this]));
710
+ this.pushStatement(this._container.resolve(LazyQueryStatement, [column, this]));
647
711
  return this;
648
712
  }
649
713
  // handle simple key = object[key] AND ....
@@ -672,7 +736,7 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
672
736
  if (sVal === null) {
673
737
  return this.whereNull(c);
674
738
  }
675
- self._statements.push(self._container.resolve(WhereStatement, [c, SqlOperator.EQ, sVal, this]));
739
+ self.pushStatement(self._container.resolve(WhereStatement, [c, SqlOperator.EQ, sVal, this]));
676
740
  return self;
677
741
  }
678
742
  /**
@@ -691,12 +755,17 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
691
755
  throw new InvalidArgument(`value cannot be NaN`);
692
756
  }
693
757
  if (sVal === null) {
694
- return this.whereNull(c);
695
- }
696
- if (sVal === null) {
697
- return o === SqlOperator.NOT_NULL ? this.whereNotNull(c) : this.whereNull(c);
758
+ const op = String(o).toLowerCase();
759
+ // where(col, '=', null) => IS NULL ; where(col, '!=' / '<>', null) => IS NOT NULL
760
+ if (op === SqlOperator.EQ) {
761
+ return this.whereNull(c);
762
+ }
763
+ if (op === SqlOperator.NOT || op === SqlOperator.NOT_2) {
764
+ return this.whereNotNull(c);
765
+ }
766
+ throw new InvalidArgument(`operator ${o} cannot be used with null value ( only =, !=, <> are allowed )`);
698
767
  }
699
- self._statements.push(self._container.resolve(WhereStatement, [c, o, sVal, self]));
768
+ self.pushStatement(self._container.resolve(WhereStatement, [c, o, sVal, self]));
700
769
  return this;
701
770
  }
702
771
  }
@@ -713,9 +782,9 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
713
782
  for (const key of Object.keys(obj).filter((x) => obj[x] !== undefined)) {
714
783
  const val = obj[key];
715
784
  if (Array.isArray(val)) {
716
- if (val.length !== 0) {
717
- this.whereIn(key, val);
718
- }
785
+ // empty array => SQL `IN ()` semantics => match nothing (FALSE),
786
+ // never "no condition" (which would match everything)
787
+ this.whereIn(key, val);
719
788
  }
720
789
  else if (val === null) {
721
790
  this.whereNull(key);
@@ -726,165 +795,82 @@ let WhereBuilder = WhereBuilder_1 = class WhereBuilder {
726
795
  return this;
727
796
  }
728
797
  whereNotNull(column) {
729
- this._statements.push(this._container.resolve(WhereStatement, [column, SqlOperator.NOT_NULL, null, this]));
798
+ this.pushStatement(this._container.resolve(WhereStatement, [column, SqlOperator.NOT_NULL, null, this]));
730
799
  return this;
731
800
  }
732
801
  whereNull(column) {
733
- this._statements.push(this._container.resolve(WhereStatement, [column, SqlOperator.NULL, null, this]));
802
+ this.pushStatement(this._container.resolve(WhereStatement, [column, SqlOperator.NULL, null, this]));
734
803
  return this;
735
804
  }
736
805
  whereNot(column, val) {
737
806
  return this.where(column, SqlOperator.NOT, val);
738
807
  }
739
808
  whereIn(column, val) {
740
- this._statements.push(this._container.resolve(InStatement, [column, val, false, this]));
809
+ // `IN ()` matches nothing in SQL; compile an empty set to FALSE rather than
810
+ // emitting no condition (which would silently match every row).
811
+ if (Array.isArray(val) && val.length === 0) {
812
+ this.where(false);
813
+ return this;
814
+ }
815
+ this.pushStatement(this._container.resolve(InStatement, [column, val, false, this]));
741
816
  return this;
742
817
  }
743
818
  whereNotIn(column, val) {
744
- this._statements.push(this._container.resolve(InStatement, [column, val, true, this]));
819
+ this.pushStatement(this._container.resolve(InStatement, [column, val, true, this]));
745
820
  return this;
746
821
  }
747
822
  whereExist(query, callback) {
748
- // TODO: refactor and remove code duplication with whereNotExists
749
- // TODO: move relation handling to separate DI service for every exists relation type
750
- let relQuery;
751
- let sourcePKey = '';
752
- const self = this;
753
- let tableName = '';
754
- let tDesc;
755
- if (typeof query === 'string') {
756
- const rel = this._model.getRelationDescriptor(query);
757
- if (!rel) {
758
- throw new OrmException(`relation ${query} not found in model ${this.constructor.name}`);
759
- }
760
- switch (rel.Type) {
761
- case RelationType.One:
762
- this.whereNotNull(rel.ForeignKey);
763
- // simply use right join for condition check
764
- if (callback) {
765
- // TODO: cast fix
766
- this.rightJoin(rel.TargetModel, callback.bind(query));
767
- }
768
- break;
769
- case RelationType.Many:
770
- tableName = rel.TargetModel.getModelDescriptor().TableName;
771
- tDesc = self._model.getModelDescriptor();
772
- // 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
773
- relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
774
- relQuery.where(Lazy.oF(function () {
775
- const sourceAlias = self._tableAlias ?? (self._parent ? self._parent.TableAlias : tDesc.TableName);
776
- sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
777
- // relQuery is guaranteed assigned above before this lazy callback executes
778
- relQuery.where(new RawQuery(`${rel.ForeignKey} = ${sourcePKey}`));
779
- }));
780
- if (callback) {
781
- callback.apply(relQuery);
782
- }
783
- this.whereExist(relQuery);
784
- break;
785
- case RelationType.ManyToMany:
786
- relQuery = rel.JunctionModel.query();
787
- tableName = rel.TargetModel.getModelDescriptor().TableName;
788
- tDesc = self._model.getModelDescriptor();
789
- relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
790
- relQuery.where(Lazy.oF(function () {
791
- const sourceAlias = self._tableAlias ?? (self._parent ? self._parent.TableAlias : tDesc.TableName);
792
- sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
793
- // relQuery is guaranteed assigned above before this lazy callback executes
794
- relQuery.where(new RawQuery(`${rel.JunctionModelSourceModelFKey_Name} = ${sourcePKey}`));
795
- }));
796
- this.setAlias();
797
- relQuery.rightJoin({
798
- joinModel: rel.TargetModel,
799
- joinTableForeignKey: rel.ForeignKey,
800
- sourceTablePrimaryKey: rel.JunctionModelTargetModelFKey_Name,
801
- callback: callback,
802
- });
803
- this.whereExist(relQuery);
804
- break;
805
- }
806
- }
807
- else {
808
- this._statements.push(this._container.resolve(ExistsQueryStatement, [query, false]));
809
- }
810
- return this;
823
+ return this.buildExistsClause(query, false, callback);
811
824
  }
812
825
  whereNotExists(query, callback) {
813
- let relQuery;
814
- let sourcePKey = '';
815
- const self = this;
816
- let tableName = '';
817
- let tDesc;
818
- if (typeof query === 'string') {
819
- const rel = this._model.getRelationDescriptor(query);
820
- if (!rel) {
821
- throw new OrmException(`relation ${query} not found in model ${this.constructor.name}`);
822
- }
823
- switch (rel.Type) {
824
- case RelationType.One:
825
- this.whereNotNull(rel.ForeignKey);
826
- // simply use right join for condition check
827
- if (callback) {
828
- // TODO: cast fix
829
- this.rightJoin(rel.TargetModel, callback.bind(query));
830
- }
831
- break;
832
- case RelationType.Many:
833
- tableName = rel.TargetModel.getModelDescriptor().TableName;
834
- tDesc = self._model.getModelDescriptor();
835
- // 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
836
- relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
837
- relQuery.where(Lazy.oF(function () {
838
- const sourceAlias = self._tableAlias || (self._parent ? self._parent.TableAlias : tDesc.TableName);
839
- sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
840
- // relQuery is guaranteed assigned above before this lazy callback executes
841
- relQuery.where(new RawQuery(`${rel.ForeignKey} = ${sourcePKey}`));
842
- }));
843
- if (callback) {
844
- callback.apply(relQuery);
845
- }
846
- this.whereNotExists(relQuery);
847
- break;
848
- case RelationType.ManyToMany:
849
- relQuery = rel.JunctionModel.query();
850
- tableName = rel.TargetModel.getModelDescriptor().TableName;
851
- tDesc = self._model.getModelDescriptor();
852
- relQuery = rel.TargetModel.query().setAlias(`${tableName}_exists`);
853
- relQuery.where(Lazy.oF(function () {
854
- const sourceAlias = self._tableAlias || (self._parent ? self._parent.TableAlias : tDesc.TableName);
855
- sourcePKey = `\`${sourceAlias}\`.\`${self._model.getModelDescriptor().PrimaryKey}\``;
856
- // relQuery is guaranteed assigned above before this lazy callback executes
857
- relQuery.where(new RawQuery(`${rel.JunctionModelSourceModelFKey_Name} = ${sourcePKey}`));
858
- }));
859
- relQuery.rightJoin({
860
- joinModel: rel.TargetModel,
861
- joinTableForeignKey: rel.PrimaryKey,
862
- sourceTablePrimaryKey: rel.ForeignKey,
863
- callback: callback,
864
- });
865
- this.whereNotExists(relQuery);
866
- break;
867
- }
826
+ return this.buildExistsClause(query, true, callback);
827
+ }
828
+ /**
829
+ * Shared implementation for {@link whereExist} / {@link whereNotExists}.
830
+ *
831
+ * For a ready sub-query it pushes an {@link ExistsQueryStatement} directly. For a relation
832
+ * name it resolves the matching {@link ExistsRelationHandler} from the container (one per
833
+ * {@link RelationType}) and lets it either mutate this builder or return a correlated
834
+ * sub-query that we then wrap in EXISTS / NOT EXISTS.
835
+ *
836
+ * @param query relation name or a ready sub-query
837
+ * @param negated `true` for NOT EXISTS, `false` for EXISTS
838
+ * @param callback optional where-callback applied to the relation sub-query
839
+ */
840
+ buildExistsClause(query, negated, callback) {
841
+ if (typeof query !== 'string') {
842
+ this.pushStatement(this._container.resolve(ExistsQueryStatement, [query, negated]));
843
+ return this;
868
844
  }
869
- else {
870
- this._statements.push(this._container.resolve(ExistsQueryStatement, [query, true]));
845
+ const rel = this._model.getRelationDescriptor(query);
846
+ if (!rel) {
847
+ throw new OrmException(`relation ${query} not found in model ${this.constructor.name}`);
848
+ }
849
+ const handlers = this._container.resolve(Array.ofType(ExistsRelationHandler));
850
+ const handler = handlers.find((h) => h.Type === rel.Type);
851
+ if (!handler) {
852
+ throw new OrmException(`no ExistsRelationHandler registered for relation type ${rel.Type} (relation ${query} on ${this.constructor.name})`);
853
+ }
854
+ const subquery = handler.apply(this, rel, query, callback);
855
+ if (subquery) {
856
+ this.pushStatement(this._container.resolve(ExistsQueryStatement, [subquery, negated]));
871
857
  }
872
858
  return this;
873
859
  }
874
860
  whereBetween(column, val) {
875
- this._statements.push(this._container.resolve(BetweenStatement, [column, val, false, this.TableAlias]));
861
+ this.pushStatement(this._container.resolve(BetweenStatement, [column, val, false, this.TableAlias]));
876
862
  return this;
877
863
  }
878
864
  whereNotBetween(column, val) {
879
- this._statements.push(this._container.resolve(BetweenStatement, [column, val, true, this.TableAlias]));
865
+ this.pushStatement(this._container.resolve(BetweenStatement, [column, val, true, this.TableAlias]));
880
866
  return this;
881
867
  }
882
868
  whereInSet(column, val) {
883
- this._statements.push(this._container.resolve(InSetStatement, [column, val, false, this.TableAlias]));
869
+ this.pushStatement(this._container.resolve(InSetStatement, [column, val, false, this.TableAlias]));
884
870
  return this;
885
871
  }
886
872
  whereNotInSet(column, val) {
887
- this._statements.push(this._container.resolve(InSetStatement, [column, val, true, this.TableAlias]));
873
+ this.pushStatement(this._container.resolve(InSetStatement, [column, val, true, this.TableAlias]));
888
874
  return this;
889
875
  }
890
876
  clearWhere() {
@@ -924,10 +910,7 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
924
910
  this._distinct = false;
925
911
  this._method = QueryMethod.SELECT;
926
912
  this._boolean = WhereBoolean.AND;
927
- this._sort = {
928
- column: '',
929
- order: SortOrder.NONE,
930
- };
913
+ this._sorts = [];
931
914
  this._first = false;
932
915
  this._limit = {
933
916
  limit: -1,
@@ -942,13 +925,12 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
942
925
  return (await this);
943
926
  }
944
927
  setAlias(alias) {
945
- if (!alias || alias.trim() === '') {
946
- alias = `${this._driver.Options.AliasSeparator}${this._table}${this._driver.Options.AliasSeparator}`;
947
- }
948
- this._tableAlias = alias;
949
- this._columns.forEach((c) => (c.TableAlias = alias));
950
- this._joinStatements.forEach((c) => (c.TableAlias = alias));
951
- this._statements.forEach((c) => (c.TableAlias = alias));
928
+ // isNullOrWhitespace is not a type predicate; a false result guarantees a non-blank string
929
+ const resolvedAlias = isNullOrWhitespace(alias) ? `${this._driver.Options.AliasSeparator}${this._table}${this._driver.Options.AliasSeparator}` : alias;
930
+ this._tableAlias = resolvedAlias;
931
+ this._columns.forEach((c) => (c.TableAlias = resolvedAlias));
932
+ this._joinStatements.forEach((c) => (c.TableAlias = resolvedAlias));
933
+ this._statements.forEach((c) => (c.TableAlias = resolvedAlias));
952
934
  return this;
953
935
  }
954
936
  clone() {
@@ -959,11 +941,18 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
959
941
  builder._joinStatements = this._joinStatements.map(c => c.clone(builder));
960
942
  // Clone statements with mapped WhereBuilder references
961
943
  builder._statements = this._statements.map(c => c.clone(builder));
944
+ // Clone group-by statements (previously dropped, silently losing GROUP BY
945
+ // on a cloned query — eg. orm-api count-pagination clones the builder).
946
+ builder._groupStatements = this._groupStatements.map(c => c.clone(builder));
947
+ // Carry relations and result middlewares over. These hold live objects and
948
+ // are shared the same way mergeRelations()/mergeBuilder() already share them.
949
+ builder._relations = [...this._relations];
950
+ builder._middlewares = [...this._middlewares];
962
951
  /**
963
952
  * ------------------------------------------------------------------
964
953
  */
965
954
  builder._limit = { ...this._limit };
966
- builder._sort = { ...this._sort };
955
+ builder._sorts = this._sorts.map((s) => ({ ...s }));
967
956
  builder._boolean = this._boolean;
968
957
  builder._distinct = this._distinct;
969
958
  builder._table = this._table;
@@ -1062,15 +1051,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
1062
1051
  this._getRelationInstance(relation).execute(callback);
1063
1052
  return this;
1064
1053
  }
1065
- mergeBuilder(builder) {
1054
+ mergeBuilder(builder, includeStatements = true) {
1066
1055
  this._columns = this._columns.concat(builder._columns);
1067
1056
  this._cteStatement = builder._cteStatement;
1068
1057
  this._distinct = builder._distinct;
1069
- this._sort = {
1070
- column: builder._sort.column !== '' ? builder._sort.column : this._sort.column,
1071
- order: builder._sort.order !== '' ? builder._sort.order : this._sort.order,
1072
- };
1073
- this.mergeStatements(builder);
1058
+ // Fold the merged builder's sorts into this query's sorts (multi-column
1059
+ // ORDER BY). If the merged builder has no sorts, this keeps our own.
1060
+ this._sorts = this._sorts.concat(builder._sorts.map((s) => ({ ...s })));
1061
+ // `includeStatements: false` is used by JoinStatement so that a join
1062
+ // callback's WHERE conditions are NOT folded into the main query's WHERE
1063
+ // (which silently turns a LEFT JOIN into an inner filter). The join emits
1064
+ // those conditions in its own ON clause instead. Columns/sort are still
1065
+ // merged so join-callback selects (e.g. extra joined columns) keep working.
1066
+ if (includeStatements) {
1067
+ this.mergeStatements(builder);
1068
+ }
1074
1069
  }
1075
1070
  mergeRelations(builder) {
1076
1071
  this._relations = this._relations.concat(builder._relations);
@@ -1081,6 +1076,21 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
1081
1076
  this._joinStatements = this._joinStatements.concat(builder._joinStatements);
1082
1077
  this._statements = this._statements.concat(stms);
1083
1078
  }
1079
+ /**
1080
+ * Includes soft-deleted rows in the result set by removing the default
1081
+ * `DeletedAt IS NULL` filter added by createQuery for @SoftDelete models.
1082
+ */
1083
+ withDeleted() {
1084
+ const descriptor = extractModelDescriptor(this._model);
1085
+ const deletedAt = descriptor?.SoftDelete?.DeletedAt;
1086
+ if (!deletedAt) {
1087
+ return this;
1088
+ }
1089
+ this._statements = this._statements.filter((s) => {
1090
+ return !(s instanceof WhereStatement && s.Column === deletedAt && s.Operator === SqlOperator.NULL);
1091
+ });
1092
+ return this;
1093
+ }
1084
1094
  min(column, as) {
1085
1095
  this._columns.push(this._container.resolve(ColumnMethodStatement, [column, ColumnMethods.MIN, as, this._tableAlias]));
1086
1096
  return this;
@@ -1122,44 +1132,26 @@ let SelectQueryBuilder = SelectQueryBuilder_1 = class SelectQueryBuilder extends
1122
1132
  return compiler.compile();
1123
1133
  }
1124
1134
  async all() {
1125
- return await this;
1135
+ return await this.execute();
1126
1136
  }
1127
1137
  async resultExists() {
1128
- return this.then((res) => {
1129
- if (Array.isArray(res)) {
1130
- return res.length > 0;
1131
- }
1132
- return res !== undefined && res !== null;
1133
- });
1134
- }
1135
- then(onfulfilled, onrejected) {
1136
- this._queryMiddlewares.forEach((x) => x.beforeQueryExecution(this));
1137
- return super.then((result) => {
1138
- if (this._first) {
1139
- if (Array.isArray(result)) {
1140
- if (result.length !== 0) {
1141
- return onfulfilled?.(result[0]);
1142
- }
1143
- else {
1144
- try {
1145
- return onfulfilled?.(undefined);
1146
- }
1147
- catch (err) {
1148
- onrejected?.(err);
1149
- }
1150
- }
1151
- }
1152
- else {
1153
- return onfulfilled?.(result);
1154
- }
1155
- }
1156
- else {
1157
- return onfulfilled?.(result);
1158
- }
1159
- }, onrejected);
1138
+ const res = await this.execute();
1139
+ if (Array.isArray(res)) {
1140
+ return res.length > 0;
1141
+ }
1142
+ return res !== undefined && res !== null;
1160
1143
  }
1161
- async execute() {
1162
- return (await this);
1144
+ /**
1145
+ * Overrides the engine rather than `execute()` so that the `takeFirst()` unwrapping
1146
+ * happens *inside* the memo. `beforeQueryExecution` is dispatched by `Builder._run()`
1147
+ * for every builder type, so it still fires once per builder rather than once per await.
1148
+ */
1149
+ async _run() {
1150
+ const result = await super._run();
1151
+ if (this._first && Array.isArray(result)) {
1152
+ return (result.length !== 0 ? result[0] : undefined);
1153
+ }
1154
+ return result;
1163
1155
  }
1164
1156
  };
1165
1157
  __decorate([
@@ -1184,6 +1176,10 @@ let DeleteQueryBuilder = class DeleteQueryBuilder extends QueryBuilder {
1184
1176
  offset: -1,
1185
1177
  };
1186
1178
  this.QueryContext = QueryContext.Delete;
1179
+ // Query middlewares (e.g. rbac ownership enforcement) must run for deletes
1180
+ // too, not only selects — otherwise :own permission constraints are never
1181
+ // applied and any row can be deleted.
1182
+ this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
1187
1183
  }
1188
1184
  toDB() {
1189
1185
  return this._container.resolve(DeleteQueryCompiler, [this]).compile();
@@ -1223,6 +1219,9 @@ let OnDuplicateQueryBuilder = class OnDuplicateQueryBuilder {
1223
1219
  this._columnsToUpdate = columns;
1224
1220
  return this;
1225
1221
  }
1222
+ execute() {
1223
+ return this._parent.execute();
1224
+ }
1226
1225
  then(onfulfilled, onrejected) {
1227
1226
  return this._parent.then(onfulfilled, onrejected);
1228
1227
  }
@@ -1246,6 +1245,10 @@ let UpdateQueryBuilder = class UpdateQueryBuilder extends QueryBuilder {
1246
1245
  this._boolean = WhereBoolean.AND;
1247
1246
  this._statements = [];
1248
1247
  this.QueryContext = QueryContext.Update;
1248
+ // Query middlewares (e.g. rbac ownership enforcement) must run for updates
1249
+ // too, not only selects — otherwise :own permission constraints are never
1250
+ // applied and any row can be updated.
1251
+ this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
1249
1252
  }
1250
1253
  in(name) {
1251
1254
  this.setTable(name);
@@ -1281,6 +1284,10 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
1281
1284
  get Replace() {
1282
1285
  return this._replace;
1283
1286
  }
1287
+ /** Columns requested via {@link returning}. Empty when no RETURNING clause was asked for. */
1288
+ get Returning() {
1289
+ return this._returning ?? [];
1290
+ }
1284
1291
  constructor(container, driver, model) {
1285
1292
  super(container, driver, model);
1286
1293
  this._update = false;
@@ -1289,6 +1296,33 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
1289
1296
  this._columns = [];
1290
1297
  this._values = [];
1291
1298
  this.QueryContext = QueryContext.Insert;
1299
+ // Inserts get the same middleware pass as the other three builders. Note that a
1300
+ // middleware which needs to see or amend the row payload must use
1301
+ // `beforeQueryExecution` instead — at construction time `values()` has not been called.
1302
+ this._queryMiddlewares.forEach((x) => x.afterQueryCreation(this));
1303
+ }
1304
+ /**
1305
+ * Forces `column` to `value` on every row of the payload, overwriting whatever the caller
1306
+ * supplied.
1307
+ *
1308
+ * This is the write path for a policy that must not be negotiable — rbac's `createOwn`
1309
+ * ownership stamp. `values()` cannot serve: it appends rows and takes the column list from
1310
+ * its own argument, so calling it a second time adds a row rather than amending the
1311
+ * existing ones.
1312
+ *
1313
+ * @param column - column to overwrite on every row
1314
+ * @param value - value to force
1315
+ */
1316
+ forceColumn(column, value) {
1317
+ const existing = this._columns.findIndex((c) => !(c.Column instanceof RawQuery) && c.Column === column);
1318
+ if (existing === -1) {
1319
+ this.columns([...this._columns.filter((c) => !(c.Column instanceof RawQuery)).map((c) => c.Column), column]);
1320
+ this._values.forEach((row) => row.push(value));
1321
+ return this;
1322
+ }
1323
+ // eslint-disable-next-line security/detect-object-injection
1324
+ this._values.forEach((row) => (row[existing] = value));
1325
+ return this;
1292
1326
  }
1293
1327
  /**
1294
1328
  * Sets insert to ignore on duplicate
@@ -1301,8 +1335,21 @@ let InsertQueryBuilder = class InsertQueryBuilder extends QueryBuilder {
1301
1335
  this._replace = true;
1302
1336
  return this;
1303
1337
  }
1338
+ /**
1339
+ * Asks the dialect to echo the given columns of every inserted row back.
1340
+ *
1341
+ * @throws NotSupported on drivers whose `supportedFeatures().insertReturning` is false —
1342
+ * silently doing nothing is how this API was a no-op on MySQL and MSSQL for years.
1343
+ */
1304
1344
  returning(columns) {
1345
+ if (!this.Driver.supportedFeatures().insertReturning) {
1346
+ throw new NotSupported(`driver ${this.Driver.Options.Driver} does not support RETURNING on INSERT`);
1347
+ }
1305
1348
  this._returning = columns;
1349
+ // onDuplicate() sets Upsert unconditionally and wins if it runs afterwards.
1350
+ if (this.QueryContext === QueryContext.Insert) {
1351
+ this.QueryContext = QueryContext.InsertReturning;
1352
+ }
1306
1353
  return this;
1307
1354
  }
1308
1355
  values(data) {
@@ -1464,6 +1511,7 @@ let ColumnQueryBuilder = class ColumnQueryBuilder {
1464
1511
  this.Comment = '';
1465
1512
  this.Unique = false;
1466
1513
  this.Unsigned = false;
1514
+ this.InlinePrimaryKey = true;
1467
1515
  this.Args.push(...args);
1468
1516
  }
1469
1517
  notNull() {
@@ -1634,10 +1682,11 @@ AlterTableQueryBuilder = __decorate([
1634
1682
  export { AlterTableQueryBuilder };
1635
1683
  let TableQueryBuilder = class TableQueryBuilder extends QueryBuilder {
1636
1684
  /**
1637
- * Alias for string(name, 36 )
1685
+ * Alias for binary(name, 16 ) - uuids are stored as 16-byte BINARY to match
1686
+ * the UuidConverter ( which writes a dashed uuid as a 16-byte buffer ).
1638
1687
  */
1639
1688
  uuid(name) {
1640
- return this.string(name, 36);
1689
+ return this.binary(name, 16);
1641
1690
  }
1642
1691
  ifExists() {
1643
1692
  this._checkExists = true;
@@ -2043,6 +2092,16 @@ export function createQuery(model, query, injectModel = true) {
2043
2092
  }
2044
2093
  qr.middleware(new DiscriminationMapMiddleware(dsc));
2045
2094
  qr.setTable(dsc.TableName);
2095
+ // Soft-delete read filtering: by default exclude rows that have been soft
2096
+ // deleted (DeletedAt IS NOT NULL). SelectQueryBuilder.withDeleted() removes
2097
+ // this default statement to include soft-deleted rows again.
2098
+ // Guarded on the DeletedAt column actually being present in the model's
2099
+ // reflected columns — a filter on a column the schema does not expose is
2100
+ // impossible anyway, and the guard keeps queries working when table info
2101
+ // has not (yet) surfaced the column.
2102
+ if (qr instanceof SelectQueryBuilder && dsc.SoftDelete?.DeletedAt && dsc.Columns?.some((c) => c.Name === dsc.SoftDelete.DeletedAt)) {
2103
+ qr.whereNull(dsc.SoftDelete.DeletedAt);
2104
+ }
2046
2105
  if (driver.Options.Database) {
2047
2106
  qr.database(driver.Options.Database);
2048
2107
  }