joist-core 2.3.0-next.99 → 2.4.0

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 (131) hide show
  1. package/build/EntityManager.cjs +1 -1
  2. package/build/EntityManager.cjs.map +1 -1
  3. package/build/EntityManager.d.cts +9 -8
  4. package/build/EntityManager.d.cts.map +1 -1
  5. package/build/EntityManager.d.mts +9 -8
  6. package/build/EntityManager.d.mts.map +1 -1
  7. package/build/EntityManager.js +1 -1
  8. package/build/EntityManager.js.map +1 -1
  9. package/build/dataloaders/findCountDataLoader.cjs +2 -0
  10. package/build/dataloaders/findCountDataLoader.cjs.map +1 -1
  11. package/build/dataloaders/findCountDataLoader.d.cts.map +1 -1
  12. package/build/dataloaders/findCountDataLoader.d.mts.map +1 -1
  13. package/build/dataloaders/findCountDataLoader.js +2 -0
  14. package/build/dataloaders/findCountDataLoader.js.map +1 -1
  15. package/build/dataloaders/findDataLoader.cjs +1 -0
  16. package/build/dataloaders/findDataLoader.cjs.map +1 -1
  17. package/build/dataloaders/findDataLoader.d.cts.map +1 -1
  18. package/build/dataloaders/findDataLoader.d.mts.map +1 -1
  19. package/build/dataloaders/findDataLoader.js +2 -1
  20. package/build/dataloaders/findDataLoader.js.map +1 -1
  21. package/build/dataloaders/findIdsDataLoader.cjs +2 -0
  22. package/build/dataloaders/findIdsDataLoader.cjs.map +1 -1
  23. package/build/dataloaders/findIdsDataLoader.d.cts.map +1 -1
  24. package/build/dataloaders/findIdsDataLoader.d.mts.map +1 -1
  25. package/build/dataloaders/findIdsDataLoader.js +2 -0
  26. package/build/dataloaders/findIdsDataLoader.js.map +1 -1
  27. package/build/dataloaders/findPaginatedDataLoader.cjs +2 -0
  28. package/build/dataloaders/findPaginatedDataLoader.cjs.map +1 -1
  29. package/build/dataloaders/findPaginatedDataLoader.d.cts.map +1 -1
  30. package/build/dataloaders/findPaginatedDataLoader.d.mts.map +1 -1
  31. package/build/dataloaders/findPaginatedDataLoader.js +2 -0
  32. package/build/dataloaders/findPaginatedDataLoader.js.map +1 -1
  33. package/build/index.cjs +6 -3
  34. package/build/index.cjs.map +1 -1
  35. package/build/index.d.cts +6 -4
  36. package/build/index.d.cts.map +1 -1
  37. package/build/index.d.mts +6 -4
  38. package/build/index.d.mts.map +1 -1
  39. package/build/index.js +6 -5
  40. package/build/index.js.map +1 -1
  41. package/build/keys.cjs +6 -1
  42. package/build/keys.cjs.map +1 -1
  43. package/build/keys.d.cts +3 -1
  44. package/build/keys.d.cts.map +1 -1
  45. package/build/keys.d.mts +3 -1
  46. package/build/keys.d.mts.map +1 -1
  47. package/build/keys.js +6 -2
  48. package/build/keys.js.map +1 -1
  49. package/build/queries/find/QueryParser.cjs +1 -2
  50. package/build/queries/find/QueryParser.cjs.map +1 -1
  51. package/build/queries/find/QueryParser.d.cts.map +1 -1
  52. package/build/queries/find/QueryParser.d.mts.map +1 -1
  53. package/build/queries/find/QueryParser.js +2 -3
  54. package/build/queries/find/QueryParser.js.map +1 -1
  55. package/build/queries/find/QueryVisitor.cjs +17 -0
  56. package/build/queries/find/QueryVisitor.cjs.map +1 -1
  57. package/build/queries/find/QueryVisitor.d.cts +3 -1
  58. package/build/queries/find/QueryVisitor.d.cts.map +1 -1
  59. package/build/queries/find/QueryVisitor.d.mts +3 -1
  60. package/build/queries/find/QueryVisitor.d.mts.map +1 -1
  61. package/build/queries/find/QueryVisitor.js +17 -1
  62. package/build/queries/find/QueryVisitor.js.map +1 -1
  63. package/build/queries/sql/Expr.cjs +18 -1
  64. package/build/queries/sql/Expr.cjs.map +1 -1
  65. package/build/queries/sql/Expr.d.cts +13 -3
  66. package/build/queries/sql/Expr.d.cts.map +1 -1
  67. package/build/queries/sql/Expr.d.mts +13 -3
  68. package/build/queries/sql/Expr.d.mts.map +1 -1
  69. package/build/queries/sql/Expr.js +18 -2
  70. package/build/queries/sql/Expr.js.map +1 -1
  71. package/build/queries/sql/JoinTree.cjs +72 -0
  72. package/build/queries/sql/JoinTree.cjs.map +1 -0
  73. package/build/queries/sql/JoinTree.d.cts +107 -0
  74. package/build/queries/sql/JoinTree.d.cts.map +1 -0
  75. package/build/queries/sql/JoinTree.d.mts +107 -0
  76. package/build/queries/sql/JoinTree.d.mts.map +1 -0
  77. package/build/queries/sql/JoinTree.js +71 -0
  78. package/build/queries/sql/JoinTree.js.map +1 -0
  79. package/build/queries/sql/Tables.cjs +103 -6
  80. package/build/queries/sql/Tables.cjs.map +1 -1
  81. package/build/queries/sql/Tables.d.cts +72 -13
  82. package/build/queries/sql/Tables.d.cts.map +1 -1
  83. package/build/queries/sql/Tables.d.mts +72 -13
  84. package/build/queries/sql/Tables.d.mts.map +1 -1
  85. package/build/queries/sql/Tables.js +101 -10
  86. package/build/queries/sql/Tables.js.map +1 -1
  87. package/build/queries/sql/custom.cjs +52 -0
  88. package/build/queries/sql/custom.cjs.map +1 -0
  89. package/build/queries/sql/custom.d.cts +85 -0
  90. package/build/queries/sql/custom.d.cts.map +1 -0
  91. package/build/queries/sql/custom.d.mts +85 -0
  92. package/build/queries/sql/custom.d.mts.map +1 -0
  93. package/build/queries/sql/custom.js +49 -0
  94. package/build/queries/sql/custom.js.map +1 -0
  95. package/build/queries/sql/execute.cjs +68 -28
  96. package/build/queries/sql/execute.cjs.map +1 -1
  97. package/build/queries/sql/execute.d.cts +110 -11
  98. package/build/queries/sql/execute.d.cts.map +1 -1
  99. package/build/queries/sql/execute.d.mts +110 -11
  100. package/build/queries/sql/execute.d.mts.map +1 -1
  101. package/build/queries/sql/execute.js +69 -29
  102. package/build/queries/sql/execute.js.map +1 -1
  103. package/build/queries/sql/expressions/types.d.cts +5 -5
  104. package/build/queries/sql/expressions/types.d.cts.map +1 -1
  105. package/build/queries/sql/expressions/types.d.mts +5 -5
  106. package/build/queries/sql/expressions/types.d.mts.map +1 -1
  107. package/build/queries/sql/keywords.cjs +1 -1
  108. package/build/queries/sql/keywords.cjs.map +1 -1
  109. package/build/queries/sql/keywords.d.cts +1 -1
  110. package/build/queries/sql/keywords.d.mts +1 -1
  111. package/build/queries/sql/keywords.js +1 -1
  112. package/build/queries/sql/keywords.js.map +1 -1
  113. package/build/queries/sql/query.cjs +100 -38
  114. package/build/queries/sql/query.cjs.map +1 -1
  115. package/build/queries/sql/query.d.cts +96 -46
  116. package/build/queries/sql/query.d.cts.map +1 -1
  117. package/build/queries/sql/query.d.mts +96 -46
  118. package/build/queries/sql/query.d.mts.map +1 -1
  119. package/build/queries/sql/query.js +102 -40
  120. package/build/queries/sql/query.js.map +1 -1
  121. package/build/queries/valueFilters.cjs +28 -36
  122. package/build/queries/valueFilters.cjs.map +1 -1
  123. package/build/queries/valueFilters.d.cts +2 -4
  124. package/build/queries/valueFilters.d.cts.map +1 -1
  125. package/build/queries/valueFilters.d.mts +2 -4
  126. package/build/queries/valueFilters.d.mts.map +1 -1
  127. package/build/queries/valueFilters.js +29 -36
  128. package/build/queries/valueFilters.js.map +1 -1
  129. package/build/serde/serde.cjs.map +1 -1
  130. package/build/serde/serde.js.map +1 -1
  131. package/package.json +2 -2
@@ -13,6 +13,7 @@ const require_queries_renderConditions = require("../renderConditions.cjs");
13
13
  const require_queries_sql_AliasAssigner = require("./AliasAssigner.cjs");
14
14
  const require_queries_sql_Tables = require("./Tables.cjs");
15
15
  const require_queries_sql_expressions_expression = require("./expressions/expression.cjs");
16
+ const require_queries_sql_JoinTree = require("./JoinTree.cjs");
16
17
  //#region src/queries/sql/query.ts
17
18
  /**
18
19
  * `em.query`: SQL-shaped queries as plain object literals.
@@ -28,10 +29,10 @@ const require_queries_sql_expressions_expression = require("./expressions/expres
28
29
  * select: { name: a.first_name, n: bookStats.n },
29
30
  * orderBy: { n: "DESC" },
30
31
  * });
31
- * // rows: { name: string; n: number | null }[] (null because of the LEFT join)
32
+ * // rows: { name: string; n: number | undefined }[] (undefined because of the LEFT join)
32
33
  *
33
- * `em.query(pojo)` runs it. `select` decides the row type: a bare table returns entities, a
34
- * `{ key: expr }` object returns typed POJOs, a bare subquery returns that subquery's rows.
34
+ * `em.query(pojo)` runs it. `select` decides the row type: a bare table returns entities, an array of
35
+ * columns or a `{ key: expr }` object returns typed POJOs, and a bare subquery returns its rows.
35
36
  *
36
37
  * `query(pojo)` turns the *same* POJO into a value: a derived table with typed columns, a scalar
37
38
  * expression, or an entity list. It is the one non-POJO step, and the subquery analog of `table(Author)`:
@@ -51,24 +52,26 @@ const subqueryBrand = Symbol("joist.subquery");
51
52
  const entityQueryBrand = Symbol("joist.entityQuery");
52
53
  const scalarQueryBrand = Symbol("joist.scalarQuery");
53
54
  function query(q) {
54
- const handle = new SubqueryHandle(toQuery(q));
55
- const output = handle.output();
56
- if (output.kind === "entity") return { [entityQueryBrand]: handle };
57
- else if (output.kind === "scalar") return new SubqueryExpr(handle);
58
- else return newSubqueryProxy(handle);
55
+ return createQueryValue(q);
59
56
  }
60
57
  /**
61
- * Builds a query value only when its user-supplied `where` survives pruning.
58
+ * Builds a query value only when its user-supplied `where` or join-tree filters survive pruning.
62
59
  *
63
60
  * Missing or empty conditions prune the value, including nested groups and `pruneIfUndefined`.
64
- * Joins, HAVING, and implicit soft-delete/STI conditions do not keep the value alive.
61
+ * Alias-only joins, HAVING, and implicit soft-delete/STI conditions do not keep the value alive.
65
62
  *
66
63
  * I.e. `queryMaybe({ from: b, where: b.authorId.eq(enabled ? a.id : undefined), select: b.id })`
67
64
  * returns `undefined` when `enabled` is false, so an enclosing `notExists` also prunes.
68
65
  */
69
66
  function queryMaybe(q) {
70
- checkCondition(q.where);
71
- return isPrunedQueryCondition(q.where) ? void 0 : query(q);
67
+ return createMaybeQueryValue(q);
68
+ }
69
+ /** Applies queryMaybe's structural pruning before creating any kind of read-query value. */
70
+ function createMaybeQueryValue(q) {
71
+ const normalized = normalizeQueryJoins(q);
72
+ const where = normalizeWhere(normalized.where);
73
+ checkCondition(where);
74
+ return isPrunedQueryCondition(where) ? void 0 : createQueryValue(normalized);
72
75
  }
73
76
  /**
74
77
  * Declares a `WITH RECURSIVE` CTE from its two terms, and returns the CTE as a readable value.
@@ -123,9 +126,9 @@ function recursiveQuery(name, base, step, opts = {}) {
123
126
  * // Selecting this expression keeps the join to Book b.
124
127
  * sql.number`${b.order} * ${2}`
125
128
  *
126
- * // Reference an unmodeled column; it is untracked at the type level.
127
- * sql.ref<string>(a, "ts_search")
128
- * sql.condition`${sql.ref(a, "ts_search")} @@ plainto_tsquery(${term})`
129
+ * // Reference an unmodeled column; prefer the table's own `column`, which keeps its source key.
130
+ * a.column<string>("ts_search")
131
+ * sql.condition`${a.column("ts_search")} @@ plainto_tsquery(${term})`
129
132
  * ```
130
133
  */
131
134
  function sql(strings, ...values) {
@@ -159,10 +162,6 @@ sql.booleanArrayOrNull = typedSql;
159
162
  sql.condition = function condition(strings, ...values) {
160
163
  return require_queries_sql_Expr.deferredCondition((ctx) => new require_queries_sql_Expr.TemplateExpr(strings, values).toSql(ctx));
161
164
  };
162
- /** A column Joist does not model, on a source that is in the query. */
163
- sql.ref = function ref(source, column) {
164
- return new require_queries_sql_Expr.RefExpr(handleOf(source), column);
165
- };
166
165
  /**
167
166
  * Parses `arg` (a `Query` POJO or `query(...)` value) into a runnable `Plan`.
168
167
  *
@@ -189,8 +188,8 @@ function isReadQueryValue(arg) {
189
188
  * Compiles scalar or nonempty named expression projections, shared by SELECT and mutation RETURNING.
190
189
  * Source-shaped values need read hydration/source rules and are deliberately excluded here.
191
190
  */
192
- function projectionToSql(select, ctx) {
193
- const output = projectionOutput(select);
191
+ function projectionToSql(select, ctx, joins) {
192
+ const output = joinedOutput(projectionOutput(select), joins);
194
193
  if (output.kind === "scalar") {
195
194
  const expr = output.columns[0][1];
196
195
  const fragment = expr.toSql(ctx);
@@ -199,7 +198,7 @@ function projectionToSql(select, ctx) {
199
198
  ...fragment,
200
199
  sql: `${fragment.sql} AS value`
201
200
  }],
202
- decodeRows: (_, rows) => rows.map((row) => expr.decode(row.value)),
201
+ decodeRows: (_, rows) => rows.map((row) => decodeValue(expr, row.value)),
203
202
  output
204
203
  };
205
204
  }
@@ -272,6 +271,9 @@ var SubqueryColumnExpr = class extends require_queries_sql_Expr.BaseExpr {
272
271
  this.key = key;
273
272
  this.inner = inner;
274
273
  }
274
+ get [require_queries_sql_Expr.selectKeyBrand]() {
275
+ return this.key;
276
+ }
275
277
  toSql(ctx) {
276
278
  const alias = ctx.aliasFor(this.handle);
277
279
  return {
@@ -501,6 +503,7 @@ function queryOutput(q) {
501
503
  columns
502
504
  };
503
505
  }
506
+ q = normalizeQueryJoins(q);
504
507
  const { select } = q;
505
508
  if (require_queries_sql_Tables.isTable(select)) return {
506
509
  kind: "entity",
@@ -515,6 +518,20 @@ function projectionOutput(select) {
515
518
  kind: "scalar",
516
519
  columns: [["value", asExpr(select, "select")]]
517
520
  };
521
+ if (Array.isArray(select)) {
522
+ if (select.length === 0) require_utils.fail("A column array projection must not be empty");
523
+ const columns = select.map((value, index) => {
524
+ const expr = asExpr(value, `select[${index}]`);
525
+ const key = expr[require_queries_sql_Expr.selectKeyBrand];
526
+ if (typeof key !== "string") require_utils.fail(`select[${index}] must be a physical or subquery column`);
527
+ return [key, expr];
528
+ });
529
+ if (new Set(columns.map(([key]) => key)).size !== columns.length) require_utils.fail("A column array projection must not contain duplicate field names");
530
+ return {
531
+ kind: "pojo",
532
+ columns
533
+ };
534
+ }
518
535
  if (isPlainSelect(select)) {
519
536
  const columns = [];
520
537
  for (const key of Reflect.ownKeys(select)) {
@@ -530,7 +547,7 @@ function projectionOutput(select) {
530
547
  return require_utils.fail("Expected a scalar expression or a nonempty named expression projection");
531
548
  }
532
549
  /**
533
- * Adds physical NULL from unmatched LEFT joins without mutating shared expressions or their codecs.
550
+ * Adds SQL nullability from unmatched LEFT joins without mutating shared expressions or their codecs.
534
551
  * Only direct columns carry sqlSource; COUNT and COALESCE retain their own SQL nullability.
535
552
  * I.e. selecting Book.title from a LEFT-joined Book makes its output nullable, but COUNT(Book.id) stays NOT NULL.
536
553
  */
@@ -638,7 +655,7 @@ function parseSetQuery(q, parent, assigner, recursiveSelf) {
638
655
  bindings,
639
656
  outerRefs: [...new Set(outerRefs.filter((ref) => !cteAliases.has(ref)))],
640
657
  output,
641
- decodeRows: plans[0].decodeRows
658
+ decodeRows: (_, rows) => rows.map((row) => decodeRow(row, output.columns))
642
659
  };
643
660
  }
644
661
  /** Validate and quote only named set output sorts, including before a reusable value is parsed. */
@@ -777,6 +794,7 @@ function describeHandle(handle) {
777
794
  function parseQuery(q, parent, assigner, recursiveSelf) {
778
795
  validateReadQuery(q);
779
796
  if (isSetQuery(q)) return parseSetQuery(q, parent, assigner, recursiveSelf);
797
+ q = normalizeQueryJoins(q);
780
798
  const ctx = new Ctx(assigner, parent);
781
799
  if (recursiveSelf) ctx.setRecursiveSelf(recursiveSelf);
782
800
  const joinEntries = resolveReferenceJoins(q.join);
@@ -824,7 +842,8 @@ function parseQuery(q, parent, assigner, recursiveSelf) {
824
842
  const sti = stiEntityPlan(q, from);
825
843
  const { selects, decodeRows, output } = selectsToSql(q, ctx, from, cti.selects ?? sti.selects);
826
844
  const fromInjected = [...injectedConditions(from, softDeletes), ...sti.conditions];
827
- const where = conditionToSql(fromInjected.length > 0 ? { and: [q.where, ...fromInjected] } : q.where, ctx, true);
845
+ const predicate = normalizeWhere(q.where);
846
+ const where = conditionToSql(fromInjected.length > 0 ? { and: [predicate, ...fromInjected] } : predicate, ctx, true);
828
847
  const having = conditionToSql(q.having, ctx, true);
829
848
  const groupBys = (q.groupBy ?? []).map((g) => asExpr(g, "groupBy").toSql(ctx));
830
849
  const orderBys = orderBysToSql(q, ctx);
@@ -937,15 +956,17 @@ function registerSource(source, ctx, assigner) {
937
956
  };
938
957
  };
939
958
  } else {
940
- const meta = require_queries_sql_Tables.getTableMetadata(source);
941
- const alias = assigner.getAlias(meta.tableName);
959
+ const mgmt = require_queries_sql_Tables.getTableMgmt(source);
960
+ const { tableName } = mgmt;
961
+ const meta = "meta" in mgmt ? mgmt.meta : void 0;
962
+ const alias = assigner.getAlias(tableName);
942
963
  ctx.register(handle, alias);
943
964
  return () => {
944
- const entitySelects = meta.hasLazyColumns ? require_queries_entityQueryUtils.lazyExcludedSelects(meta, alias) : [require_queries_sql_keywords.kqStar(alias)];
965
+ const entitySelects = !meta ? [] : meta.hasLazyColumns ? require_queries_entityQueryUtils.lazyExcludedSelects(meta, alias) : [require_queries_sql_keywords.kqStar(alias)];
945
966
  return {
946
967
  handle,
947
968
  alias,
948
- sql: `${require_queries_sql_keywords.kq(meta.tableName)} AS ${require_queries_sql_keywords.kq(alias)}`,
969
+ sql: `${require_queries_sql_Tables.tableSqlName(mgmt)} AS ${require_queries_sql_keywords.kq(alias)}`,
949
970
  bindings: [],
950
971
  refs: [],
951
972
  entitySelects,
@@ -997,7 +1018,8 @@ function registerJoinTable(handle, ctx, assigner) {
997
1018
  /** Generates the `select` clause SQL and returns how to decode the resulting rows. */
998
1019
  function selectsToSql(q, ctx, from, entitySelects) {
999
1020
  const { select } = q;
1000
- if (require_queries_sql_Tables.isTable(select)) {
1021
+ if (require_queries_sql_Tables.isTable(select) && !require_queries_sql_Tables.isEntityTable(select)) require_utils.fail("A custom table cannot be selected as an entity; select its columns individually");
1022
+ if (require_queries_sql_Tables.isEntityTable(select)) {
1001
1023
  if (from.handle !== require_queries_sql_Tables.getTableMgmt(select)) require_utils.fail("Selecting a joined table is not supported yet; select the from table, or select its columns individually");
1002
1024
  const alias = ctx.aliasFor(require_queries_sql_Tables.getTableMgmt(select));
1003
1025
  const meta = require_queries_sql_Tables.getTableMetadata(select);
@@ -1028,9 +1050,7 @@ function selectsToSql(q, ctx, from, entitySelects) {
1028
1050
  output
1029
1051
  };
1030
1052
  }
1031
- const projection = projectionToSql(select, ctx);
1032
- projection.output = joinedOutput(projection.output, q.join);
1033
- return projection;
1053
+ return projectionToSql(select, ctx, q.join);
1034
1054
  }
1035
1055
  /**
1036
1056
  * Builds the mandatory CTI joins and flat row projection needed by entity hydration.
@@ -1041,7 +1061,7 @@ function selectsToSql(q, ctx, from, entitySelects) {
1041
1061
  * `_sN` aliases can be made unique before their projection fragments are built.
1042
1062
  */
1043
1063
  function ctiEntityPlan(q, from, assigner) {
1044
- if (!require_queries_sql_Tables.isTable(q.select) || from.handle !== require_queries_sql_Tables.getTableMgmt(q.select)) return {
1064
+ if (!require_queries_sql_Tables.isEntityTable(q.select) || from.handle !== require_queries_sql_Tables.getTableMgmt(q.select)) return {
1045
1065
  joins: [],
1046
1066
  selects: void 0
1047
1067
  };
@@ -1103,7 +1123,7 @@ function ctiEntityPlan(q, from, assigner) {
1103
1123
  }
1104
1124
  /** Adds the discriminator projection and subtype filter required by STI entity hydration. */
1105
1125
  function stiEntityPlan(q, from) {
1106
- if (!require_queries_sql_Tables.isTable(q.select) || from.handle !== require_queries_sql_Tables.getTableMgmt(q.select)) return {
1126
+ if (!require_queries_sql_Tables.isEntityTable(q.select) || from.handle !== require_queries_sql_Tables.getTableMgmt(q.select)) return {
1107
1127
  selects: void 0,
1108
1128
  conditions: []
1109
1129
  };
@@ -1169,7 +1189,7 @@ function decodeRow(row, decoders) {
1169
1189
  const result = {};
1170
1190
  for (const [key, expr] of decoders) {
1171
1191
  const value = row[key];
1172
- const decoded = value === null || value === void 0 ? null : expr.decode(value);
1192
+ const decoded = decodeValue(expr, value);
1173
1193
  if (key === "__proto__") Object.defineProperty(result, key, {
1174
1194
  value: decoded,
1175
1195
  enumerable: true,
@@ -1180,6 +1200,10 @@ function decodeRow(row, decoders) {
1180
1200
  }
1181
1201
  return result;
1182
1202
  }
1203
+ /** Decodes a top-level projected SQL NULL using Joist's undefined convention. */
1204
+ function decodeValue(expr, value) {
1205
+ return value === null ? void 0 : expr.decode(value);
1206
+ }
1183
1207
  const ORDER_BY_DIRECTIONS = [
1184
1208
  "ASC",
1185
1209
  "DESC",
@@ -1189,7 +1213,7 @@ const ORDER_BY_DIRECTIONS = [
1189
1213
  "DESC NULLS LAST"
1190
1214
  ];
1191
1215
  /**
1192
- * Generates ORDER BY SQL in entry order for keyed/expression arrays or a single keyed object.
1216
+ * Generates ORDER BY SQL in entry order for keyed/expression arrays or a single entry.
1193
1217
  *
1194
1218
  * Expression entries retain bindings and alias references for join pruning. Undefined entries and
1195
1219
  * directions are omitted before expression references are collected.
@@ -1218,7 +1242,7 @@ function orderBysToSql(q, ctx) {
1218
1242
  });
1219
1243
  } else {
1220
1244
  if (require_queries_sql_Expr.isExpr(select)) return require_utils.fail(`the keyed orderBy form needs a POJO or entity select`);
1221
- if (!(isSubqueryValue(select) ? select[subqueryBrand].columnKeys() : Object.keys(select)).includes(key)) return require_utils.fail(`orderBy key '${key}' is not a key of select`);
1245
+ if (!(isSubqueryValue(select) ? select[subqueryBrand].columnKeys() : projectionOutput(select).columns.map(([key]) => key)).includes(key)) return require_utils.fail(`orderBy key '${key}' is not a key of select`);
1222
1246
  result.push({
1223
1247
  sql: `${require_queries_sql_keywords.safeKq(key)} ${dir}`,
1224
1248
  bindings: [],
@@ -1261,6 +1285,10 @@ function conditionToSql(cond, ctx, topLevel) {
1261
1285
  function checkCondition(value) {
1262
1286
  if (require_queries_find_DeferredAlias.isDeferredAliasCondition(value) || value && typeof value === "object" && require_queries_conditions.predicateBrand in value && value[require_queries_conditions.predicateBrand] === "domain") require_utils.fail("Domain alias conditions are only supported by em.find; use table(...) predicates in SQL queries and mutations.");
1263
1287
  if (value === void 0 || value === require_queries_skipCondition.skipCondition) return;
1288
+ if (isInQueryCondition(value)) {
1289
+ checkCondition(normalizeWhere(normalizeQueryJoins(toQuery(value[require_queries_sql_Tables.inQueryCondition].query)).where));
1290
+ return;
1291
+ }
1264
1292
  if (!value || typeof value !== "object" || Array.isArray(value) || require_queries_sql_Expr.isExpr(value)) require_utils.fail("Query predicate must be a condition or an and/or group");
1265
1293
  const condition = value;
1266
1294
  if ("and" in condition || "or" in condition) {
@@ -1346,6 +1374,13 @@ function checkConditionKeys(value, allowed, description) {
1346
1374
  */
1347
1375
  function resolveQueryCondition(cond, ctx) {
1348
1376
  if (cond === void 0) return void 0;
1377
+ if (isInQueryCondition(cond)) {
1378
+ const { column, query } = cond[require_queries_sql_Tables.inQueryCondition];
1379
+ const value = createMaybeQueryValue(query);
1380
+ if (value === void 0) return void 0;
1381
+ if (!require_queries_sql_Expr.isExpr(value)) return require_utils.fail("An in query literal must select one expression");
1382
+ return column.in(value);
1383
+ }
1349
1384
  if ("and" in cond && cond.and) return {
1350
1385
  and: cond.and.map((child) => resolveQueryCondition(child, ctx)),
1351
1386
  pruneIfUndefined: cond.pruneIfUndefined
@@ -1380,6 +1415,7 @@ function isFilter(cond) {
1380
1415
  */
1381
1416
  function isPrunedQueryCondition(cond) {
1382
1417
  if (cond === void 0 || cond === require_queries_skipCondition.skipCondition) return true;
1418
+ if (isInQueryCondition(cond)) return isPrunedQueryCondition(normalizeWhere(normalizeQueryJoins(cond[require_queries_sql_Tables.inQueryCondition].query).where));
1383
1419
  if ("and" in cond || "or" in cond) {
1384
1420
  const children = (cond.and ?? cond.or ?? []).map(isPrunedQueryCondition);
1385
1421
  return children.every((pruned) => pruned) || "pruneIfUndefined" in cond && cond.pruneIfUndefined === "any" && children.some((pruned) => pruned);
@@ -1388,6 +1424,10 @@ function isPrunedQueryCondition(cond) {
1388
1424
  if ("notExists" in cond) return cond.notExists === void 0;
1389
1425
  return false;
1390
1426
  }
1427
+ /** Recognizes the direct query-literal condition created by EntityColumn.in. */
1428
+ function isInQueryCondition(value) {
1429
+ return typeof value === "object" && value !== null && require_queries_sql_Tables.inQueryCondition in value;
1430
+ }
1391
1431
  /** The physical source aliases a parsed condition tree references. */
1392
1432
  function refsOf(parsed) {
1393
1433
  return require_queries_parsedConditions.deepFindConditions(parsed, false).flatMap((c) => c.kind === "column" ? [c.alias] : c.kind === "raw" ? c.aliases : c.outerAliases);
@@ -1521,6 +1561,28 @@ function joinFragmentParts(parts, sep) {
1521
1561
  function isDefined(value) {
1522
1562
  return value !== void 0;
1523
1563
  }
1564
+ /** Lowers a relationship tree before SQL rendering or output nullability is calculated. */
1565
+ function normalizeQueryJoins(q) {
1566
+ if (q.join === void 0 || Array.isArray(q.join)) return q;
1567
+ const tree = require_queries_sql_JoinTree.compileJoinTree(q.from, q.join);
1568
+ return {
1569
+ ...q,
1570
+ join: tree.joins,
1571
+ where: { and: [tree.condition, normalizeWhere(q.where)] }
1572
+ };
1573
+ }
1574
+ /** Gives WHERE arrays the same pruning and validation as explicit AND groups. */
1575
+ function normalizeWhere(where) {
1576
+ return Array.isArray(where) ? { and: where } : where;
1577
+ }
1578
+ /** Creates the runtime query value after the public signature has checked its input. */
1579
+ function createQueryValue(q) {
1580
+ const handle = new SubqueryHandle(toQuery(q));
1581
+ const output = handle.output();
1582
+ if (output.kind === "entity") return { [entityQueryBrand]: handle };
1583
+ else if (output.kind === "scalar") return new SubqueryExpr(handle);
1584
+ else return newSubqueryProxy(handle);
1585
+ }
1524
1586
  /** Keeps shorthand declarations callable-only, without recursively copying sql's own shorthand properties. */
1525
1587
  function typedSql(strings, ...values) {
1526
1588
  return new require_queries_sql_Expr.TemplateExpr(strings, values);