@prisma/orm-family-sql 8.0.0-rc.1-dev.10 → 8.0.0-rc.1-dev.12

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 (100) hide show
  1. package/dist/{aggregate-descriptor-registry-DPV4z8MS.mjs → aggregate-descriptor-registry-DSap8YyW.mjs} +22 -7
  2. package/dist/aggregate-descriptor-registry-DSap8YyW.mjs.map +1 -0
  3. package/dist/{ast-DDAyIKdt.mjs → ast-B-eeN74F.mjs} +2 -2
  4. package/dist/ast-B-eeN74F.mjs.map +1 -0
  5. package/dist/{ast-DW-oFG2N.d.mts → ast-Du28qINj.d.mts} +3 -3
  6. package/dist/{ast-DW-oFG2N.d.mts.map → ast-Du28qINj.d.mts.map} +1 -1
  7. package/dist/builder.d.mts +2 -2
  8. package/dist/builder__runtime.d.mts +6 -6
  9. package/dist/builder__runtime.mjs +139 -77
  10. package/dist/builder__runtime.mjs.map +1 -1
  11. package/dist/builder__types.d.mts +2 -2
  12. package/dist/{control-CiDHc3iC.d.mts → control-CQGQNGSM.d.mts} +6 -6
  13. package/dist/{control-CiDHc3iC.d.mts.map → control-CQGQNGSM.d.mts.map} +1 -1
  14. package/dist/{control-adapter-C99J7aEF-DWH4HR8s.d.mts → control-adapter-C99J7aEF-DqhVuC1_.d.mts} +4 -4
  15. package/dist/{control-adapter-C99J7aEF-DWH4HR8s.d.mts.map → control-adapter-C99J7aEF-DqhVuC1_.d.mts.map} +1 -1
  16. package/dist/control-adapter-DiJGgwVx.d.mts +1 -0
  17. package/dist/{db-B5PMfDDd-DcJie6Go.d.mts → db-C5jmOFPM-C8E4QLgQ.d.mts} +40 -22
  18. package/dist/db-C5jmOFPM-C8E4QLgQ.d.mts.map +1 -0
  19. package/dist/{ddl-types-BMp2bWVS-D1waZJ4n.mjs → ddl-types-BGdnAbXo-DKPHPUGx.mjs} +3 -3
  20. package/dist/ddl-types-BGdnAbXo-DKPHPUGx.mjs.map +1 -0
  21. package/dist/{ddl-types-h_3_pXmq-1i7p99aC.d.mts → ddl-types-CnLSuwXa-BfAdBtW-.d.mts} +4 -4
  22. package/dist/{ddl-types-h_3_pXmq-1i7p99aC.d.mts.map → ddl-types-CnLSuwXa-BfAdBtW-.d.mts.map} +1 -1
  23. package/dist/{diff-CS3BDvZD.d.mts → diff-lZRkiTxp.d.mts} +3 -3
  24. package/dist/{diff-CS3BDvZD.d.mts.map → diff-lZRkiTxp.d.mts.map} +1 -1
  25. package/dist/{expression-Da4F0ehj.mjs → expression-CYVKobXp.mjs} +2 -2
  26. package/dist/{expression-Da4F0ehj.mjs.map → expression-CYVKobXp.mjs.map} +1 -1
  27. package/dist/{expression-BUmcEWHx.d.mts → expression-X88ppPyv.d.mts} +2 -2
  28. package/dist/{expression-BUmcEWHx.d.mts.map → expression-X88ppPyv.d.mts.map} +1 -1
  29. package/dist/family.d.mts +7 -7
  30. package/dist/family__control-adapter.d.mts +2 -2
  31. package/dist/family__control.d.mts +2 -2
  32. package/dist/family__diff.d.mts +2 -2
  33. package/dist/family__migration.d.mts +1 -1
  34. package/dist/family__runtime.d.mts +1 -1
  35. package/dist/{index-CYC083AG.d.mts → index-CYUp-sEz.d.mts} +13 -13
  36. package/dist/{index-CYC083AG.d.mts.map → index-CYUp-sEz.d.mts.map} +1 -1
  37. package/dist/lane-query-builder.d.mts +1 -1
  38. package/dist/{middleware-eb7YwdD4-DPIUcY9j.d.mts → middleware-Bp5oIYM0-B-m0jAm8.d.mts} +3 -3
  39. package/dist/{middleware-eb7YwdD4-DPIUcY9j.d.mts.map → middleware-Bp5oIYM0-B-m0jAm8.d.mts.map} +1 -1
  40. package/dist/middleware-g3HMYfJq.d.mts +1 -0
  41. package/dist/{migration-hktwXU_H.d.mts → migration-DoDR93C_.d.mts} +2 -2
  42. package/dist/{migration-hktwXU_H.d.mts.map → migration-DoDR93C_.d.mts.map} +1 -1
  43. package/dist/orm-client.d.mts +152 -99
  44. package/dist/orm-client.d.mts.map +1 -1
  45. package/dist/orm-client.mjs +244 -198
  46. package/dist/orm-client.mjs.map +1 -1
  47. package/dist/plan-D3FQ6FXy.d.mts +2 -0
  48. package/dist/{plan-Bmp5R9sq-BYkfwppR.d.mts → plan-DfLDIdep-qOGizVoO.d.mts} +3 -3
  49. package/dist/plan-DfLDIdep-qOGizVoO.d.mts.map +1 -0
  50. package/dist/query-lane-context-Dc9gwgtK.d.mts +1 -0
  51. package/dist/{query-lane-context-DjyAyYht-BWgx-nsP.d.mts → query-lane-context-DyDSQVS--UJOY8AwQ.d.mts} +9 -8
  52. package/dist/query-lane-context-DyDSQVS--UJOY8AwQ.d.mts.map +1 -0
  53. package/dist/relational-core.d.mts +12 -12
  54. package/dist/relational-core.mjs +5 -6
  55. package/dist/relational-core__aggregate-descriptor-registry.d.mts +2 -2
  56. package/dist/relational-core__aggregate-descriptor-registry.mjs +1 -1
  57. package/dist/relational-core__ast.d.mts +4 -4
  58. package/dist/relational-core__ast.mjs +4 -5
  59. package/dist/relational-core__codec-descriptor-registry.d.mts +2 -2
  60. package/dist/relational-core__contract-free.d.mts +2 -2
  61. package/dist/relational-core__contract-free.mjs +2 -2
  62. package/dist/relational-core__contract-free.mjs.map +1 -1
  63. package/dist/relational-core__expression.d.mts +1 -1
  64. package/dist/relational-core__expression.mjs +1 -1
  65. package/dist/relational-core__middleware.d.mts +2 -2
  66. package/dist/relational-core__plan.d.mts +3 -3
  67. package/dist/relational-core__query-lane-context.d.mts +2 -2
  68. package/dist/relational-core__types.d.mts +2 -2
  69. package/dist/{runtime-Cl4WEBC7.d.mts → runtime-CbcODreO.d.mts} +2 -2
  70. package/dist/{runtime-Cl4WEBC7.d.mts.map → runtime-CbcODreO.d.mts.map} +1 -1
  71. package/dist/runtime.d.mts +3 -3
  72. package/dist/runtime.mjs +2 -2
  73. package/dist/{sql-execution-plan-Da_d82pz-8mU4Fvvo.d.mts → sql-execution-plan-qxvHnFcA-eC6n5Ek9.d.mts} +3 -3
  74. package/dist/sql-execution-plan-qxvHnFcA-eC6n5Ek9.d.mts.map +1 -0
  75. package/dist/{types-dD8JJfeD-D8LDAPQW.d.mts → types-CLN9Y3BH-DiekC2oe.d.mts} +6 -6
  76. package/dist/{types-dD8JJfeD-D8LDAPQW.d.mts.map → types-CLN9Y3BH-DiekC2oe.d.mts.map} +1 -1
  77. package/dist/{types-D-_2ZCos-9q6j1gKx.mjs → types-CSP6WmWn-DVuxsZJW.mjs} +35 -4
  78. package/dist/types-CSP6WmWn-DVuxsZJW.mjs.map +1 -0
  79. package/dist/types-DrgaZ2lG.d.mts +1 -0
  80. package/dist/{types-IqZMLrFC-BXR6F1nU.d.mts → types-IqZMLrFC-B8FPWCcB.d.mts} +2 -2
  81. package/dist/{types-IqZMLrFC-BXR6F1nU.d.mts.map → types-IqZMLrFC-B8FPWCcB.d.mts.map} +1 -1
  82. package/dist/{types-BwePDa3m-CMy7CB9N.d.mts → types-ZzeQKmYD-PJlaIsww.d.mts} +9 -3
  83. package/dist/types-ZzeQKmYD-PJlaIsww.d.mts.map +1 -0
  84. package/package.json +18 -18
  85. package/dist/aggregate-descriptor-registry-DPV4z8MS.mjs.map +0 -1
  86. package/dist/ast-DDAyIKdt.mjs.map +0 -1
  87. package/dist/codec-types-B_xmJL2m-CC_Q-_u9.mjs +0 -24
  88. package/dist/codec-types-B_xmJL2m-CC_Q-_u9.mjs.map +0 -1
  89. package/dist/control-adapter-DpCa7zm_.d.mts +0 -1
  90. package/dist/db-B5PMfDDd-DcJie6Go.d.mts.map +0 -1
  91. package/dist/ddl-types-BMp2bWVS-D1waZJ4n.mjs.map +0 -1
  92. package/dist/middleware-BDVd5Hbn.d.mts +0 -1
  93. package/dist/plan-Ba8TRfem.d.mts +0 -2
  94. package/dist/plan-Bmp5R9sq-BYkfwppR.d.mts.map +0 -1
  95. package/dist/query-lane-context-CZEGLV1r.d.mts +0 -1
  96. package/dist/query-lane-context-DjyAyYht-BWgx-nsP.d.mts.map +0 -1
  97. package/dist/sql-execution-plan-Da_d82pz-8mU4Fvvo.d.mts.map +0 -1
  98. package/dist/types-BwePDa3m-CMy7CB9N.d.mts.map +0 -1
  99. package/dist/types-D-_2ZCos-9q6j1gKx.mjs.map +0 -1
  100. package/dist/types-KK31t3v0.d.mts +0 -1
@@ -1,8 +1,8 @@
1
1
  import { t as resolveStorageTable } from "./resolve-storage-table-DZsfnzeW.mjs";
2
2
  import { i as isUniqueConstraintViolation } from "./dist-x6jvtkKM.mjs";
3
- import { B as isWhereExpr, C as LiteralExpr, D as OrExpr, F as SubqueryExpr, I as TableSource, L as UpdateAst, O as OrderByItem, P as SelectAst, R as WindowFuncExpr, S as ListExpression, T as NullCheckExpr, _ as InsertAst, b as JsonArrayAggExpr, c as DeleteAst, f as EqColJoinOn, j as ProjectionItem, k as ParamRef, l as DerivedTableSource, n as AndExpr, o as ColumnRef, p as ExistsExpr, r as BinaryExpr, s as DefaultValueExpr, t as AggregateExpr, v as InsertOnConflict, w as NotExpr, x as JsonObjectExpr, y as JoinAst } from "./types-D-_2ZCos-9q6j1gKx.mjs";
3
+ import { C as LiteralExpr, D as OrExpr, F as SubqueryExpr, I as TableSource, L as UpdateAst, O as OrderByItem, P as SelectAst, R as WindowFuncExpr, S as ListExpression, T as NullCheckExpr, U as isWhereExpr, V as isAggregateFn, _ as InsertAst, b as JsonArrayAggExpr, c as DeleteAst, f as EqColJoinOn, j as ProjectionItem, k as ParamRef, l as DerivedTableSource, n as AndExpr, o as ColumnRef, p as ExistsExpr, r as BinaryExpr, s as DefaultValueExpr, t as AggregateExpr, v as InsertOnConflict, w as NotExpr, x as JsonObjectExpr, y as JoinAst } from "./types-CSP6WmWn-DVuxsZJW.mjs";
4
4
  import { t as collectOrderedParamRefs } from "./util-DQQgv2j1-D6INZaRG.mjs";
5
- import { n as JsonDocumentProjection, r as NativeJsonValueProjection, t as CodecJsonValueProjection } from "./ast-DDAyIKdt.mjs";
5
+ import { n as JsonDocumentProjection, r as NativeJsonValueProjection, t as CodecJsonValueProjection } from "./ast-B-eeN74F.mjs";
6
6
  import { n as codecRefForStorageColumn } from "./codec-descriptor-registry-BQPj9q8O.mjs";
7
7
  import { structuredError } from "@prisma/orm-framework/utils/structured-error";
8
8
  import { InternalError } from "@prisma/orm-framework/utils/internal-error";
@@ -12,6 +12,26 @@ import { blindCast, castAs } from "@prisma/orm-framework/utils/casts";
12
12
  import { assertDefined, invariant } from "@prisma/orm-framework/utils/assertions";
13
13
  import { AsyncIterableResult, createMetaBuilder, isRuntimeError, runtimeError } from "@prisma/orm-framework/components/runtime";
14
14
  //#region ../../../3-extensions/sql-orm-client/dist/index.mjs
15
+ const operationNamesByRegistry = /* @__PURE__ */ new WeakMap();
16
+ /**
17
+ * The operation vocabulary the composed registry contributes, in contribution
18
+ * order — the runtime mirror of the contract's emitted aggregate map, both
19
+ * settled from the same contributed descriptors. Cached per registry: the set
20
+ * is fixed at composition time, and every collection construction reads it.
21
+ */
22
+ function aggregateOperationNames(registry) {
23
+ const cached = operationNamesByRegistry.get(registry);
24
+ if (cached !== void 0) return cached;
25
+ const names = [];
26
+ const seen = /* @__PURE__ */ new Set();
27
+ for (const { operation } of registry.values()) {
28
+ if (seen.has(operation)) continue;
29
+ seen.add(operation);
30
+ names.push(operation);
31
+ }
32
+ operationNamesByRegistry.set(registry, names);
33
+ return names;
34
+ }
15
35
  function ormError(code, message, options) {
16
36
  return structuredError(code, message, options);
17
37
  }
@@ -343,70 +363,31 @@ function capabilityEnabled(value) {
343
363
  function isToOneCardinality(cardinality) {
344
364
  return cardinality === "1:1" || cardinality === "N:1";
345
365
  }
346
- function createAggregateBuilder(contract, namespaceId, modelName) {
366
+ /**
367
+ * The aggregate selector methods, one per operation the registry contributes —
368
+ * the runtime mirror of the contract's emitted aggregate map, which is what
369
+ * types the surface as {@link AggregateBuilder}. Each method builds a selector
370
+ * carrying the operation's name and, for a field-taking call, the field's
371
+ * storage column; what a selector resolves to is the plan compiler's question
372
+ * to the same registry.
373
+ */
374
+ function createAggregateBuilder(contract, aggregates, namespaceId, modelName) {
347
375
  const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
348
- return {
349
- count() {
350
- return {
351
- kind: "aggregate",
352
- fn: "count"
353
- };
354
- },
355
- sum(field) {
356
- return createFieldAggregateSelector(fieldToColumn, field, "sum");
357
- },
358
- avg(field) {
359
- return createFieldAggregateSelector(fieldToColumn, field, "avg");
360
- },
361
- min(field) {
362
- return createFieldAggregateSelector(fieldToColumn, field, "min");
363
- },
364
- max(field) {
365
- return createFieldAggregateSelector(fieldToColumn, field, "max");
366
- }
376
+ const builder = {};
377
+ for (const operation of aggregateOperationNames(aggregates)) builder[operation] = (field) => {
378
+ const column = field === void 0 ? void 0 : fieldToColumn[field] ?? field;
379
+ return {
380
+ kind: "aggregate",
381
+ fn: operation,
382
+ ...ifDefined("column", column)
383
+ };
367
384
  };
385
+ return blindCast(builder);
368
386
  }
369
387
  function isAggregateSelector(value) {
370
388
  if (!value || typeof value !== "object") return false;
371
389
  const candidate = value;
372
- if (candidate.kind !== "aggregate") return false;
373
- return candidate.fn === "count" || candidate.fn === "sum" || candidate.fn === "avg" || candidate.fn === "min" || candidate.fn === "max";
374
- }
375
- /**
376
- * The selector's result type is the contract's to state — it varies by target, operation, and the field's own codec — so the builder erases it here and each method's declared return type names it.
377
- */
378
- function createFieldAggregateSelector(fieldToColumn, field, fn) {
379
- return {
380
- kind: "aggregate",
381
- fn,
382
- column: fieldToColumn[field] ?? field
383
- };
384
- }
385
- /**
386
- * What an aggregate reads as when there is no row to read at all.
387
- *
388
- * SQL answers an empty input set itself — `count` collapses to zero, the rest
389
- * to null — so this covers only the degenerate case of a result set with no
390
- * row: an absent aggregate alias, or an include whose envelope never arrived.
391
- * Zero is a `bigint` because that is what both targets' `count` codecs decode
392
- * to; a count is a cardinality, and cardinalities are not capped at 2^53.
393
- */
394
- function emptyAggregateResult(fn) {
395
- return fn === "count" ? 0n : null;
396
- }
397
- function mapFieldsToColumns(contract, namespaceId, modelName, fieldNames) {
398
- const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
399
- return fieldNames.map((fieldName) => fieldToColumn[fieldName] ?? fieldName);
400
- }
401
- function mapCursorValuesToColumns(contract, namespaceId, modelName, cursorValues) {
402
- const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
403
- const mappedCursor = {};
404
- for (const [fieldName, value] of Object.entries(cursorValues)) {
405
- if (value === void 0) continue;
406
- const columnName = fieldToColumn[fieldName] ?? fieldName;
407
- mappedCursor[columnName] = value;
408
- }
409
- return mappedCursor;
390
+ return candidate.kind === "aggregate" && typeof candidate.fn === "string";
410
391
  }
411
392
  /**
412
393
  * What an aggregate resolves to: the codec its result carries, and the expression the target wants built for it.
@@ -419,15 +400,26 @@ function resolveAggregate(query) {
419
400
  const input = inputCodecRef(query);
420
401
  const resolved = query.aggregates.resolve(query.fn, input);
421
402
  if (resolved === void 0) throw unsupportedAggregate(query, input);
403
+ const nullability = resolved.nullable ? { nullable: true } : {
404
+ nullable: false,
405
+ emptyResultJson: resolved.emptyResultJson
406
+ };
422
407
  return {
423
408
  codec: resolved.output,
409
+ ...nullability,
424
410
  input,
425
411
  lower: resolved.lower
426
412
  };
427
413
  }
428
- /** The codec an aggregate's result carries. Rejects a pair the composed stack declares no overload for, exactly as planning does. */
429
- function resolveAggregateOutputCodec(query) {
430
- return resolveAggregate(query).codec;
414
+ /**
415
+ * The plain SQL form of an operation in the closed aggregate alphabet.
416
+ * Registry composition guarantees every operation outside the alphabet
417
+ * carries a lowering hook, so a resolution without one for any other name is
418
+ * a composition bug, not a user error.
419
+ */
420
+ function plainAggregateExpr(fn, expr) {
421
+ if (!isAggregateFn(fn)) throw new InternalError(`aggregate operation '${fn}' is outside the SQL aggregate alphabet and resolved without a lowering hook`);
422
+ return new AggregateExpr(fn, expr);
431
423
  }
432
424
  function unsupportedAggregate(query, input) {
433
425
  return ormError("ORM.AGGREGATE_UNSUPPORTED", input === void 0 ? `The composed target declares no '${query.fn}' aggregate for a call without an input.` : `The composed target declares no '${query.fn}' aggregate over codec '${input.codecId}' (column '${query.column}' of table '${query.tableName}').`, {
@@ -445,6 +437,36 @@ function unsupportedAggregate(query, input) {
445
437
  function inputCodecRef(query) {
446
438
  return query.column === void 0 ? void 0 : codecRefForStorageColumn(query.contract.storage, query.namespaceId, query.tableName, query.column);
447
439
  }
440
+ /**
441
+ * What an aggregate reads as when there is no row to read at all.
442
+ *
443
+ * SQL answers an empty input set itself — a nullable-declared aggregate
444
+ * collapses to NULL, a non-nullable one to a value SQL still produces, a
445
+ * count's zero cardinality — so this covers only the degenerate case of a
446
+ * result set with no row: an absent aggregate alias, or an include whose
447
+ * envelope never arrived. The answer reads off the operation's declared row:
448
+ * NULL where the row is nullable, else the value the row declares, decoded
449
+ * through the codec it declared beside it — so the application sees the same
450
+ * value shape a real row would produce, in whichever form that codec's
451
+ * canonical JSON takes.
452
+ */
453
+ function emptyAggregateResult(result, codec) {
454
+ return result.nullable ? null : codec.decodeJson(result.emptyResultJson);
455
+ }
456
+ function mapFieldsToColumns(contract, namespaceId, modelName, fieldNames) {
457
+ const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
458
+ return fieldNames.map((fieldName) => fieldToColumn[fieldName] ?? fieldName);
459
+ }
460
+ function mapCursorValuesToColumns(contract, namespaceId, modelName, cursorValues) {
461
+ const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
462
+ const mappedCursor = {};
463
+ for (const [fieldName, value] of Object.entries(cursorValues)) {
464
+ if (value === void 0) continue;
465
+ const columnName = fieldToColumn[fieldName] ?? fieldName;
466
+ mappedCursor[columnName] = value;
467
+ }
468
+ return mappedCursor;
469
+ }
448
470
  function stripHiddenMappedFields(contract, namespaceId, modelName, mapped, hiddenColumns) {
449
471
  if (hiddenColumns.length === 0) return;
450
472
  const columnToField = getColumnToFieldMap(contract, namespaceId, modelName);
@@ -611,7 +633,7 @@ function toAggregateProjection(contract, aggregates, namespaceId, tableName, sel
611
633
  expr: lower !== void 0 ? lower({
612
634
  expr: inputExpr,
613
635
  inputCodec
614
- }) : new AggregateExpr(selector.fn, inputExpr),
636
+ }) : plainAggregateExpr(selector.fn, inputExpr),
615
637
  codec
616
638
  };
617
639
  }
@@ -1579,7 +1601,7 @@ function buildIncludeAggregateExpr(scalar, childTableRef, lower, inputCodec) {
1579
1601
  expr,
1580
1602
  inputCodec
1581
1603
  });
1582
- return new AggregateExpr(scalar.fn, expr);
1604
+ return plainAggregateExpr(scalar.fn, expr);
1583
1605
  }
1584
1606
  /**
1585
1607
  * Build the inner SELECT for a `combine({ a, b, ... })` include.
@@ -2121,9 +2143,8 @@ function describeEnvelopeShape(value) {
2121
2143
  *
2122
2144
  * Contract: the envelope is always either
2123
2145
  * - a `{ value: <primitive> }` JSON object (the SQL path), or
2124
- * - `null` / `undefined` (the mutation read-back's
2125
- * `assignEmptyMutationIncludes` short-circuit before this decoder
2126
- * runs, for a parent absent from the read-back result).
2146
+ * - `null` / `undefined` (the mutation read-back's empty-include
2147
+ * short-circuit, for a parent absent from the read-back result).
2127
2148
  *
2128
2149
  * Any other shape — array, primitive, string that JSON-parses to
2129
2150
  * non-object — indicates a planner / decoder bug, so we throw
@@ -2132,21 +2153,16 @@ function describeEnvelopeShape(value) {
2132
2153
  *
2133
2154
  * The value passes through its own codec — the one the planner projected it
2134
2155
  * under — because it arrived inside a JSON document, where a count past 2^53
2135
- * would otherwise have been read as a rounded number. SQL semantics drive the
2136
- * empty-relation case: `COUNT(*)` over an empty input set is `0`;
2137
- * `SUM` / `AVG` / `MIN` / `MAX` over an empty input set return SQL
2138
- * `NULL`, which surfaces as `null` here. The outer `raw === null`
2139
- * fallback is defensive cover for an empty parent set; in single-query
2140
- * dispatch the correlated subquery always produces a row, so the inner
2141
- * envelope's `value` is always set by SQL.
2156
+ * would otherwise have been read as a rounded number. Resolution mirrors
2157
+ * planning the same registry, operation, and column so the empty-relation
2158
+ * answer derives from the operation's declared row: NULL where the row is
2159
+ * nullable, else the value that row declares. The outer `raw === null` fallback is
2160
+ * defensive cover for an empty parent set; in single-query dispatch the
2161
+ * correlated subquery always produces a row, so the inner envelope's `value`
2162
+ * is always set by SQL.
2142
2163
  */
2143
2164
  function decodeScalarIncludePayload(contract, context, include, scalar, raw) {
2144
- if (raw === null || raw === void 0) return emptyAggregateResult(scalar.fn);
2145
- const parsed = parseIncludePayload(raw);
2146
- if (!isPlainObjectEnvelope(parsed)) throw new InternalError(`scalar() envelope for include "${include.relationName}" has unexpected shape (expected object, got ${describeEnvelopeShape(parsed)}); this indicates a planner or decoder bug.`);
2147
- const value = parsed["value"];
2148
- if (value === null || value === void 0) return emptyAggregateResult(scalar.fn);
2149
- const codecRef = resolveAggregateOutputCodec({
2165
+ const resolved = resolveAggregate({
2150
2166
  aggregates: context.aggregateDescriptors,
2151
2167
  contract,
2152
2168
  namespaceId: include.relatedNamespaceId,
@@ -2154,10 +2170,16 @@ function decodeScalarIncludePayload(contract, context, include, scalar, raw) {
2154
2170
  fn: scalar.fn,
2155
2171
  column: scalar.column
2156
2172
  });
2173
+ const codec = context.contractCodecs.forCodecRef(resolved.codec);
2174
+ if (raw === null || raw === void 0) return emptyAggregateResult(resolved, codec);
2175
+ const parsed = parseIncludePayload(raw);
2176
+ if (!isPlainObjectEnvelope(parsed)) throw new InternalError(`scalar() envelope for include "${include.relationName}" has unexpected shape (expected object, got ${describeEnvelopeShape(parsed)}); this indicates a planner or decoder bug.`);
2177
+ const value = parsed["value"];
2178
+ if (value === null || value === void 0) return emptyAggregateResult(resolved, codec);
2157
2179
  return decodeIncludedJsonValue({
2158
2180
  table: include.relatedTableName,
2159
2181
  column: include.relationName
2160
- }, codecRef.codecId, context.contractCodecs.forCodecRef(codecRef), value);
2182
+ }, resolved.codec.codecId, codec, value);
2161
2183
  }
2162
2184
  function parseIncludedRows(include, value) {
2163
2185
  if (value === null || value === void 0) return [];
@@ -2328,7 +2350,7 @@ var GroupedCollection = class GroupedCollection {
2328
2350
  this.havingFilters = options.havingFilters;
2329
2351
  }
2330
2352
  having(predicate) {
2331
- const havingExpr = predicate(createHavingBuilder(this.contract, this.namespaceId, this.modelName, this.tableName));
2353
+ const havingExpr = predicate(createHavingBuilder(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.modelName, this.tableName));
2332
2354
  return new GroupedCollection(this.ctx, this.modelName, {
2333
2355
  tableName: this.tableName,
2334
2356
  namespaceId: this.namespaceId,
@@ -2346,7 +2368,7 @@ var GroupedCollection = class GroupedCollection {
2346
2368
  * Annotations are merged into the compiled plan's `meta.annotations`.
2347
2369
  */
2348
2370
  async aggregate(fn, configure) {
2349
- const aggregateSpec = fn(createAggregateBuilder(this.contract, this.namespaceId, this.modelName));
2371
+ const aggregateSpec = fn(createAggregateBuilder(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.modelName));
2350
2372
  const aggregateEntries = Object.entries(aggregateSpec);
2351
2373
  if (aggregateEntries.length === 0) throw ormError("ORM.AGGREGATE_SELECTOR_MISSING", "groupBy().aggregate() requires at least one aggregation selector", { meta: {
2352
2374
  method: "groupBy.aggregate",
@@ -2371,26 +2393,28 @@ var GroupedCollection = class GroupedCollection {
2371
2393
  });
2372
2394
  }
2373
2395
  };
2374
- function createHavingBuilder(contract, namespaceId, modelName, tableName) {
2396
+ /**
2397
+ * The having metric methods, one per operation the registry contributes —
2398
+ * the runtime mirror of the contract's emitted aggregate map, which is what
2399
+ * types the surface as {@link HavingBuilder}. HAVING compares the value
2400
+ * inside the database, so only an operation's plain `AggregateExpr` form is
2401
+ * sound here: an operation outside the SQL aggregate alphabet exists only in
2402
+ * its descriptor-lowered form — a rendering for the driver boundary — and is
2403
+ * refused. The typed surface already excludes it; the runtime refusal covers
2404
+ * dynamic invocation.
2405
+ */
2406
+ function createHavingBuilder(contract, aggregates, namespaceId, modelName, tableName) {
2375
2407
  const fieldToColumn = getFieldToColumnMap(contract, namespaceId, modelName);
2376
- const createMetricExpr = (fn, fieldName) => new AggregateExpr(fn, ColumnRef.of(tableName, fieldToColumn[fieldName] ?? fieldName));
2377
- return {
2378
- count() {
2379
- return createHavingComparisonMethods(AggregateExpr.count());
2380
- },
2381
- sum(field) {
2382
- return createHavingComparisonMethods(createMetricExpr("sum", field));
2383
- },
2384
- avg(field) {
2385
- return createHavingComparisonMethods(createMetricExpr("avg", field));
2386
- },
2387
- min(field) {
2388
- return createHavingComparisonMethods(createMetricExpr("min", field));
2389
- },
2390
- max(field) {
2391
- return createHavingComparisonMethods(createMetricExpr("max", field));
2392
- }
2408
+ const builder = {};
2409
+ for (const operation of aggregateOperationNames(aggregates)) builder[operation] = (field) => {
2410
+ if (!isAggregateFn(operation)) throw ormError("ORM.AGGREGATE_PROJECTION_ONLY", `Aggregate operation '${operation}' is projection-only: it has no plain SQL form for HAVING, ORDER BY, or comparison positions.`, {
2411
+ why: "An operation outside the SQL aggregate alphabet reaches SQL only through its descriptor's lowering hook — a rendering for the driver boundary. HAVING and ORDER BY compare the value inside the database, where that rendering would change SQL semantics.",
2412
+ fix: `Project '${operation}' in a select and filter or order on the projected value, or use an operation from the SQL aggregate alphabet.`,
2413
+ meta: { operation }
2414
+ });
2415
+ return createHavingComparisonMethods(new AggregateExpr(operation, field === void 0 ? void 0 : ColumnRef.of(tableName, fieldToColumn[field] ?? field)));
2393
2416
  };
2417
+ return blindCast(builder);
2394
2418
  }
2395
2419
  function createHavingComparisonMethods(metric) {
2396
2420
  const buildBinaryExpr = (op, value) => new BinaryExpr(op, metric, LiteralExpr.of(value));
@@ -2415,13 +2439,6 @@ function createHavingComparisonMethods(metric) {
2415
2439
  }
2416
2440
  };
2417
2441
  }
2418
- const aggregateFns = /* @__PURE__ */ new Set([
2419
- "count",
2420
- "sum",
2421
- "avg",
2422
- "min",
2423
- "max"
2424
- ]);
2425
2442
  function createIncludeScalar(fn, state, column) {
2426
2443
  return {
2427
2444
  kind: "includeScalar",
@@ -2439,7 +2456,7 @@ function createIncludeCombine(branches) {
2439
2456
  function isIncludeScalar(value) {
2440
2457
  if (typeof value !== "object" || value === null) return false;
2441
2458
  const candidate = value;
2442
- return candidate.kind === "includeScalar" && typeof candidate.fn === "string" && aggregateFns.has(candidate.fn) && isCollectionState(candidate.state);
2459
+ return candidate.kind === "includeScalar" && typeof candidate.fn === "string" && isCollectionState(candidate.state);
2443
2460
  }
2444
2461
  function isIncludeCombine(value) {
2445
2462
  if (typeof value !== "object" || value === null) return false;
@@ -3332,7 +3349,7 @@ function isWhereDirectInput(value) {
3332
3349
  function isMtiVariantInfo(variant) {
3333
3350
  return variant?.strategy === "mti";
3334
3351
  }
3335
- var Collection = class Collection {
3352
+ var CollectionImpl = class CollectionImpl {
3336
3353
  /** @internal */
3337
3354
  ctx;
3338
3355
  /** @internal */
@@ -3358,6 +3375,49 @@ var Collection = class Collection {
3358
3375
  this.state = options.state ?? emptyState();
3359
3376
  this.registry = options.registry ?? /* @__PURE__ */ new Map();
3360
3377
  this.includeRefinementMode = options.includeRefinementMode ?? false;
3378
+ this.#installAggregateReducers();
3379
+ }
3380
+ /**
3381
+ * Install one include-scalar reducer per operation the composed registry
3382
+ * contributes — the runtime mirror of the contract's emitted aggregate map,
3383
+ * which is what types the reducers as {@link AggregateIncludeReducers} on
3384
+ * the public {@link Collection} surface. The reducers live on the instance
3385
+ * because their names are the registry's, not the class declaration's.
3386
+ *
3387
+ * A name the collection already carries is skipped, and which member holds
3388
+ * it decides what the skip means. A `CollectionImpl` member is rejected at
3389
+ * ORM composition with `ORM.AGGREGATE_OPERATION_RESERVED`, since
3390
+ * {@link reservedCollectionMemberNames} scans this class. A member declared
3391
+ * by a custom collection class registered through `orm({ collections })`
3392
+ * falls outside that set, so it keeps the name and the operation gets no
3393
+ * reducer. The type level is what guards that case: {@link Collection}
3394
+ * intersects the class with {@link AggregateIncludeReducers}, so for any
3395
+ * contract whose emitted map carries the operation, a subclass member that
3396
+ * does not match the reducer's signature is a type error.
3397
+ */
3398
+ #installAggregateReducers() {
3399
+ for (const operation of aggregateOperationNames(this.ctx.context.aggregateDescriptors)) {
3400
+ if (operation in this) continue;
3401
+ Object.defineProperty(this, operation, {
3402
+ value: (field) => this.#includeScalarReducer(operation, field),
3403
+ writable: true,
3404
+ enumerable: false,
3405
+ configurable: true
3406
+ });
3407
+ }
3408
+ }
3409
+ /**
3410
+ * Scalar reducer — reduces a to-many relation to the operation's value over
3411
+ * the related rows. Use inside an `include(...)` refinement callback as
3412
+ * `include(..., (rel) => rel.count())`; throws if called elsewhere. The
3413
+ * parent row's relation field becomes that value instead of an array. A
3414
+ * call without a field aggregates over rows; a call with one aggregates the
3415
+ * field's storage column.
3416
+ */
3417
+ #includeScalarReducer(operation, field) {
3418
+ this.#assertIncludeRefinementMode(`${operation}()`);
3419
+ const column = field === void 0 ? void 0 : resolveFieldToColumn(this.contract, this.namespaceId, this.modelName, field);
3420
+ return createIncludeScalar(operation, this.state, column);
3361
3421
  }
3362
3422
  where(input) {
3363
3423
  const filter = normalizeWhereArg(typeof input === "function" ? input(createModelAccessor(this.ctx.context, this.namespaceId, this.modelName, this.state.variantName)) : isWhereDirectInput(input) ? input : shorthandToWhereExpr(this.ctx.context, this.namespaceId, this.modelName, input), {
@@ -3524,7 +3584,7 @@ var Collection = class Collection {
3524
3584
  * .where({ published: true })
3525
3585
  * .groupBy('userId')
3526
3586
  * .aggregate((agg) => ({ count: agg.count(), totalViews: agg.sum('views') }));
3527
- * // [{ userId: 1, count: 3n, totalViews: 120n }, ...]
3587
+ * // [{ userId: 1, count: 3, totalViews: 120 }, ...]
3528
3588
  * ```
3529
3589
  */
3530
3590
  groupBy(...fields) {
@@ -3539,84 +3599,6 @@ var Collection = class Collection {
3539
3599
  });
3540
3600
  }
3541
3601
  /**
3542
- * Scalar reducer — reduces a to-many relation to the number of
3543
- * related rows. Use inside an `include(...)` refinement callback as
3544
- * `include(..., (rel) => rel.count())`; throws if called elsewhere.
3545
- * The parent row's relation field becomes that count instead of an
3546
- * array.
3547
- *
3548
- * ```typescript
3549
- * const users = await db.orm.User.include('posts', (posts) => posts.count()).all();
3550
- * // each user row: { ...user, posts: bigint }
3551
- * ```
3552
- */
3553
- count() {
3554
- this.#assertIncludeRefinementMode("count()");
3555
- return createIncludeScalar("count", this.state);
3556
- }
3557
- /**
3558
- * Scalar reducer — reduces a to-many relation to the sum of `field`
3559
- * across related rows. Returns `null` when there are no related
3560
- * rows. Use inside an `include(...)` refinement callback; throws if
3561
- * called elsewhere.
3562
- *
3563
- * ```typescript
3564
- * const users = await db.orm.User.include('posts', (posts) => posts.sum('views')).all();
3565
- * // each user row: { ...user, posts: bigint | null } — an int4 column's sum widens to int8
3566
- * ```
3567
- */
3568
- sum(field) {
3569
- this.#assertIncludeRefinementMode("sum()");
3570
- const columnName = resolveFieldToColumn(this.contract, this.namespaceId, this.modelName, field);
3571
- return createIncludeScalar("sum", this.state, columnName);
3572
- }
3573
- /**
3574
- * Scalar reducer — reduces a to-many relation to the average of
3575
- * `field` across related rows. Returns `null` when there are no
3576
- * related rows. Use inside an `include(...)` refinement callback;
3577
- * throws if called elsewhere.
3578
- *
3579
- * ```typescript
3580
- * const users = await db.orm.User.include('posts', (posts) => posts.avg('views')).all();
3581
- * // each user row: { ...user, posts: string | null } — PostgreSQL averages integers as numeric
3582
- * ```
3583
- */
3584
- avg(field) {
3585
- this.#assertIncludeRefinementMode("avg()");
3586
- const columnName = resolveFieldToColumn(this.contract, this.namespaceId, this.modelName, field);
3587
- return createIncludeScalar("avg", this.state, columnName);
3588
- }
3589
- /**
3590
- * Scalar reducer — reduces a to-many relation to the minimum value
3591
- * of `field` across related rows. Returns `null` when there are no
3592
- * related rows. Use inside an `include(...)` refinement callback;
3593
- * throws if called elsewhere.
3594
- *
3595
- * ```typescript
3596
- * const users = await db.orm.User.include('posts', (posts) => posts.min('views')).all();
3597
- * ```
3598
- */
3599
- min(field) {
3600
- this.#assertIncludeRefinementMode("min()");
3601
- const columnName = resolveFieldToColumn(this.contract, this.namespaceId, this.modelName, field);
3602
- return createIncludeScalar("min", this.state, columnName);
3603
- }
3604
- /**
3605
- * Scalar reducer — reduces a to-many relation to the maximum value
3606
- * of `field` across related rows. Returns `null` when there are no
3607
- * related rows. Use inside an `include(...)` refinement callback;
3608
- * throws if called elsewhere.
3609
- *
3610
- * ```typescript
3611
- * const users = await db.orm.User.include('posts', (posts) => posts.max('views')).all();
3612
- * ```
3613
- */
3614
- max(field) {
3615
- this.#assertIncludeRefinementMode("max()");
3616
- const columnName = resolveFieldToColumn(this.contract, this.namespaceId, this.modelName, field);
3617
- return createIncludeScalar("max", this.state, columnName);
3618
- }
3619
- /**
3620
3602
  * Produce multiple named sub-views of a to-many relation in a
3621
3603
  * single `include(...)`. Each branch is either another refined
3622
3604
  * collection (mapped to a row array on the parent) or a scalar
@@ -3633,7 +3615,7 @@ var Collection = class Collection {
3633
3615
  * ).all();
3634
3616
  * // each user row: {
3635
3617
  * // ...user,
3636
- * // posts: { recent: Post[]; total: bigint; averageViews: string | null };
3618
+ * // posts: { recent: Post[]; total: number; averageViews: number | null };
3637
3619
  * // }
3638
3620
  * ```
3639
3621
  */
@@ -3681,7 +3663,7 @@ var Collection = class Collection {
3681
3663
  */
3682
3664
  cursor(cursorValues) {
3683
3665
  const mappedCursor = mapCursorValuesToColumns(this.contract, this.namespaceId, this.modelName, cursorValues);
3684
- if (Object.keys(mappedCursor).length === 0) return this;
3666
+ if (Object.keys(mappedCursor).length === 0) return blindCast(this);
3685
3667
  return this.#clone({ cursor: mappedCursor });
3686
3668
  }
3687
3669
  /**
@@ -3805,7 +3787,7 @@ var Collection = class Collection {
3805
3787
  * averageViews: agg.avg('views'),
3806
3788
  * maxViews: agg.max('views'),
3807
3789
  * }));
3808
- * // { total: 42n, averageViews: '17.3000000000000000', maxViews: 9001 }
3790
+ * // { total: 42, averageViews: 17.3, maxViews: 9001 }
3809
3791
  * ```
3810
3792
  *
3811
3793
  * Accepts an optional `configure` callback that receives a
@@ -3813,7 +3795,7 @@ var Collection = class Collection {
3813
3795
  * Annotations are merged into the compiled plan's `meta.annotations`.
3814
3796
  */
3815
3797
  async aggregate(fn, configure) {
3816
- const aggregateSpec = fn(createAggregateBuilder(this.contract, this.namespaceId, this.modelName));
3798
+ const aggregateSpec = fn(createAggregateBuilder(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.modelName));
3817
3799
  const entries = Object.entries(aggregateSpec);
3818
3800
  if (entries.length === 0) throw ormError("ORM.AGGREGATE_SELECTOR_MISSING", "aggregate() requires at least one aggregation selector", { meta: {
3819
3801
  method: "aggregate",
@@ -3828,7 +3810,7 @@ var Collection = class Collection {
3828
3810
  const compiled = mergeAnnotations(compileAggregate(this.contract, this.ctx.context.aggregateDescriptors, this.namespaceId, this.tableName, this.state.filters, aggregateSpec), annotationsMap);
3829
3811
  const row = (await executeQueryPlan(this.ctx.runtime, compiled).toArray())[0] ?? {};
3830
3812
  const result = {};
3831
- for (const [alias, selector] of entries) result[alias] = row[alias] ?? emptyAggregateResult(selector.fn);
3813
+ for (const [alias, selector] of entries) result[alias] = row[alias] ?? this.#emptyAggregateValue(selector);
3832
3814
  return blindCast(result);
3833
3815
  }
3834
3816
  async create(data, configure) {
@@ -4506,6 +4488,23 @@ var Collection = class Collection {
4506
4488
  namespaceId: this.namespaceId
4507
4489
  }))[0] ?? null;
4508
4490
  }
4491
+ /**
4492
+ * The value an aggregate alias reads as when the result set has no row to
4493
+ * read at all. Resolution mirrors planning — the same registry, operation,
4494
+ * and column — so the answer derives from the operation's declared row
4495
+ * rather than its name.
4496
+ */
4497
+ #emptyAggregateValue(selector) {
4498
+ const resolved = resolveAggregate({
4499
+ aggregates: this.ctx.context.aggregateDescriptors,
4500
+ contract: this.contract,
4501
+ namespaceId: this.namespaceId,
4502
+ tableName: this.tableName,
4503
+ fn: selector.fn,
4504
+ column: selector.column
4505
+ });
4506
+ return emptyAggregateResult(resolved, this.ctx.context.contractCodecs.forCodecRef(resolved.codec));
4507
+ }
4509
4508
  #assertIncludeRefinementMode(action) {
4510
4509
  if (this.includeRefinementMode) return;
4511
4510
  throw ormError("ORM.INCLUDE_INVALID", `${action} is only available inside include() refinement callbacks`, { meta: { action } });
@@ -4544,7 +4543,7 @@ var Collection = class Collection {
4544
4543
  }));
4545
4544
  }
4546
4545
  #createCollection(modelName, options) {
4547
- return blindCast(new ((this.registry.get(modelName)) ?? (blindCast(Collection)))(this.ctx, modelName, {
4546
+ return blindCast(new ((this.registry.get(modelName)) ?? (blindCast(CollectionImpl)))(this.ctx, modelName, {
4548
4547
  tableName: options.tableName,
4549
4548
  namespaceId: options.namespaceId,
4550
4549
  state: options.state,
@@ -4602,8 +4601,55 @@ var Collection = class Collection {
4602
4601
  return meta.annotations.size === 0 ? void 0 : meta.annotations;
4603
4602
  }
4604
4603
  };
4604
+ const collectionInstanceMemberNames = [
4605
+ "ctx",
4606
+ "contract",
4607
+ "modelName",
4608
+ "tableName",
4609
+ "namespaceId",
4610
+ "state",
4611
+ "registry",
4612
+ "includeRefinementMode"
4613
+ ];
4614
+ /**
4615
+ * Every member name the collection surface owns: the prototype's methods plus
4616
+ * the declared instance fields. A contributed aggregate operation may not
4617
+ * take one of these names — reducers install into the same flat namespace —
4618
+ * so ORM composition rejects any operation this set contains.
4619
+ */
4620
+ function reservedCollectionMemberNames() {
4621
+ return /* @__PURE__ */ new Set([...Object.getOwnPropertyNames(CollectionImpl.prototype), ...collectionInstanceMemberNames]);
4622
+ }
4623
+ const Collection = blindCast(CollectionImpl);
4624
+ /**
4625
+ * The class behind {@link Collection}, for package-internal prototype-chain
4626
+ * checks (`instanceof`) and default construction. The public constructor
4627
+ * surface carries a single construct signature returning the intersection,
4628
+ * which heritage clauses require; the raw class keeps the `Function` shape
4629
+ * those checks need.
4630
+ */
4631
+ const CollectionBase = CollectionImpl;
4632
+ /**
4633
+ * Reject a contributed aggregate operation whose name a collection member
4634
+ * already owns. Aggregate operations surface as reducer methods on the
4635
+ * collection, in one flat namespace with the builder members, so a same-named
4636
+ * operation would shadow the member it collides with — a client-surface
4637
+ * concern, enforced here where that surface is assembled.
4638
+ */
4639
+ function assertAggregateOperationsNotReserved(registry) {
4640
+ const reserved = reservedCollectionMemberNames();
4641
+ for (const operation of aggregateOperationNames(registry)) {
4642
+ if (!reserved.has(operation)) continue;
4643
+ throw ormError("ORM.AGGREGATE_OPERATION_RESERVED", `Aggregate operation '${operation}' is reserved: the name is a collection builder member.`, {
4644
+ why: "Aggregate operations surface as reducer methods on the collection, in one namespace with the query-builder members; a same-named operation would shadow the member it collides with.",
4645
+ fix: "Rename the contributed aggregate operation.",
4646
+ meta: { operation }
4647
+ });
4648
+ }
4649
+ }
4605
4650
  function orm(options) {
4606
4651
  const { runtime, collections, context } = options;
4652
+ assertAggregateOperationsNotReserved(context.aggregateDescriptors);
4607
4653
  const contract = context.contract;
4608
4654
  const ctx = {
4609
4655
  runtime,
@@ -4611,7 +4657,7 @@ function orm(options) {
4611
4657
  };
4612
4658
  const collectionRegistry = createCollectionRegistry(contract, collections);
4613
4659
  function buildCollection(namespaceId, modelName, tableName) {
4614
- return new (blindCast(collectionRegistry.get(modelName) ?? Collection))(ctx, modelName, {
4660
+ return new (blindCast(collectionRegistry.get(modelName) ?? CollectionBase))(ctx, modelName, {
4615
4661
  registry: collectionRegistry,
4616
4662
  namespaceId,
4617
4663
  ...tableName !== void 0 ? { tableName } : {}
@@ -4665,7 +4711,7 @@ function isCollectionClass(value) {
4665
4711
  if (typeof value !== "function") return false;
4666
4712
  const candidate = value;
4667
4713
  if (!candidate.prototype || typeof candidate.prototype !== "object") return false;
4668
- return candidate.prototype instanceof Collection;
4714
+ return candidate.prototype instanceof CollectionBase;
4669
4715
  }
4670
4716
  //#endregion
4671
4717
  export { Collection, GroupedCollection, all, and, createModelAccessor, emptyState, not, or, orm };