joist-core 2.3.0-next.60 → 2.3.0-next.61

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 (85) hide show
  1. package/build/Aliases.cjs +345 -120
  2. package/build/Aliases.cjs.map +1 -1
  3. package/build/Aliases.d.cts +136 -51
  4. package/build/Aliases.d.cts.map +1 -1
  5. package/build/Aliases.d.mts +136 -51
  6. package/build/Aliases.d.mts.map +1 -1
  7. package/build/Aliases.js +320 -99
  8. package/build/Aliases.js.map +1 -1
  9. package/build/ConditionBuilder.cjs +2 -1
  10. package/build/ConditionBuilder.cjs.map +1 -1
  11. package/build/ConditionBuilder.js +2 -1
  12. package/build/ConditionBuilder.js.map +1 -1
  13. package/build/EntityManager.cjs +12 -0
  14. package/build/EntityManager.cjs.map +1 -1
  15. package/build/EntityManager.d.cts +27 -0
  16. package/build/EntityManager.d.cts.map +1 -1
  17. package/build/EntityManager.d.mts +27 -0
  18. package/build/EntityManager.d.mts.map +1 -1
  19. package/build/EntityManager.js +12 -0
  20. package/build/EntityManager.js.map +1 -1
  21. package/build/Expr.cjs +374 -0
  22. package/build/Expr.cjs.map +1 -0
  23. package/build/Expr.d.cts +266 -0
  24. package/build/Expr.d.cts.map +1 -0
  25. package/build/Expr.d.mts +266 -0
  26. package/build/Expr.d.mts.map +1 -0
  27. package/build/Expr.js +355 -0
  28. package/build/Expr.js.map +1 -0
  29. package/build/QueryParser.cjs +39 -16
  30. package/build/QueryParser.cjs.map +1 -1
  31. package/build/QueryParser.d.cts +3 -3
  32. package/build/QueryParser.d.cts.map +1 -1
  33. package/build/QueryParser.d.mts +3 -3
  34. package/build/QueryParser.d.mts.map +1 -1
  35. package/build/QueryParser.js +39 -16
  36. package/build/QueryParser.js.map +1 -1
  37. package/build/changes.cjs.map +1 -1
  38. package/build/changes.d.cts +2 -0
  39. package/build/changes.d.cts.map +1 -1
  40. package/build/changes.d.mts +2 -0
  41. package/build/changes.d.mts.map +1 -1
  42. package/build/changes.js.map +1 -1
  43. package/build/index.cjs +9 -1
  44. package/build/index.cjs.map +1 -1
  45. package/build/index.d.cts +7 -5
  46. package/build/index.d.cts.map +1 -1
  47. package/build/index.d.mts +7 -5
  48. package/build/index.d.mts.map +1 -1
  49. package/build/index.js +4 -2
  50. package/build/index.js.map +1 -1
  51. package/build/keywords.cjs +16 -1
  52. package/build/keywords.cjs.map +1 -1
  53. package/build/keywords.d.cts +12 -2
  54. package/build/keywords.d.cts.map +1 -1
  55. package/build/keywords.d.mts +12 -2
  56. package/build/keywords.d.mts.map +1 -1
  57. package/build/keywords.js +16 -2
  58. package/build/keywords.js.map +1 -1
  59. package/build/query.cjs +712 -0
  60. package/build/query.cjs.map +1 -0
  61. package/build/query.d.cts +348 -0
  62. package/build/query.d.cts.map +1 -0
  63. package/build/query.d.mts +348 -0
  64. package/build/query.d.mts.map +1 -0
  65. package/build/query.js +706 -0
  66. package/build/query.js.map +1 -0
  67. package/build/rules.cjs.map +1 -1
  68. package/build/rules.d.cts +13 -1
  69. package/build/rules.d.cts.map +1 -1
  70. package/build/rules.d.mts +13 -1
  71. package/build/rules.d.mts.map +1 -1
  72. package/build/rules.js.map +1 -1
  73. package/build/serde.cjs +65 -21
  74. package/build/serde.cjs.map +1 -1
  75. package/build/serde.d.cts +22 -0
  76. package/build/serde.d.cts.map +1 -1
  77. package/build/serde.d.mts +22 -0
  78. package/build/serde.d.mts.map +1 -1
  79. package/build/serde.js +65 -21
  80. package/build/serde.js.map +1 -1
  81. package/build/typeMap.d.cts +20 -1
  82. package/build/typeMap.d.cts.map +1 -1
  83. package/build/typeMap.d.mts +20 -1
  84. package/build/typeMap.d.mts.map +1 -1
  85. package/package.json +2 -2
package/build/Aliases.cjs CHANGED
@@ -1,9 +1,11 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("./typeMap.cjs");
3
+ const require_keywords = require("./keywords.cjs");
3
4
  const require_keys = require("./keys.cjs");
4
5
  require("./serde.cjs");
5
6
  const require_EntityMetadata = require("./EntityMetadata.cjs");
6
7
  const require_utils = require("./utils.cjs");
8
+ const require_Expr = require("./Expr.cjs");
7
9
  require("./index.cjs");
8
10
  require("./EntityManager.cjs");
9
11
  require("./Entity.cjs");
@@ -11,8 +13,7 @@ const require_configure = require("./configure.cjs");
11
13
  const require_QueryParser = require("./QueryParser.cjs");
12
14
  let joist_utils = require("joist-utils");
13
15
  //#region src/Aliases.ts
14
- /** Creates an alias for complex filtering against `T`. */
15
- function alias(cstr) {
16
+ function alias(cstr, _name) {
16
17
  return newAliasProxy(cstr);
17
18
  }
18
19
  /** Creates multiple aliases for complex filtering. */
@@ -25,32 +26,29 @@ function getAliasMgmt(alias) {
25
26
  }
26
27
  /** Returns the metadata for the entity that `alias` is bound to. */
27
28
  function getAliasMetadata(alias) {
28
- const mgmt = alias[aliasMgmt];
29
- return require_configure.getMetadataForTable(mgmt.tableName);
29
+ return alias[aliasMgmt].meta;
30
30
  }
31
31
  function newAliasProxy(cstr) {
32
32
  const meta = require_EntityMetadata.getMetadata(cstr);
33
- const callbacks = [];
34
33
  const mgmt = {
35
34
  tableName: meta.tableName,
36
- setAlias(newMeta, newAlias) {
37
- for (const callback of callbacks) callback(newMeta, newAlias);
38
- },
39
- onBind(callback) {
40
- callbacks.push(callback);
41
- }
35
+ meta
42
36
  };
43
- return new Proxy(cstr, {
44
- /** Create a column alias for the given field. */
37
+ const proxy = new Proxy(cstr, {
38
+ /** Create a column alias, or a relation join factory, for the given field. */
45
39
  get(_, key) {
46
40
  if (key === aliasMgmt) return mgmt;
47
41
  const field = meta.allFields[key] ?? require_utils.fail(`No field ${String(key)} on ${cstr.name}`);
48
42
  switch (field.kind) {
49
43
  case "primaryKey":
50
44
  case "primitive":
51
- case "enum": return new PrimitiveAliasImpl(meta, field, callbacks, field.serde.columns[0]);
52
- case "m2o": return new EntityAliasImpl(meta, field, callbacks, field.serde.columns[0]);
53
- case "poly": return new PolyReferenceAlias(meta, callbacks, field);
45
+ case "enum": return new PrimitiveAliasImpl(meta, field, field.serde.columns[0], mgmt);
46
+ case "m2o": return new EntityAliasImpl(meta, field, field.serde.columns[0], mgmt);
47
+ case "poly": return new PolyReferenceAlias(meta, mgmt, field);
48
+ case "o2m":
49
+ case "o2o":
50
+ case "lo2m": return new OneToManyAliasImpl(proxy, field);
51
+ case "m2m": return new ManyToManyAliasImpl(proxy, field);
54
52
  default: throw new Error(`Unsupported alias field kind ${field.kind}`);
55
53
  }
56
54
  },
@@ -58,20 +56,48 @@ function newAliasProxy(cstr) {
58
56
  return key === aliasMgmt || key in meta.allFields;
59
57
  }
60
58
  });
59
+ return proxy;
61
60
  }
62
61
  function isAlias(obj) {
63
62
  return obj && typeof obj === "function" && obj[aliasMgmt] !== void 0;
64
63
  }
65
- var AbstractAliasColumn = class {
64
+ /**
65
+ * A single column of an alias.
66
+ *
67
+ * For `em.find`, its methods create `ColumnCondition`s that resolve their alias against each parse's
68
+ * join-literal bindings.
69
+ *
70
+ * For `em.query`, the column also implements `Expr`: it renders as `alias."column"`, decodes result
71
+ * values through the field's serde, and inherits aggregate methods from `BaseExpr`.
72
+ */
73
+ var AbstractAliasColumn = class AbstractAliasColumn extends require_Expr.BaseExpr {
66
74
  meta;
67
75
  field;
68
- callbacks;
69
76
  column;
70
- constructor(meta, field, callbacks, column) {
77
+ mgmt;
78
+ constructor(meta, field, column, mgmt) {
79
+ super();
71
80
  this.meta = meta;
72
81
  this.field = field;
73
- this.callbacks = callbacks;
74
82
  this.column = column;
83
+ this.mgmt = mgmt;
84
+ }
85
+ toSql(ctx) {
86
+ const alias = ctx.aliasFor(this.mgmt);
87
+ const ctiAlias = getMaybeCtiAlias(this.meta, this.field, this.meta, alias);
88
+ return {
89
+ sql: require_keywords.kqDot(ctiAlias, this.column.columnName),
90
+ bindings: [],
91
+ refs: [alias]
92
+ };
93
+ }
94
+ /** Decodes a result-set value the same way `hydrate` would, i.e. an int into a tagged id. */
95
+ decode(value) {
96
+ if (value === null || value === void 0) return value;
97
+ return this.column.mapFromDb(value);
98
+ }
99
+ encode(value) {
100
+ return this.column.mapToDb(value);
75
101
  }
76
102
  addCondition(value) {
77
103
  const cond = {
@@ -81,128 +107,121 @@ var AbstractAliasColumn = class {
81
107
  dbType: this.column.dbType,
82
108
  cond: require_QueryParser.mapToDb(this.column, value)
83
109
  };
84
- this.callbacks.push((newMeta, newAlias) => {
85
- cond.alias = getMaybeCtiAlias(this.meta, this.field, newMeta, newAlias);
110
+ return require_Expr.withDeferredAlias(cond, (resolve) => {
111
+ const r = resolve(this.mgmt);
112
+ cond.alias = getMaybeCtiAlias(this.meta, this.field, r.meta, r.alias);
86
113
  });
87
- return cond;
88
114
  }
89
115
  addRawCondition(exp, bindings) {
90
116
  const cond = {
91
117
  kind: "raw",
92
118
  aliases: [],
93
- condition: `unset.${this.column.columnName} ${exp}`,
119
+ condition: "unset",
94
120
  pruneable: false,
95
121
  bindings
96
122
  };
97
- this.callbacks.push((newMeta, newAlias) => {
98
- const alias = getMaybeCtiAlias(this.meta, this.field, newMeta, newAlias);
99
- if (!cond.aliases.includes(alias)) cond.aliases.push(alias);
100
- cond.condition = cond.condition.replace("unset", alias);
123
+ return require_Expr.withDeferredAlias(cond, (resolve) => {
124
+ const r = resolve(this.mgmt);
125
+ const alias = getMaybeCtiAlias(this.meta, this.field, r.meta, r.alias);
126
+ cond.aliases = [alias];
127
+ cond.condition = `${alias}.${this.column.columnName} ${exp}`;
101
128
  });
102
- return cond;
103
129
  }
104
- addCrossColumnRawCondition(otherColumn, op) {
105
- const cond = {
106
- kind: "raw",
107
- aliases: [],
108
- condition: `unset1.${this.column.columnName} ${op} unset2.${otherColumn.column.columnName}`,
109
- pruneable: false,
110
- bindings: []
111
- };
112
- this.callbacks.push((newMeta, newAlias) => {
113
- const alias = getMaybeCtiAlias(this.meta, this.field, newMeta, newAlias);
114
- if (!cond.aliases.includes(alias)) cond.aliases.push(alias);
115
- cond.condition = cond.condition.replace("unset1", alias);
116
- });
117
- otherColumn.callbacks.push((newMeta, newAlias) => {
118
- const alias = getMaybeCtiAlias(otherColumn.meta, otherColumn.field, newMeta, newAlias);
119
- if (!cond.aliases.includes(alias)) cond.aliases.push(alias);
120
- cond.condition = cond.condition.replace("unset2", alias);
121
- });
122
- return cond;
130
+ /**
131
+ * Compares this column to another expression.
132
+ *
133
+ * Another alias column becomes a `DeferredAliasCondition`, which both parsers can resolve, so it also
134
+ * works inside `em.find`'s complex conditions; every other expression (an aggregate, a subquery column,
135
+ * a `sql` template) only exists in `em.query`, so it takes the `ExprContext`-deferred path, which only
136
+ * the `em.query` parser resolves.
137
+ */
138
+ compareToExpr(op, value) {
139
+ if (value instanceof AbstractAliasColumn) return newCrossColumnCondition(this.meta, this.field, this.mgmt, this.column.columnName, value, op);
140
+ return this.compare(op, value);
123
141
  }
124
142
  };
125
- var PrimitiveAliasImpl = class PrimitiveAliasImpl extends AbstractAliasColumn {
143
+ var PrimitiveAliasImpl = class extends AbstractAliasColumn {
126
144
  eq(value) {
127
- if (value === void 0) return require_QueryParser.skipCondition;
128
- else if (value instanceof PrimitiveAliasImpl) return this.addCrossColumnRawCondition(value, "=");
129
- else if (value === null) return this.addCondition({ kind: "is-null" });
130
- else return this.addCondition({
145
+ if (value === void 0) return require_Expr.skipCondition;
146
+ if (value === null) return this.addCondition({ kind: "is-null" });
147
+ if (require_Expr.isExpr(value)) return this.compareToExpr("=", value);
148
+ return this.addCondition({
131
149
  kind: "eq",
132
150
  value
133
151
  });
134
152
  }
135
153
  ne(value) {
136
- if (value === void 0) return require_QueryParser.skipCondition;
137
- else if (value instanceof PrimitiveAliasImpl) return this.addCrossColumnRawCondition(value, "!=");
138
- else if (value === null) return this.addCondition({ kind: "not-null" });
139
- else return this.addCondition({
154
+ if (value === void 0) return require_Expr.skipCondition;
155
+ if (value === null) return this.addCondition({ kind: "not-null" });
156
+ if (require_Expr.isExpr(value)) return this.compareToExpr("!=", value);
157
+ return this.addCondition({
140
158
  kind: "ne",
141
159
  value
142
160
  });
143
161
  }
144
162
  gt(value) {
145
- if (value === void 0) return require_QueryParser.skipCondition;
146
- else if (value instanceof PrimitiveAliasImpl) return this.addCrossColumnRawCondition(value, ">");
147
- else return this.addCondition({
163
+ if (value === void 0) return require_Expr.skipCondition;
164
+ if (require_Expr.isExpr(value)) return this.compareToExpr(">", value);
165
+ return this.addCondition({
148
166
  kind: "gt",
149
167
  value
150
168
  });
151
169
  }
152
170
  gte(value) {
153
- if (value === void 0) return require_QueryParser.skipCondition;
154
- else if (value instanceof PrimitiveAliasImpl) return this.addCrossColumnRawCondition(value, ">=");
155
- else return this.addCondition({
171
+ if (value === void 0) return require_Expr.skipCondition;
172
+ if (require_Expr.isExpr(value)) return this.compareToExpr(">=", value);
173
+ return this.addCondition({
156
174
  kind: "gte",
157
175
  value
158
176
  });
159
177
  }
160
178
  lt(value) {
161
- if (value === void 0) return require_QueryParser.skipCondition;
162
- else if (value instanceof PrimitiveAliasImpl) return this.addCrossColumnRawCondition(value, "<");
163
- else return this.addCondition({
179
+ if (value === void 0) return require_Expr.skipCondition;
180
+ if (require_Expr.isExpr(value)) return this.compareToExpr("<", value);
181
+ return this.addCondition({
164
182
  kind: "lt",
165
183
  value
166
184
  });
167
185
  }
168
186
  lte(value) {
169
- if (value === void 0) return require_QueryParser.skipCondition;
170
- else if (value instanceof PrimitiveAliasImpl) return this.addCrossColumnRawCondition(value, "<=");
171
- else return this.addCondition({
187
+ if (value === void 0) return require_Expr.skipCondition;
188
+ if (require_Expr.isExpr(value)) return this.compareToExpr("<=", value);
189
+ return this.addCondition({
172
190
  kind: "lte",
173
191
  value
174
192
  });
175
193
  }
176
194
  between(v1, v2) {
177
- if (v1 === void 0 || v2 === void 0) return require_QueryParser.skipCondition;
195
+ if (v1 === void 0 || v2 === void 0) return require_Expr.skipCondition;
178
196
  return this.addCondition({
179
197
  kind: "between",
180
198
  value: [v1, v2]
181
199
  });
182
200
  }
183
201
  like(value) {
184
- if (value === void 0) return require_QueryParser.skipCondition;
202
+ if (value === void 0) return require_Expr.skipCondition;
185
203
  return this.addCondition({
186
204
  kind: "like",
187
205
  value
188
206
  });
189
207
  }
190
208
  ilike(value) {
191
- if (value === void 0) return require_QueryParser.skipCondition;
209
+ if (value === void 0) return require_Expr.skipCondition;
192
210
  return this.addCondition({
193
211
  kind: "ilike",
194
212
  value
195
213
  });
196
214
  }
197
215
  search(value) {
198
- if (!value) return require_QueryParser.skipCondition;
216
+ if (!value) return require_Expr.skipCondition;
199
217
  return this.addCondition({
200
218
  kind: "ilike",
201
219
  value: require_QueryParser.makeLike(value)
202
220
  });
203
221
  }
204
222
  in(values) {
205
- if (values === void 0) return require_QueryParser.skipCondition;
223
+ if (values === void 0) return require_Expr.skipCondition;
224
+ if (require_Expr.isExpr(values)) return this.inList("IN", values);
206
225
  if (values.includes(null)) return { or: [this.addCondition({ kind: "is-null" }), this.addCondition({
207
226
  kind: "in",
208
227
  value: values.filter((v) => v !== null)
@@ -212,124 +231,154 @@ var PrimitiveAliasImpl = class PrimitiveAliasImpl extends AbstractAliasColumn {
212
231
  value: values
213
232
  });
214
233
  }
234
+ nin(values) {
235
+ if (values === void 0) return require_Expr.skipCondition;
236
+ if (require_Expr.isExpr(values)) return this.inList("NOT IN", values);
237
+ return this.addCondition({
238
+ kind: "nin",
239
+ value: values.filter((v) => v !== null)
240
+ });
241
+ }
215
242
  contains(v1) {
216
- if (v1 === void 0) return require_QueryParser.skipCondition;
243
+ if (v1 === void 0) return require_Expr.skipCondition;
217
244
  return this.addCondition({
218
245
  kind: "contains",
219
246
  value: v1
220
247
  });
221
248
  }
222
249
  ncontains(v1) {
223
- if (v1 === void 0) return require_QueryParser.skipCondition;
250
+ if (v1 === void 0) return require_Expr.skipCondition;
224
251
  return this.addCondition({
225
252
  kind: "ncontains",
226
253
  value: v1
227
254
  });
228
255
  }
229
256
  overlaps(v1) {
230
- if (v1 === void 0) return require_QueryParser.skipCondition;
257
+ if (v1 === void 0) return require_Expr.skipCondition;
231
258
  return this.addCondition({
232
259
  kind: "overlaps",
233
260
  value: v1
234
261
  });
235
262
  }
236
263
  noverlaps(v1) {
237
- if (v1 === void 0) return require_QueryParser.skipCondition;
264
+ if (v1 === void 0) return require_Expr.skipCondition;
238
265
  return this.addCondition({
239
266
  kind: "noverlaps",
240
267
  value: v1
241
268
  });
242
269
  }
243
270
  pathExists(jsonPath) {
244
- if (jsonPath === void 0) return require_QueryParser.skipCondition;
271
+ if (jsonPath === void 0) return require_Expr.skipCondition;
245
272
  return this.addCondition({
246
273
  kind: "jsonPathExists",
247
274
  value: jsonPath
248
275
  });
249
276
  }
250
277
  pathIsTrue(jsonPath) {
251
- if (jsonPath === void 0) return require_QueryParser.skipCondition;
278
+ if (jsonPath === void 0) return require_Expr.skipCondition;
252
279
  return this.addCondition({
253
280
  kind: "jsonPathPredicate",
254
281
  value: jsonPath
255
282
  });
256
283
  }
257
284
  raw(exp, bindings) {
258
- if (bindings === void 0) return require_QueryParser.skipCondition;
285
+ if (bindings === void 0) return require_Expr.skipCondition;
259
286
  return this.addRawCondition(exp, bindings);
260
287
  }
261
288
  };
262
289
  var EntityAliasImpl = class extends AbstractAliasColumn {
263
290
  eq(value) {
264
- if (value === void 0) return require_QueryParser.skipCondition;
265
- else if (value === null) return this.addCondition({ kind: "is-null" });
266
- else return this.addCondition({
291
+ if (value === void 0) return require_Expr.skipCondition;
292
+ if (value === null) return this.addCondition({ kind: "is-null" });
293
+ if (require_Expr.isExpr(value)) return this.compareToExpr("=", value);
294
+ return this.addCondition({
267
295
  kind: "eq",
268
296
  value
269
297
  });
270
298
  }
271
299
  ne(value) {
272
- if (value === void 0) return require_QueryParser.skipCondition;
273
- else if (value === null) return this.addCondition({ kind: "not-null" });
274
- else return this.addCondition({
300
+ if (value === void 0) return require_Expr.skipCondition;
301
+ if (value === null) return this.addCondition({ kind: "not-null" });
302
+ if (require_Expr.isExpr(value)) return this.compareToExpr("!=", value);
303
+ return this.addCondition({
275
304
  kind: "ne",
276
305
  value
277
306
  });
278
307
  }
279
308
  in(values) {
280
- if (values === void 0) return require_QueryParser.skipCondition;
309
+ if (values === void 0) return require_Expr.skipCondition;
281
310
  else if (values === null) throw new Error("Unsupported");
311
+ else if (require_Expr.isExpr(values)) return this.inList("IN", values);
312
+ else if (values.includes(null)) return { or: [this.addCondition({ kind: "is-null" }), this.addCondition({
313
+ kind: "in",
314
+ value: values.filter((v) => v !== null)
315
+ })] };
282
316
  else return this.addCondition({
283
317
  kind: "in",
284
318
  value: values
285
319
  });
286
320
  }
287
- gt(value) {
288
- if (value === void 0) return require_QueryParser.skipCondition;
289
- else if (value === null) throw new Error("Unsupported");
321
+ nin(values) {
322
+ if (values === void 0) return require_Expr.skipCondition;
323
+ else if (values === null) throw new Error("Unsupported");
324
+ else if (require_Expr.isExpr(values)) return this.inList("NOT IN", values);
290
325
  else return this.addCondition({
291
- kind: "gt",
292
- value
326
+ kind: "nin",
327
+ value: values.filter((v) => v !== null)
293
328
  });
294
329
  }
330
+ gt(value) {
331
+ return this.compareId(">", "gt", value);
332
+ }
295
333
  gte(value) {
296
- if (value === void 0) return require_QueryParser.skipCondition;
297
- else if (value === null) throw new Error("Unsupported");
298
- else return this.addCondition({
299
- kind: "gte",
300
- value
301
- });
334
+ return this.compareId(">=", "gte", value);
302
335
  }
303
336
  lt(value) {
304
- if (value === void 0) return require_QueryParser.skipCondition;
305
- else if (value === null) throw new Error("Unsupported");
306
- else return this.addCondition({
307
- kind: "lt",
308
- value
309
- });
337
+ return this.compareId("<", "lt", value);
310
338
  }
311
339
  lte(value) {
312
- if (value === void 0) return require_QueryParser.skipCondition;
313
- else if (value === null) throw new Error("Unsupported");
314
- else return this.addCondition({
315
- kind: "lte",
316
- value
317
- });
340
+ return this.compareId("<=", "lte", value);
318
341
  }
319
342
  raw(exp, bindings) {
320
- if (bindings === void 0) return require_QueryParser.skipCondition;
343
+ if (bindings === void 0) return require_Expr.skipCondition;
321
344
  return this.addRawCondition(exp, bindings);
322
345
  }
346
+ /** Joins `other` via this FK, INNER for a required reference and LEFT for a nullable one. */
347
+ as(other) {
348
+ return this.joinEntry(this.field.required ? "inner" : "left", other);
349
+ }
350
+ inner(other) {
351
+ return this.joinEntry("inner", other);
352
+ }
353
+ left(other) {
354
+ return this.joinEntry("left", other);
355
+ }
356
+ joinEntry(kind, other) {
357
+ return {
358
+ [kind]: requireAlias(other),
359
+ on: this.eq(idColumnOf(other))
360
+ };
361
+ }
362
+ compareId(op, kind, value) {
363
+ if (value === void 0) return require_Expr.skipCondition;
364
+ if (value === null) throw new Error("Unsupported");
365
+ if (require_Expr.isExpr(value)) return this.compareToExpr(op, value);
366
+ return this.addCondition({
367
+ kind,
368
+ value
369
+ });
370
+ }
323
371
  };
324
372
  var PolyReferenceAlias = class {
325
373
  meta;
326
- callbacks;
374
+ mgmt;
327
375
  field;
328
- constructor(meta, callbacks, field) {
376
+ constructor(meta, mgmt, field) {
329
377
  this.meta = meta;
330
- this.callbacks = callbacks;
378
+ this.mgmt = mgmt;
331
379
  this.field = field;
332
380
  }
381
+ /** Compares to a tagged id, an entity, or another alias's id column, which picks the component (`c.parent.eq(a.id)`). */
333
382
  eq(value) {
334
383
  return this.addEqOrNe("eq", value);
335
384
  }
@@ -337,7 +386,8 @@ var PolyReferenceAlias = class {
337
386
  return this.addEqOrNe("ne", value);
338
387
  }
339
388
  in(values) {
340
- if (values === void 0) return require_QueryParser.skipCondition;
389
+ if (values === void 0) return require_Expr.skipCondition;
390
+ if (require_Expr.isExpr(values)) return this.inSubquery(values);
341
391
  const idsByConstructor = (0, joist_utils.groupBy)(values, (id) => require_configure.getConstructorFromTaggedId(require_keys.maybeResolveReferenceToId(id)).name);
342
392
  return { or: Object.entries(idsByConstructor).map(([cstrName, ids]) => {
343
393
  const comp = this.field.components.find((p) => p.otherMetadata().cstr.name === cstrName) ?? require_utils.fail(`No component for ${cstrName}`);
@@ -347,8 +397,48 @@ var PolyReferenceAlias = class {
347
397
  });
348
398
  }) };
349
399
  }
400
+ /** Joins `other` via this poly's component for its entity, INNER when the poly is required. */
401
+ as(other) {
402
+ return this.joinEntry(this.field.required ? "inner" : "left", other);
403
+ }
404
+ inner(other) {
405
+ return this.joinEntry("inner", other);
406
+ }
407
+ left(other) {
408
+ return this.joinEntry("left", other);
409
+ }
410
+ joinEntry(kind, other) {
411
+ return {
412
+ [kind]: requireAlias(other),
413
+ on: this.eq(idColumnOf(other))
414
+ };
415
+ }
416
+ /**
417
+ * i.e. `c.parent.in(query({ from: a, ..., select: a.id }))`: like `eq` against an alias column, the
418
+ * subquery's select column picks the component, i.e. authors pick `parent_author_id`.
419
+ */
420
+ inSubquery(values) {
421
+ const select = require_Expr.asNode(values).subquerySelect;
422
+ if (!(select instanceof AbstractAliasColumn)) return require_utils.fail(`${this.field.fieldName} is polymorphic, so \`in\` needs a subquery selecting an id or FK column`);
423
+ const otherMeta = select.field.kind === "primaryKey" ? select.meta : select.field.kind === "m2o" ? select.field.otherMetadata() : require_utils.fail(`${this.field.fieldName} \`in\` needs an id or FK column, got ${select.field.fieldName}`);
424
+ const comp = this.field.components.find((p) => require_EntityMetadata.getBaseAndSelfMetas(otherMeta).includes(p.otherMetadata())) ?? require_utils.fail(`${this.field.fieldName} has no component for ${otherMeta.type}`);
425
+ return require_Expr.deferredCondition((ctx) => {
426
+ const alias = getMaybeCtiAlias(this.meta, this.field, this.meta, ctx.aliasFor(this.mgmt));
427
+ const sub = require_Expr.asNode(values).toSqlBare(ctx);
428
+ return {
429
+ sql: `${alias}.${comp.columnName} IN (${sub.sql})`,
430
+ bindings: sub.bindings,
431
+ refs: [alias, ...sub.refs]
432
+ };
433
+ });
434
+ }
350
435
  addEqOrNe(kind, value) {
351
- if (value === void 0) return require_QueryParser.skipCondition;
436
+ if (value === void 0) return require_Expr.skipCondition;
437
+ else if (value instanceof AbstractAliasColumn) {
438
+ const otherMeta = value.meta;
439
+ const comp = this.field.components.find((p) => require_EntityMetadata.getBaseAndSelfMetas(otherMeta).includes(p.otherMetadata())) ?? require_utils.fail(`${this.field.fieldName} has no component for ${otherMeta.type}`);
440
+ return newCrossColumnCondition(this.meta, this.field, this.mgmt, comp.columnName, value, kind === "eq" ? "=" : "!=");
441
+ } else if (require_Expr.isExpr(value)) return require_utils.fail(`${this.field.fieldName} is polymorphic, so it can only be compared to tagged ids or entity alias columns`);
352
442
  else if (value === null) {
353
443
  const value = kind === "eq" ? { kind: "is-null" } : { kind: "not-null" };
354
444
  return { and: this.field.components.map((p) => this.addCondition(p, value)) };
@@ -369,10 +459,10 @@ var PolyReferenceAlias = class {
369
459
  dbType: this.field.serde.columns[0].dbType,
370
460
  cond: require_QueryParser.mapToDb(column, value)
371
461
  };
372
- this.callbacks.push((newMeta, newAlias) => {
373
- cond.alias = getMaybeCtiAlias(this.meta, this.field, newMeta, newAlias);
462
+ return require_Expr.withDeferredAlias(cond, (resolve) => {
463
+ const r = resolve(this.mgmt);
464
+ cond.alias = getMaybeCtiAlias(this.meta, this.field, r.meta, r.alias);
374
465
  });
375
- return cond;
376
466
  }
377
467
  };
378
468
  /**
@@ -385,13 +475,148 @@ function getMaybeCtiAlias(meta, field, newMeta, newAlias) {
385
475
  }
386
476
  return `${newAlias}${field.aliasSuffix}`;
387
477
  }
478
+ /**
479
+ * Marks a sugar join entry whose target is a *collection* (o2m/m2m), which filter soft-deletes like
480
+ * em.find's "collections filter out soft-deletes, but m2o/o2o references still return them"; see
481
+ * `QueryParser.addSoftDeleteCondition`'s call site. Reference joins and explicit joins are unmarked.
482
+ */
483
+ const collectionJoin = Symbol("joist.collectionJoin");
484
+ /** Marks a sugar m2m join entry (`a.tags.as(t)`) with its hidden join-table join; `parseQuery` expands it. */
485
+ const m2mJoinTable = Symbol("joist.m2mJoinTable");
486
+ /** The runtime identity of an m2m join table in a query, i.e. `authors_to_tags`; it has no entity. */
487
+ var JoinTableHandle = class {
488
+ joinTableName;
489
+ constructor(joinTableName) {
490
+ this.joinTableName = joinTableName;
491
+ }
492
+ };
493
+ /**
494
+ * The `as`/`inner`/`left` join factory for collection relations, i.e. `a.books.as(b)` joins with the
495
+ * relation's default kind (LEFT: a collection may be empty) and `.inner`/`.left` override. m2o/poly
496
+ * relations implement the same three methods directly on their column/impl classes, so `b.author`
497
+ * stays a plain expression that is also a join factory.
498
+ */
499
+ var AbstractCollectionAlias = class {
500
+ as(other) {
501
+ return this.joinEntry("left", requireAlias(other));
502
+ }
503
+ inner(other) {
504
+ return this.joinEntry("inner", requireAlias(other));
505
+ }
506
+ left(other) {
507
+ return this.joinEntry("left", requireAlias(other));
508
+ }
509
+ };
510
+ /** An o2m/lo2m/o2o relation: the ON is the other side's FK (an m2o or a poly component) back to our id. */
511
+ var OneToManyAliasImpl = class extends AbstractCollectionAlias {
512
+ proxy;
513
+ field;
514
+ constructor(proxy, field) {
515
+ super();
516
+ this.proxy = proxy;
517
+ this.field = field;
518
+ }
519
+ joinEntry(kind, other) {
520
+ const { field } = this;
521
+ const on = other[field.otherFieldName].eq(this.proxy.id);
522
+ const filtered = field.kind === "o2m" ? field.softDeletes !== "include" : field.kind === "lo2m";
523
+ return {
524
+ [kind]: other,
525
+ on,
526
+ [collectionJoin]: filtered
527
+ };
528
+ }
529
+ };
530
+ /**
531
+ * An m2m relation, i.e. `a.tags.as(t)`.
532
+ *
533
+ * The entry's own ON is `t.id = att.tag_id`, and the hidden `[m2mJoinTable]` half is
534
+ * `att.author_id = a.id`; only the target's ON references the join table, so reference pruning keeps
535
+ * or drops the pair together. Both are deferred conditions: the join table has no entity metadata, so
536
+ * its alias only exists once the parser registers it.
537
+ */
538
+ var ManyToManyAliasImpl = class extends AbstractCollectionAlias {
539
+ proxy;
540
+ field;
541
+ constructor(proxy, field) {
542
+ super();
543
+ this.proxy = proxy;
544
+ this.field = field;
545
+ }
546
+ joinEntry(kind, other) {
547
+ const { joinTableName, columnNames } = this.field;
548
+ const [ourColumn, otherColumn] = columnNames;
549
+ const jt = new JoinTableHandle(joinTableName);
550
+ const ourId = this.proxy.id;
551
+ const otherId = idColumnOf(other);
552
+ const jtOn = require_Expr.deferredCondition((ctx) => {
553
+ const jtAlias = ctx.aliasFor(jt);
554
+ const id = ourId.toSql(ctx);
555
+ return {
556
+ sql: `${require_keywords.kqDot(jtAlias, ourColumn)} = ${id.sql}`,
557
+ bindings: id.bindings,
558
+ refs: id.refs
559
+ };
560
+ });
561
+ const on = require_Expr.deferredCondition((ctx) => {
562
+ const jtAlias = ctx.aliasFor(jt);
563
+ const id = otherId.toSql(ctx);
564
+ return {
565
+ sql: `${id.sql} = ${require_keywords.kqDot(jtAlias, otherColumn)}`,
566
+ bindings: id.bindings,
567
+ refs: [...id.refs, jtAlias]
568
+ };
569
+ });
570
+ const filtered = this.field.softDeletes !== "include";
571
+ return {
572
+ [kind]: other,
573
+ on,
574
+ [collectionJoin]: filtered,
575
+ [m2mJoinTable]: {
576
+ handle: jt,
577
+ on: jtOn
578
+ }
579
+ };
580
+ }
581
+ };
582
+ /** Compares columns with per-parse aliases, i.e. `parent_author_id = <other id>` for `c.parent.eq(a.id)`. */
583
+ function newCrossColumnCondition(meta, field, mgmt, columnName, otherColumn, op) {
584
+ const cond = {
585
+ kind: "raw",
586
+ aliases: [],
587
+ condition: "unset",
588
+ pruneable: false,
589
+ bindings: []
590
+ };
591
+ return require_Expr.withDeferredAlias(cond, (resolve) => {
592
+ const r1 = resolve(mgmt);
593
+ const r2 = resolve(otherColumn.mgmt);
594
+ const a1 = getMaybeCtiAlias(meta, field, r1.meta, r1.alias);
595
+ const a2 = getMaybeCtiAlias(otherColumn.meta, otherColumn.field, r2.meta, r2.alias);
596
+ cond.aliases = [a1, a2];
597
+ cond.condition = `${a1}.${columnName} ${op} ${a2}.${otherColumn.column.columnName}`;
598
+ });
599
+ }
600
+ /** Fails fast when a join factory is passed something other than an alias. */
601
+ function requireAlias(other) {
602
+ if (!isAlias(other)) return require_utils.fail(`Expected an alias to join, got ${other}`);
603
+ return other;
604
+ }
605
+ /** The id column of a joined alias, for building sugar ON conditions. */
606
+ function idColumnOf(other) {
607
+ return other.id;
608
+ }
388
609
  //#endregion
610
+ exports.JoinTableHandle = JoinTableHandle;
389
611
  exports.alias = alias;
612
+ exports.aliasMgmt = aliasMgmt;
390
613
  exports.aliases = aliases;
614
+ exports.collectionJoin = collectionJoin;
391
615
  exports.getAliasMetadata = getAliasMetadata;
392
616
  exports.getAliasMgmt = getAliasMgmt;
393
617
  exports.getMaybeCtiAlias = getMaybeCtiAlias;
394
618
  exports.isAlias = isAlias;
619
+ exports.m2mJoinTable = m2mJoinTable;
395
620
  exports.newAliasProxy = newAliasProxy;
396
621
 
397
622
  //# sourceMappingURL=Aliases.cjs.map