@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.
- package/dist/{aggregate-descriptor-registry-DPV4z8MS.mjs → aggregate-descriptor-registry-DSap8YyW.mjs} +22 -7
- package/dist/aggregate-descriptor-registry-DSap8YyW.mjs.map +1 -0
- package/dist/{ast-DDAyIKdt.mjs → ast-B-eeN74F.mjs} +2 -2
- package/dist/ast-B-eeN74F.mjs.map +1 -0
- package/dist/{ast-DW-oFG2N.d.mts → ast-Du28qINj.d.mts} +3 -3
- package/dist/{ast-DW-oFG2N.d.mts.map → ast-Du28qINj.d.mts.map} +1 -1
- package/dist/builder.d.mts +2 -2
- package/dist/builder__runtime.d.mts +6 -6
- package/dist/builder__runtime.mjs +139 -77
- package/dist/builder__runtime.mjs.map +1 -1
- package/dist/builder__types.d.mts +2 -2
- package/dist/{control-CiDHc3iC.d.mts → control-CQGQNGSM.d.mts} +6 -6
- package/dist/{control-CiDHc3iC.d.mts.map → control-CQGQNGSM.d.mts.map} +1 -1
- package/dist/{control-adapter-C99J7aEF-DWH4HR8s.d.mts → control-adapter-C99J7aEF-DqhVuC1_.d.mts} +4 -4
- package/dist/{control-adapter-C99J7aEF-DWH4HR8s.d.mts.map → control-adapter-C99J7aEF-DqhVuC1_.d.mts.map} +1 -1
- package/dist/control-adapter-DiJGgwVx.d.mts +1 -0
- package/dist/{db-B5PMfDDd-DcJie6Go.d.mts → db-C5jmOFPM-C8E4QLgQ.d.mts} +40 -22
- package/dist/db-C5jmOFPM-C8E4QLgQ.d.mts.map +1 -0
- package/dist/{ddl-types-BMp2bWVS-D1waZJ4n.mjs → ddl-types-BGdnAbXo-DKPHPUGx.mjs} +3 -3
- package/dist/ddl-types-BGdnAbXo-DKPHPUGx.mjs.map +1 -0
- package/dist/{ddl-types-h_3_pXmq-1i7p99aC.d.mts → ddl-types-CnLSuwXa-BfAdBtW-.d.mts} +4 -4
- package/dist/{ddl-types-h_3_pXmq-1i7p99aC.d.mts.map → ddl-types-CnLSuwXa-BfAdBtW-.d.mts.map} +1 -1
- package/dist/{diff-CS3BDvZD.d.mts → diff-lZRkiTxp.d.mts} +3 -3
- package/dist/{diff-CS3BDvZD.d.mts.map → diff-lZRkiTxp.d.mts.map} +1 -1
- package/dist/{expression-Da4F0ehj.mjs → expression-CYVKobXp.mjs} +2 -2
- package/dist/{expression-Da4F0ehj.mjs.map → expression-CYVKobXp.mjs.map} +1 -1
- package/dist/{expression-BUmcEWHx.d.mts → expression-X88ppPyv.d.mts} +2 -2
- package/dist/{expression-BUmcEWHx.d.mts.map → expression-X88ppPyv.d.mts.map} +1 -1
- package/dist/family.d.mts +7 -7
- package/dist/family__control-adapter.d.mts +2 -2
- package/dist/family__control.d.mts +2 -2
- package/dist/family__diff.d.mts +2 -2
- package/dist/family__migration.d.mts +1 -1
- package/dist/family__runtime.d.mts +1 -1
- package/dist/{index-CYC083AG.d.mts → index-CYUp-sEz.d.mts} +13 -13
- package/dist/{index-CYC083AG.d.mts.map → index-CYUp-sEz.d.mts.map} +1 -1
- package/dist/lane-query-builder.d.mts +1 -1
- package/dist/{middleware-eb7YwdD4-DPIUcY9j.d.mts → middleware-Bp5oIYM0-B-m0jAm8.d.mts} +3 -3
- package/dist/{middleware-eb7YwdD4-DPIUcY9j.d.mts.map → middleware-Bp5oIYM0-B-m0jAm8.d.mts.map} +1 -1
- package/dist/middleware-g3HMYfJq.d.mts +1 -0
- package/dist/{migration-hktwXU_H.d.mts → migration-DoDR93C_.d.mts} +2 -2
- package/dist/{migration-hktwXU_H.d.mts.map → migration-DoDR93C_.d.mts.map} +1 -1
- package/dist/orm-client.d.mts +152 -99
- package/dist/orm-client.d.mts.map +1 -1
- package/dist/orm-client.mjs +244 -198
- package/dist/orm-client.mjs.map +1 -1
- package/dist/plan-D3FQ6FXy.d.mts +2 -0
- package/dist/{plan-Bmp5R9sq-BYkfwppR.d.mts → plan-DfLDIdep-qOGizVoO.d.mts} +3 -3
- package/dist/plan-DfLDIdep-qOGizVoO.d.mts.map +1 -0
- package/dist/query-lane-context-Dc9gwgtK.d.mts +1 -0
- package/dist/{query-lane-context-DjyAyYht-BWgx-nsP.d.mts → query-lane-context-DyDSQVS--UJOY8AwQ.d.mts} +9 -8
- package/dist/query-lane-context-DyDSQVS--UJOY8AwQ.d.mts.map +1 -0
- package/dist/relational-core.d.mts +12 -12
- package/dist/relational-core.mjs +5 -6
- package/dist/relational-core__aggregate-descriptor-registry.d.mts +2 -2
- package/dist/relational-core__aggregate-descriptor-registry.mjs +1 -1
- package/dist/relational-core__ast.d.mts +4 -4
- package/dist/relational-core__ast.mjs +4 -5
- package/dist/relational-core__codec-descriptor-registry.d.mts +2 -2
- package/dist/relational-core__contract-free.d.mts +2 -2
- package/dist/relational-core__contract-free.mjs +2 -2
- package/dist/relational-core__contract-free.mjs.map +1 -1
- package/dist/relational-core__expression.d.mts +1 -1
- package/dist/relational-core__expression.mjs +1 -1
- package/dist/relational-core__middleware.d.mts +2 -2
- package/dist/relational-core__plan.d.mts +3 -3
- package/dist/relational-core__query-lane-context.d.mts +2 -2
- package/dist/relational-core__types.d.mts +2 -2
- package/dist/{runtime-Cl4WEBC7.d.mts → runtime-CbcODreO.d.mts} +2 -2
- package/dist/{runtime-Cl4WEBC7.d.mts.map → runtime-CbcODreO.d.mts.map} +1 -1
- package/dist/runtime.d.mts +3 -3
- package/dist/runtime.mjs +2 -2
- package/dist/{sql-execution-plan-Da_d82pz-8mU4Fvvo.d.mts → sql-execution-plan-qxvHnFcA-eC6n5Ek9.d.mts} +3 -3
- package/dist/sql-execution-plan-qxvHnFcA-eC6n5Ek9.d.mts.map +1 -0
- package/dist/{types-dD8JJfeD-D8LDAPQW.d.mts → types-CLN9Y3BH-DiekC2oe.d.mts} +6 -6
- package/dist/{types-dD8JJfeD-D8LDAPQW.d.mts.map → types-CLN9Y3BH-DiekC2oe.d.mts.map} +1 -1
- package/dist/{types-D-_2ZCos-9q6j1gKx.mjs → types-CSP6WmWn-DVuxsZJW.mjs} +35 -4
- package/dist/types-CSP6WmWn-DVuxsZJW.mjs.map +1 -0
- package/dist/types-DrgaZ2lG.d.mts +1 -0
- package/dist/{types-IqZMLrFC-BXR6F1nU.d.mts → types-IqZMLrFC-B8FPWCcB.d.mts} +2 -2
- package/dist/{types-IqZMLrFC-BXR6F1nU.d.mts.map → types-IqZMLrFC-B8FPWCcB.d.mts.map} +1 -1
- package/dist/{types-BwePDa3m-CMy7CB9N.d.mts → types-ZzeQKmYD-PJlaIsww.d.mts} +9 -3
- package/dist/types-ZzeQKmYD-PJlaIsww.d.mts.map +1 -0
- package/package.json +18 -18
- package/dist/aggregate-descriptor-registry-DPV4z8MS.mjs.map +0 -1
- package/dist/ast-DDAyIKdt.mjs.map +0 -1
- package/dist/codec-types-B_xmJL2m-CC_Q-_u9.mjs +0 -24
- package/dist/codec-types-B_xmJL2m-CC_Q-_u9.mjs.map +0 -1
- package/dist/control-adapter-DpCa7zm_.d.mts +0 -1
- package/dist/db-B5PMfDDd-DcJie6Go.d.mts.map +0 -1
- package/dist/ddl-types-BMp2bWVS-D1waZJ4n.mjs.map +0 -1
- package/dist/middleware-BDVd5Hbn.d.mts +0 -1
- package/dist/plan-Ba8TRfem.d.mts +0 -2
- package/dist/plan-Bmp5R9sq-BYkfwppR.d.mts.map +0 -1
- package/dist/query-lane-context-CZEGLV1r.d.mts +0 -1
- package/dist/query-lane-context-DjyAyYht-BWgx-nsP.d.mts.map +0 -1
- package/dist/sql-execution-plan-Da_d82pz-8mU4Fvvo.d.mts.map +0 -1
- package/dist/types-BwePDa3m-CMy7CB9N.d.mts.map +0 -1
- package/dist/types-D-_2ZCos-9q6j1gKx.mjs.map +0 -1
- package/dist/types-KK31t3v0.d.mts +0 -1
package/dist/orm-client.mjs
CHANGED
|
@@ -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 {
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
}) :
|
|
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
|
|
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
|
-
*
|
|
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.
|
|
2136
|
-
*
|
|
2137
|
-
*
|
|
2138
|
-
*
|
|
2139
|
-
*
|
|
2140
|
-
*
|
|
2141
|
-
*
|
|
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
|
-
|
|
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
|
-
},
|
|
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
|
-
|
|
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
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
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" &&
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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] ??
|
|
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(
|
|
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) ??
|
|
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
|
|
4714
|
+
return candidate.prototype instanceof CollectionBase;
|
|
4669
4715
|
}
|
|
4670
4716
|
//#endregion
|
|
4671
4717
|
export { Collection, GroupedCollection, all, and, createModelAccessor, emptyState, not, or, orm };
|