@prisma-next/sql-orm-client 0.5.0-dev.8 → 0.5.0-dev.85

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/index.mjs CHANGED
@@ -1,7 +1,6 @@
1
- import { AsyncIterableResult } from "@prisma-next/runtime-executor";
2
- import { AggregateExpr, AndExpr, BinaryExpr, ColumnRef, DefaultValueExpr, DeleteAst, DerivedTableSource, EqColJoinOn, ExistsExpr, InsertAst, InsertOnConflict, JoinAst, JsonArrayAggExpr, JsonObjectExpr, ListExpression, LiteralExpr, NotExpr, NullCheckExpr, OperationExpr, OrExpr, OrderByItem, ParamRef, ProjectionItem, SelectAst, SubqueryExpr, TableSource, UpdateAst, isWhereExpr } from "@prisma-next/sql-relational-core/ast";
1
+ import { AsyncIterableResult } from "@prisma-next/framework-components/runtime";
2
+ import { AggregateExpr, AndExpr, BinaryExpr, ColumnRef, DefaultValueExpr, DeleteAst, DerivedTableSource, EqColJoinOn, ExistsExpr, InsertAst, InsertOnConflict, JoinAst, JsonArrayAggExpr, JsonObjectExpr, ListExpression, LiteralExpr, NotExpr, NullCheckExpr, OrExpr, OrderByItem, ParamRef, ProjectionItem, SelectAst, SubqueryExpr, TableSource, UpdateAst, collectOrderedParamRefs, isWhereExpr } from "@prisma-next/sql-relational-core/ast";
3
3
  import { ifDefined } from "@prisma-next/utils/defined";
4
-
5
4
  //#region src/collection-contract.ts
6
5
  function modelsOf(contract) {
7
6
  return contract.models;
@@ -177,6 +176,13 @@ function resolveModelTableName(contract, modelName) {
177
176
  function resolvePrimaryKeyColumn(contract, tableName) {
178
177
  return contract.storage.tables[tableName]?.primaryKey?.columns[0] ?? "id";
179
178
  }
179
+ function resolveRowIdentityColumns(contract, tableName) {
180
+ const table = contract.storage.tables[tableName];
181
+ if (!table) return [];
182
+ if (table.primaryKey && table.primaryKey.columns.length > 0) return table.primaryKey.columns;
183
+ for (const unique of table.uniques) if (unique.columns.length > 0) return unique.columns;
184
+ return [];
185
+ }
180
186
  function assertReturningCapability(contract, action) {
181
187
  if (hasContractCapability(contract, "returning")) return;
182
188
  throw new Error(`${action} requires contract capability "returning"`);
@@ -198,7 +204,6 @@ function capabilityEnabled(value) {
198
204
  function isToOneCardinality(cardinality) {
199
205
  return cardinality === "1:1" || cardinality === "N:1";
200
206
  }
201
-
202
207
  //#endregion
203
208
  //#region src/aggregate-builder.ts
204
209
  function createAggregateBuilder(contract, modelName) {
@@ -238,7 +243,6 @@ function createFieldAggregateSelector(fieldToColumn, field, fn) {
238
243
  column: fieldToColumn[fieldName] ?? fieldName
239
244
  };
240
245
  }
241
-
242
246
  //#endregion
243
247
  //#region src/collection-aggregate-result.ts
244
248
  function normalizeAggregateResult(aggregateSpec, row) {
@@ -270,7 +274,6 @@ function normalizeAggregateResult(aggregateSpec, row) {
270
274
  }
271
275
  return result;
272
276
  }
273
-
274
277
  //#endregion
275
278
  //#region src/collection-column-mapping.ts
276
279
  function mapFieldsToColumns(contract, modelName, fieldNames) {
@@ -287,7 +290,6 @@ function mapCursorValuesToColumns(contract, modelName, cursorValues) {
287
290
  }
288
291
  return mappedCursor;
289
292
  }
290
-
291
293
  //#endregion
292
294
  //#region src/collection-runtime.ts
293
295
  function augmentSelectionForJoinColumns(selectedFields, requiredColumns) {
@@ -348,12 +350,12 @@ function mapPolymorphicRow(contract, baseModelName, polyInfo, row, variantName)
348
350
  const variant = variantName ? polyInfo.variants.get(variantName) : polyInfo.variantsByValue.get(row[polyInfo.discriminatorColumn]);
349
351
  if (!variant) {
350
352
  const baseMap = getCompleteColumnToFieldMap(contract, baseModelName);
351
- const mapped$1 = {};
353
+ const mapped = {};
352
354
  for (const [col, val] of Object.entries(row)) {
353
355
  const field = baseMap[col];
354
- if (field !== void 0) mapped$1[field] = val;
356
+ if (field !== void 0) mapped[field] = val;
355
357
  }
356
- return mapped$1;
358
+ return mapped;
357
359
  }
358
360
  const mtiTable = variant.strategy === "mti" ? variant.table : void 0;
359
361
  const mergedMap = getMergedColumnToFieldMap(contract, baseModelName, variant.modelName, mtiTable);
@@ -389,95 +391,78 @@ async function acquireRuntimeScope(runtime) {
389
391
  };
390
392
  return { scope: connection };
391
393
  }
392
-
393
394
  //#endregion
394
395
  //#region src/execute-query-plan.ts
395
396
  function executeQueryPlan(scope, plan) {
396
397
  return scope.execute(plan);
397
398
  }
398
-
399
399
  //#endregion
400
400
  //#region src/include-strategy.ts
401
+ /**
402
+ * Choose the SQL emission strategy for nested includes based on the
403
+ * contract's declared capabilities.
404
+ *
405
+ * - `'lateral'`: outer SELECT with one LATERAL JOIN per relation,
406
+ * aggregating to JSON. Requires both `lateral` and `jsonAgg`.
407
+ * Postgres has both.
408
+ * - `'correlated'`: outer SELECT with one correlated subquery per
409
+ * relation, aggregating to JSON. Requires `jsonAgg` only.
410
+ * SQLite has `jsonAgg` (via `json_group_array`) but no LATERAL.
411
+ * - `'multiQuery'`: fallback. One SELECT per relation, stitched
412
+ * together in JS via `WHERE pk IN (parent-pk-values)`. Always
413
+ * correct; just N+1 round-trips.
414
+ *
415
+ * The capability flags are looked up under the contract's
416
+ * `targetFamily` and `target` namespaces — the two layers the contract
417
+ * emitter actually populates. Cross-namespace ("`postgres.lateral`
418
+ * found while running SQLite") false positives are impossible because
419
+ * we only inspect the running target's namespaces.
420
+ */
401
421
  function selectIncludeStrategy(contract) {
402
- const capabilities = contract.capabilities;
403
- const hasLateral = hasCapability(capabilities?.["lateral"]);
404
- const hasJsonAgg = hasCapability(capabilities?.["jsonAgg"]);
422
+ const hasLateral = capabilityFlag(contract, "lateral");
423
+ const hasJsonAgg = capabilityFlag(contract, "jsonAgg");
405
424
  if (hasLateral && hasJsonAgg) return "lateral";
406
425
  if (hasJsonAgg) return "correlated";
407
426
  return "multiQuery";
408
427
  }
409
- function hasCapability(value) {
410
- if (value === true) return true;
411
- if (typeof value !== "object" || value === null) return false;
412
- const flags = value;
413
- return Object.values(flags).some((flag) => flag === true);
428
+ /**
429
+ * Read a capability flag from the contract's target/family namespaces.
430
+ *
431
+ * The contract emitter populates `capabilities[targetFamily]` (universal
432
+ * SQL flags like `jsonAgg`, `returning`) and `capabilities[target]`
433
+ * (target-specific flags like `lateral` on Postgres). Either may
434
+ * declare a given flag; the family namespace declares the floor and the
435
+ * target namespace can extend on top.
436
+ */
437
+ function capabilityFlag(contract, flag) {
438
+ return contract.capabilities[contract.targetFamily]?.[flag] === true || contract.capabilities[contract.target]?.[flag] === true;
414
439
  }
415
-
416
440
  //#endregion
417
441
  //#region src/query-plan-meta.ts
418
- function resolveProjectionCodecs(contract, ast) {
419
- const codecs = {};
420
- if (ast.kind === "select") {
421
- for (const item of ast.projection) if (item.expr.kind === "column-ref") {
422
- const col = contract.storage.tables[item.expr.table]?.columns[item.expr.column];
423
- if (col?.codecId) codecs[item.alias] = col.codecId;
424
- }
425
- } else if (ast.returning) {
426
- const tableName = ast.table.name;
427
- const table = contract.storage.tables[tableName];
428
- if (!table) return void 0;
429
- for (const colRef of ast.returning) {
430
- const col = table.columns[colRef.column];
431
- if (col?.codecId) codecs[colRef.column] = col.codecId;
432
- }
433
- }
434
- return Object.keys(codecs).length > 0 ? codecs : void 0;
435
- }
436
442
  function deriveParamsFromAst(ast) {
437
- const collectedParams = [...new Set(ast.collectParamRefs())];
438
- return {
439
- params: collectedParams.map((p) => p.value),
440
- paramDescriptors: collectedParams.map((p) => ({
441
- ...ifDefined("name", p.name),
442
- ...ifDefined("codecId", p.codecId),
443
- source: "dsl"
444
- }))
445
- };
443
+ return { params: collectOrderedParamRefs(ast).map((p) => p.value) };
446
444
  }
447
445
  function resolveTableColumns(contract, tableName) {
448
446
  const table = contract.storage.tables[tableName];
449
447
  if (!table) throw new Error(`Unknown table "${tableName}" in SQL ORM query planner`);
450
448
  return Object.keys(table.columns);
451
449
  }
452
- function buildOrmPlanMeta(contract, paramDescriptors = []) {
450
+ function buildOrmPlanMeta(contract) {
453
451
  return {
454
452
  target: contract.target,
455
453
  targetFamily: contract.targetFamily,
456
454
  storageHash: contract.storage.storageHash,
457
455
  ...contract.profileHash !== void 0 ? { profileHash: contract.profileHash } : {},
458
- lane: "orm-client",
459
- paramDescriptors: [...paramDescriptors]
456
+ lane: "orm-client"
460
457
  };
461
458
  }
462
- function buildOrmQueryPlan(contract, ast, params, paramDescriptors = []) {
463
- const projectionTypes = resolveProjectionCodecs(contract, ast);
464
- const codecAnnotations = projectionTypes ? { codecs: Object.freeze({ ...projectionTypes }) } : void 0;
465
- const limitAnnotation = ast.kind === "select" && ast.limit !== void 0 ? { limit: ast.limit } : void 0;
466
- const annotations = codecAnnotations || limitAnnotation ? Object.freeze({
467
- ...codecAnnotations,
468
- ...limitAnnotation
469
- }) : void 0;
459
+ function buildOrmQueryPlan(contract, ast, params) {
470
460
  return Object.freeze({
471
461
  ast,
472
462
  params: [...params],
473
- meta: {
474
- ...buildOrmPlanMeta(contract, paramDescriptors),
475
- ...ifDefined("projectionTypes", projectionTypes),
476
- ...ifDefined("annotations", annotations)
477
- }
463
+ meta: buildOrmPlanMeta(contract)
478
464
  });
479
465
  }
480
-
481
466
  //#endregion
482
467
  //#region src/where-utils.ts
483
468
  function combineWhereExprs(filters) {
@@ -485,13 +470,23 @@ function combineWhereExprs(filters) {
485
470
  if (filters.length === 1) return filters[0];
486
471
  return AndExpr.of(filters);
487
472
  }
488
-
489
473
  //#endregion
490
474
  //#region src/query-plan-aggregate.ts
491
- function toAggregateExpr(tableName, selector) {
492
- if (selector.fn === "count") return AggregateExpr.count();
475
+ function toAggregateProjection(contract, tableName, selector) {
476
+ if (selector.fn === "count") return {
477
+ expr: AggregateExpr.count(),
478
+ codecId: void 0
479
+ };
493
480
  if (!selector.column) throw new Error(`Aggregate selector "${selector.fn}" requires a field`);
494
- return new AggregateExpr(selector.fn, ColumnRef.of(tableName, selector.column));
481
+ const expr = new AggregateExpr(selector.fn, ColumnRef.of(tableName, selector.column));
482
+ if (selector.fn === "min" || selector.fn === "max") return {
483
+ expr,
484
+ codecId: contract.storage.tables[tableName]?.columns[selector.column]?.codecId
485
+ };
486
+ return {
487
+ expr,
488
+ codecId: void 0
489
+ };
495
490
  }
496
491
  function validateGroupedComparable(value) {
497
492
  switch (value.kind) {
@@ -528,53 +523,61 @@ function validateGroupedHavingExpr(expr) {
528
523
  throw new Error("ParamRef is not supported in grouped having expressions");
529
524
  },
530
525
  list: rejectHavingExpr,
531
- and(expr$1) {
532
- return AndExpr.of(expr$1.exprs.map((child) => validateGroupedHavingExpr(child)));
526
+ and(expr) {
527
+ return AndExpr.of(expr.exprs.map((child) => validateGroupedHavingExpr(child)));
533
528
  },
534
- or(expr$1) {
535
- return OrExpr.of(expr$1.exprs.map((child) => validateGroupedHavingExpr(child)));
529
+ or(expr) {
530
+ return OrExpr.of(expr.exprs.map((child) => validateGroupedHavingExpr(child)));
536
531
  },
537
- exists(expr$1) {
538
- throw new Error(`Unsupported grouped having expression kind "${expr$1.kind}"`);
532
+ exists(expr) {
533
+ throw new Error(`Unsupported grouped having expression kind "${expr.kind}"`);
539
534
  },
540
- nullCheck(expr$1) {
541
- return new NullCheckExpr(validateGroupedMetricExpr(expr$1.expr), expr$1.isNull);
535
+ nullCheck(expr) {
536
+ return new NullCheckExpr(validateGroupedMetricExpr(expr.expr), expr.isNull);
542
537
  },
543
- not(expr$1) {
544
- return new NotExpr(validateGroupedHavingExpr(expr$1.expr));
538
+ not(expr) {
539
+ return new NotExpr(validateGroupedHavingExpr(expr.expr));
545
540
  },
546
- binary(expr$1) {
547
- return new BinaryExpr(expr$1.op, validateGroupedMetricExpr(expr$1.left), validateGroupedComparable(expr$1.right));
541
+ binary(expr) {
542
+ return new BinaryExpr(expr.op, validateGroupedMetricExpr(expr.left), validateGroupedComparable(expr.right));
548
543
  }
549
544
  });
550
545
  }
551
546
  function compileAggregate(contract, tableName, filters, aggregateSpec) {
552
547
  const entries = Object.entries(aggregateSpec);
553
548
  if (entries.length === 0) throw new Error("aggregate() requires at least one aggregation selector");
554
- const projection = entries.map(([alias, selector]) => ProjectionItem.of(alias, toAggregateExpr(tableName, selector)));
549
+ const projection = entries.map(([alias, selector]) => {
550
+ const { expr, codecId } = toAggregateProjection(contract, tableName, selector);
551
+ return ProjectionItem.of(alias, expr, codecId);
552
+ });
555
553
  let ast = SelectAst.from(TableSource.named(tableName)).withProjection(projection);
556
554
  const where = combineWhereExprs(filters);
557
555
  if (where) ast = ast.withWhere(where);
558
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
559
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
556
+ const { params } = deriveParamsFromAst(ast);
557
+ return buildOrmQueryPlan(contract, ast, params);
560
558
  }
561
559
  function compileGroupedAggregate(contract, tableName, filters, groupByColumns, aggregateSpec, havingExpr) {
562
560
  if (groupByColumns.length === 0) throw new Error("groupBy() requires at least one field");
563
561
  const entries = Object.entries(aggregateSpec);
564
562
  if (entries.length === 0) throw new Error("groupBy().aggregate() requires at least one aggregation selector");
565
- const projection = [...groupByColumns.map((column) => ProjectionItem.of(column, ColumnRef.of(tableName, column))), ...entries.map(([alias, selector]) => ProjectionItem.of(alias, toAggregateExpr(tableName, selector)))];
563
+ const table = contract.storage.tables[tableName];
564
+ const projection = [...groupByColumns.map((column) => ProjectionItem.of(column, ColumnRef.of(tableName, column), table?.columns[column]?.codecId)), ...entries.map(([alias, selector]) => {
565
+ const { expr, codecId } = toAggregateProjection(contract, tableName, selector);
566
+ return ProjectionItem.of(alias, expr, codecId);
567
+ })];
566
568
  let ast = SelectAst.from(TableSource.named(tableName)).withProjection(projection).withGroupBy(groupByColumns.map((column) => ColumnRef.of(tableName, column)));
567
569
  const where = combineWhereExprs(filters);
568
570
  if (where) ast = ast.withWhere(where);
569
571
  if (havingExpr) ast = ast.withHaving(validateGroupedHavingExpr(havingExpr));
570
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
571
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
572
+ const { params } = deriveParamsFromAst(ast);
573
+ return buildOrmQueryPlan(contract, ast, params);
572
574
  }
573
-
574
575
  //#endregion
575
576
  //#region src/query-plan-mutations.ts
576
577
  function buildReturningColumns(contract, tableName, returningColumns) {
577
- return (returningColumns && returningColumns.length > 0 ? [...returningColumns] : resolveTableColumns(contract, tableName)).map((column) => ColumnRef.of(tableName, column));
578
+ const columns = returningColumns && returningColumns.length > 0 ? [...returningColumns] : resolveTableColumns(contract, tableName);
579
+ const table = contract.storage.tables[tableName];
580
+ return columns.map((column) => ProjectionItem.of(column, ColumnRef.of(tableName, column), table?.columns[column]?.codecId));
578
581
  }
579
582
  function toParamAssignments(contract, tableName, values) {
580
583
  const assignments = {};
@@ -585,7 +588,11 @@ function toParamAssignments(contract, tableName, values) {
585
588
  if (!codecId) throw new Error(`Unknown column "${column}" in table "${tableName}"`);
586
589
  assignments[column] = ParamRef.of(value, {
587
590
  name: column,
588
- codecId
591
+ codecId,
592
+ refs: {
593
+ table: tableName,
594
+ column
595
+ }
589
596
  });
590
597
  }
591
598
  return { assignments };
@@ -610,7 +617,11 @@ function normalizeInsertRows(contract, tableName, rows) {
610
617
  if (!codecId) throw new Error(`Unknown column "${column}" in table "${tableName}"`);
611
618
  normalizedRow[column] = ParamRef.of(row[column], {
612
619
  name: column,
613
- codecId
620
+ codecId,
621
+ refs: {
622
+ table: tableName,
623
+ column
624
+ }
614
625
  });
615
626
  continue;
616
627
  }
@@ -622,14 +633,14 @@ function normalizeInsertRows(contract, tableName, rows) {
622
633
  function compileInsertReturning(contract, tableName, rows, returningColumns) {
623
634
  const { rows: normalizedRows } = normalizeInsertRows(contract, tableName, rows);
624
635
  const ast = InsertAst.into(TableSource.named(tableName)).withRows(normalizedRows).withReturning(buildReturningColumns(contract, tableName, returningColumns));
625
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
626
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
636
+ const { params } = deriveParamsFromAst(ast);
637
+ return buildOrmQueryPlan(contract, ast, params);
627
638
  }
628
639
  function compileInsertCount(contract, tableName, rows) {
629
640
  const { rows: normalizedRows } = normalizeInsertRows(contract, tableName, rows);
630
641
  const ast = InsertAst.into(TableSource.named(tableName)).withRows(normalizedRows);
631
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
632
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
642
+ const { params } = deriveParamsFromAst(ast);
643
+ return buildOrmQueryPlan(contract, ast, params);
633
644
  }
634
645
  function stripUndefinedValues(row) {
635
646
  const result = {};
@@ -665,40 +676,39 @@ function compileUpsertReturning(contract, tableName, createValues, updateValues,
665
676
  const updateAssignments = Object.keys(updateValues).length > 0 ? toParamAssignments(contract, tableName, updateValues) : void 0;
666
677
  const onConflict = updateAssignments ? InsertOnConflict.on(conflictColumns.map((column) => ColumnRef.of(tableName, column))).doUpdateSet(updateAssignments.assignments) : InsertOnConflict.on(conflictColumns.map((column) => ColumnRef.of(tableName, column))).doNothing();
667
678
  const ast = InsertAst.into(TableSource.named(tableName)).withValues(createAssignments.assignments).withOnConflict(onConflict).withReturning(buildReturningColumns(contract, tableName, returningColumns));
668
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
669
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
679
+ const { params } = deriveParamsFromAst(ast);
680
+ return buildOrmQueryPlan(contract, ast, params);
670
681
  }
671
682
  function compileUpdateReturning(contract, tableName, setValues, filters, returningColumns) {
672
683
  const where = combineWhereExprs(filters);
673
684
  const { assignments } = toParamAssignments(contract, tableName, setValues);
674
685
  let ast = UpdateAst.table(TableSource.named(tableName)).withSet(assignments).withReturning(buildReturningColumns(contract, tableName, returningColumns));
675
686
  if (where) ast = ast.withWhere(where);
676
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
677
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
687
+ const { params } = deriveParamsFromAst(ast);
688
+ return buildOrmQueryPlan(contract, ast, params);
678
689
  }
679
690
  function compileUpdateCount(contract, tableName, setValues, filters) {
680
691
  const where = combineWhereExprs(filters);
681
692
  const { assignments } = toParamAssignments(contract, tableName, setValues);
682
693
  let ast = UpdateAst.table(TableSource.named(tableName)).withSet(assignments);
683
694
  if (where) ast = ast.withWhere(where);
684
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
685
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
695
+ const { params } = deriveParamsFromAst(ast);
696
+ return buildOrmQueryPlan(contract, ast, params);
686
697
  }
687
698
  function compileDeleteReturning(contract, tableName, filters, returningColumns) {
688
699
  const where = combineWhereExprs(filters);
689
700
  let ast = DeleteAst.from(TableSource.named(tableName)).withReturning(buildReturningColumns(contract, tableName, returningColumns));
690
701
  if (where) ast = ast.withWhere(where);
691
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
692
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
702
+ const { params } = deriveParamsFromAst(ast);
703
+ return buildOrmQueryPlan(contract, ast, params);
693
704
  }
694
705
  function compileDeleteCount(contract, tableName, filters) {
695
706
  const where = combineWhereExprs(filters);
696
707
  let ast = DeleteAst.from(TableSource.named(tableName));
697
708
  if (where) ast = ast.withWhere(where);
698
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
699
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
709
+ const { params } = deriveParamsFromAst(ast);
710
+ return buildOrmQueryPlan(contract, ast, params);
700
711
  }
701
-
702
712
  //#endregion
703
713
  //#region src/where-binding.ts
704
714
  function bindWhereExpr(contract, expr) {
@@ -706,55 +716,55 @@ function bindWhereExpr(contract, expr) {
706
716
  }
707
717
  function bindWhereExprNode(contract, expr) {
708
718
  return expr.accept({
709
- columnRef(expr$1) {
710
- return bindExpression(contract, expr$1);
719
+ columnRef(expr) {
720
+ return bindExpression(contract, expr);
711
721
  },
712
- identifierRef(expr$1) {
713
- return expr$1;
722
+ identifierRef(expr) {
723
+ return expr;
714
724
  },
715
- subquery(expr$1) {
716
- return bindExpression(contract, expr$1);
725
+ subquery(expr) {
726
+ return bindExpression(contract, expr);
717
727
  },
718
- operation(expr$1) {
719
- return bindExpression(contract, expr$1);
728
+ operation(expr) {
729
+ return bindExpression(contract, expr);
720
730
  },
721
- aggregate(expr$1) {
722
- return bindExpression(contract, expr$1);
731
+ aggregate(expr) {
732
+ return bindExpression(contract, expr);
723
733
  },
724
- jsonObject(expr$1) {
725
- return bindExpression(contract, expr$1);
734
+ jsonObject(expr) {
735
+ return bindExpression(contract, expr);
726
736
  },
727
- jsonArrayAgg(expr$1) {
728
- return bindExpression(contract, expr$1);
737
+ jsonArrayAgg(expr) {
738
+ return bindExpression(contract, expr);
729
739
  },
730
- literal(expr$1) {
731
- return expr$1;
740
+ literal(expr) {
741
+ return expr;
732
742
  },
733
- param(expr$1) {
734
- return expr$1;
743
+ param(expr) {
744
+ return expr;
735
745
  },
736
- list(expr$1) {
737
- return bindExpression(contract, expr$1);
746
+ list(expr) {
747
+ return bindExpression(contract, expr);
738
748
  },
739
- binary(expr$1) {
740
- const left = bindExpression(contract, expr$1.left);
749
+ binary(expr) {
750
+ const left = bindExpression(contract, expr.left);
741
751
  const bindingColumn = left.kind === "column-ref" ? left : void 0;
742
- return new BinaryExpr(expr$1.op, left, bindComparable(contract, expr$1.right, bindingColumn));
752
+ return new BinaryExpr(expr.op, left, bindComparable(contract, expr.right, bindingColumn));
743
753
  },
744
- and(expr$1) {
745
- return AndExpr.of(expr$1.exprs.map((part) => bindWhereExprNode(contract, part)));
754
+ and(expr) {
755
+ return AndExpr.of(expr.exprs.map((part) => bindWhereExprNode(contract, part)));
746
756
  },
747
- or(expr$1) {
748
- return OrExpr.of(expr$1.exprs.map((part) => bindWhereExprNode(contract, part)));
757
+ or(expr) {
758
+ return OrExpr.of(expr.exprs.map((part) => bindWhereExprNode(contract, part)));
749
759
  },
750
- exists(expr$1) {
751
- return expr$1.notExists ? ExistsExpr.notExists(bindSelectAst(contract, expr$1.subquery)) : ExistsExpr.exists(bindSelectAst(contract, expr$1.subquery));
760
+ exists(expr) {
761
+ return expr.notExists ? ExistsExpr.notExists(bindSelectAst(contract, expr.subquery)) : ExistsExpr.exists(bindSelectAst(contract, expr.subquery));
752
762
  },
753
- nullCheck(expr$1) {
754
- return expr$1.isNull ? NullCheckExpr.isNull(bindExpression(contract, expr$1.expr)) : NullCheckExpr.isNotNull(bindExpression(contract, expr$1.expr));
763
+ nullCheck(expr) {
764
+ return expr.isNull ? NullCheckExpr.isNull(bindExpression(contract, expr.expr)) : NullCheckExpr.isNotNull(bindExpression(contract, expr.expr));
755
765
  },
756
- not(expr$1) {
757
- return new NotExpr(bindWhereExprNode(contract, expr$1.expr));
766
+ not(expr) {
767
+ return new NotExpr(bindWhereExprNode(contract, expr.expr));
758
768
  }
759
769
  });
760
770
  }
@@ -767,7 +777,13 @@ function bindComparable(contract, comparable, bindingColumn) {
767
777
  function createParamRef(contract, columnRef, value) {
768
778
  const codecId = contract.storage.tables[columnRef.table]?.columns[columnRef.column]?.codecId;
769
779
  if (!codecId) throw new Error(`Unknown column "${columnRef.column}" in table "${columnRef.table}"`);
770
- return ParamRef.of(value, { codecId });
780
+ return ParamRef.of(value, {
781
+ codecId,
782
+ refs: {
783
+ table: columnRef.table,
784
+ column: columnRef.column
785
+ }
786
+ });
771
787
  }
772
788
  function createExpressionBinder(contract) {
773
789
  return { select: (ast) => bindSelectAst(contract, ast) };
@@ -796,7 +812,7 @@ function bindSelectAst(contract, ast) {
796
812
  return new SelectAst({
797
813
  from: bindFromSource(contract, ast.from),
798
814
  joins: ast.joins?.map((join) => bindJoin(contract, join)),
799
- projection: ast.projection.map((projection) => new ProjectionItem(projection.alias, bindProjectionExpr(contract, projection.expr))),
815
+ projection: ast.projection.map((projection) => new ProjectionItem(projection.alias, bindProjectionExpr(contract, projection.expr), projection.codecId, projection.refs)),
800
816
  where: ast.where ? bindWhereExprNode(contract, ast.where) : void 0,
801
817
  orderBy: ast.orderBy?.map((orderItem) => bindOrderByItem(contract, orderItem)),
802
818
  distinct: ast.distinct,
@@ -808,11 +824,12 @@ function bindSelectAst(contract, ast) {
808
824
  selectAllIntent: ast.selectAllIntent
809
825
  });
810
826
  }
811
-
812
827
  //#endregion
813
828
  //#region src/query-plan-select.ts
814
829
  function buildProjection(contract, tableName, selectedFields, tableRef = tableName) {
815
- return (selectedFields && selectedFields.length > 0 ? [...selectedFields] : resolveTableColumns(contract, tableName)).map((column) => ProjectionItem.of(column, ColumnRef.of(tableRef, column)));
830
+ const columns = selectedFields && selectedFields.length > 0 ? [...selectedFields] : resolveTableColumns(contract, tableName);
831
+ const table = contract.storage.tables[tableName];
832
+ return columns.map((column) => ProjectionItem.of(column, ColumnRef.of(tableRef, column), table?.columns[column]?.codecId));
816
833
  }
817
834
  function createBoundaryExpr(tableName, entry) {
818
835
  return new BinaryExpr(entry.direction === "asc" ? "gt" : "lt", ColumnRef.of(tableName, entry.column), LiteralExpr.of(entry.value));
@@ -946,10 +963,11 @@ function buildMtiJoins(contract, polyInfo, variantName) {
946
963
  const join = joinType === "inner" ? JoinAst.inner(TableSource.named(variant.table), joinOn) : JoinAst.left(TableSource.named(variant.table), joinOn);
947
964
  joins.push(join);
948
965
  const variantColumns = resolveTableColumns(contract, variant.table);
966
+ const variantTable = contract.storage.tables[variant.table];
949
967
  for (const col of variantColumns) {
950
968
  if (col === pkColumn) continue;
951
969
  const alias = `${variant.table}__${col}`;
952
- projection.push(ProjectionItem.of(alias, ColumnRef.of(variant.table, col)));
970
+ projection.push(ProjectionItem.of(alias, ColumnRef.of(variant.table, col), variantTable?.columns[col]?.codecId));
953
971
  }
954
972
  }
955
973
  return {
@@ -967,8 +985,8 @@ function compileSelect(contract, tableName, state, modelName) {
967
985
  joins: mtiArtifacts.joins,
968
986
  includeProjection: mtiArtifacts.projection
969
987
  } : void 0);
970
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
971
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
988
+ const { params } = deriveParamsFromAst(ast);
989
+ return buildOrmQueryPlan(contract, ast, params);
972
990
  }
973
991
  function compileRelationSelect(contract, relatedTableName, targetColumn, parentPks, nestedState) {
974
992
  const inFilter = BinaryExpr.in(ColumnRef.of(relatedTableName, targetColumn), ListExpression.fromValues(parentPks));
@@ -993,9 +1011,9 @@ function compileSelectWithIncludeStrategy(contract, tableName, state, strategy,
993
1011
  }
994
1012
  for (const include of state.includes) {
995
1013
  if (strategy === "lateral") {
996
- const artifact$1 = buildLateralIncludeArtifacts(contract, tableName, include);
997
- includeJoins.push(artifact$1.join);
998
- includeProjection.push(artifact$1.projection);
1014
+ const artifact = buildLateralIncludeArtifacts(contract, tableName, include);
1015
+ includeJoins.push(artifact.join);
1016
+ includeProjection.push(artifact.projection);
999
1017
  continue;
1000
1018
  }
1001
1019
  const artifact = buildCorrelatedIncludeProjection(contract, tableName, include);
@@ -1009,10 +1027,9 @@ function compileSelectWithIncludeStrategy(contract, tableName, state, strategy,
1009
1027
  includeProjection,
1010
1028
  ...topLevelWhere ? { where: topLevelWhere } : {}
1011
1029
  });
1012
- const { params, paramDescriptors } = deriveParamsFromAst(ast);
1013
- return buildOrmQueryPlan(contract, ast, params, paramDescriptors);
1030
+ const { params } = deriveParamsFromAst(ast);
1031
+ return buildOrmQueryPlan(contract, ast, params);
1014
1032
  }
1015
-
1016
1033
  //#endregion
1017
1034
  //#region src/collection-dispatch.ts
1018
1035
  function dispatchCollectionRows(options) {
@@ -1282,7 +1299,6 @@ function coerceNumericValue(value) {
1282
1299
  }
1283
1300
  return null;
1284
1301
  }
1285
-
1286
1302
  //#endregion
1287
1303
  //#region src/collection-mutation-dispatch.ts
1288
1304
  function dispatchMutationRows(options) {
@@ -1364,7 +1380,6 @@ async function executeMutationReturningSingleRow(options) {
1364
1380
  if (release) await release();
1365
1381
  }
1366
1382
  }
1367
-
1368
1383
  //#endregion
1369
1384
  //#region src/filters.ts
1370
1385
  function and(...exprs) {
@@ -1401,9 +1416,8 @@ function shorthandToWhereExpr(context, modelName, filters) {
1401
1416
  function assertFieldHasEqualityTrait(context, modelName, fieldName) {
1402
1417
  const fieldType = modelOf(context.contract, modelName)?.fields?.[fieldName]?.type;
1403
1418
  const codecId = fieldType?.kind === "scalar" ? fieldType.codecId : void 0;
1404
- if (!(codecId ? context.codecs.traitsOf(codecId) : []).includes("equality")) throw new Error(`Shorthand filter on "${modelName}.${fieldName}": field does not support equality comparisons`);
1419
+ if (!(codecId ? context.codecDescriptors.descriptorFor(codecId)?.traits ?? [] : []).includes("equality")) throw new Error(`Shorthand filter on "${modelName}.${fieldName}": field does not support equality comparisons`);
1405
1420
  }
1406
-
1407
1421
  //#endregion
1408
1422
  //#region src/grouped-collection.ts
1409
1423
  var GroupedCollection = class GroupedCollection {
@@ -1503,7 +1517,6 @@ function coerceAggregateValue(fn, value) {
1503
1517
  }
1504
1518
  return value;
1505
1519
  }
1506
-
1507
1520
  //#endregion
1508
1521
  //#region src/include-descriptors.ts
1509
1522
  const aggregateFns = new Set([
@@ -1548,7 +1561,6 @@ function isCollectionState(value) {
1548
1561
  const candidate = value;
1549
1562
  return Array.isArray(candidate.filters) && Array.isArray(candidate.includes);
1550
1563
  }
1551
-
1552
1564
  //#endregion
1553
1565
  //#region src/types.ts
1554
1566
  function emptyState() {
@@ -1565,17 +1577,40 @@ function emptyState() {
1565
1577
  variantName: void 0
1566
1578
  };
1567
1579
  }
1568
- function param(codecId, value) {
1569
- return codecId ? ParamRef.of(value, { codecId }) : ParamRef.of(value);
1580
+ /**
1581
+ * Resolve the unique column ref carried by `left` so its surrounding `ParamRef` can dispatch through `forColumn`. The previous implementation only matched bare `column-ref` expressions, which lost refs the moment the expression got wrapped (`upper(f.id)`, `BinaryExpr`, function-call expressions, etc.) — and column-aware dispatch (AC-5) silently degraded for any predicate that touched a computed column.
1582
+ *
1583
+ * Walking via `collectColumnRefs()` and accepting only a single unambiguous ref gives `eq(upper(f.id), value)` and friends correct dispatch without inventing refs for ambiguous shapes (e.g. `eq(concat(f.firstName, f.lastName), value)` returns `undefined` and falls through to the codec-id path, which is correct for non-parameterized comparisons).
1584
+ */
1585
+ function refsFromLeft(left) {
1586
+ if (left.kind === "column-ref") return {
1587
+ table: left.table,
1588
+ column: left.column
1589
+ };
1590
+ const columnRefs = left.collectColumnRefs();
1591
+ if (columnRefs.length !== 1) return void 0;
1592
+ const single = columnRefs[0];
1593
+ if (!single) return void 0;
1594
+ return {
1595
+ table: single.table,
1596
+ column: single.column
1597
+ };
1598
+ }
1599
+ function param(codecId, value, refs) {
1600
+ if (codecId === void 0 && refs === void 0) return ParamRef.of(value);
1601
+ return ParamRef.of(value, {
1602
+ ...ifDefined("codecId", codecId),
1603
+ ...ifDefined("refs", refs)
1604
+ });
1570
1605
  }
1571
- function paramList(codecId, values) {
1572
- return ListExpression.of(values.map((value) => param(codecId, value)));
1606
+ function paramList(codecId, values, refs) {
1607
+ return ListExpression.of(values.map((value) => param(codecId, value, refs)));
1573
1608
  }
1574
1609
  function scalarComparisonMethod(op) {
1575
- return ((left, codecId) => (value) => new BinaryExpr(op, left, param(codecId, value)));
1610
+ return ((left, codecId) => (value) => new BinaryExpr(op, left, param(codecId, value, refsFromLeft(left))));
1576
1611
  }
1577
1612
  function listComparisonMethod(op) {
1578
- return ((left, codecId) => (values) => new BinaryExpr(op, left, paramList(codecId, values)));
1613
+ return ((left, codecId) => (values) => new BinaryExpr(op, left, paramList(codecId, values, refsFromLeft(left))));
1579
1614
  }
1580
1615
  /**
1581
1616
  * Declares trait requirements and runtime factory for each comparison method.
@@ -1637,7 +1672,6 @@ const COMPARISON_METHODS_META = {
1637
1672
  create: (left) => () => NullCheckExpr.isNotNull(left)
1638
1673
  }
1639
1674
  };
1640
-
1641
1675
  //#endregion
1642
1676
  //#region src/model-accessor.ts
1643
1677
  function createModelAccessor(context, modelName) {
@@ -1655,12 +1689,13 @@ function createModelAccessor(context, modelName) {
1655
1689
  existing.push(op);
1656
1690
  }
1657
1691
  for (const [name, entry] of Object.entries(context.queryOperations.entries())) {
1658
- const self = entry.args[0];
1659
1692
  const op = [name, entry];
1660
- if (self?.codecId) registerOp(self.codecId, op);
1661
- else if (self?.traits) for (const codec of context.codecs.values()) {
1662
- const codecTraits = codec.traits ?? [];
1663
- if (self.traits.every((t) => codecTraits.includes(t))) registerOp(codec.id, op);
1693
+ const self = entry.self;
1694
+ if (!self) continue;
1695
+ if (self.codecId !== void 0) registerOp(self.codecId, op);
1696
+ else if (self.traits !== void 0) for (const descriptor of context.codecDescriptors.values()) {
1697
+ const descriptorTraits = descriptor.traits;
1698
+ if (self.traits.every((t) => descriptorTraits.includes(t))) registerOp(descriptor.codecId, op);
1664
1699
  }
1665
1700
  }
1666
1701
  return new Proxy({}, { get(_target, prop) {
@@ -1668,48 +1703,51 @@ function createModelAccessor(context, modelName) {
1668
1703
  const relation = modelRelations[prop];
1669
1704
  if (relation) return createRelationFilterAccessor(context, modelName, tableName, relation);
1670
1705
  const columnName = fieldToColumn[prop] ?? prop;
1671
- const traits = resolveFieldTraits(contract, modelName, prop, context);
1672
- const codecId = resolveFieldCodecId(contract, tableName, columnName);
1673
- return createScalarFieldAccessor(tableName, columnName, codecId, traits, codecId ? opsByCodecId.get(codecId) ?? [] : [], context);
1706
+ const column = resolveColumn(contract, tableName, columnName);
1707
+ if (!column) return;
1708
+ const traits = context.codecDescriptors.descriptorFor(column.codecId)?.traits ?? [];
1709
+ const operations = opsByCodecId.get(column.codecId) ?? [];
1710
+ return createScalarFieldAccessor(tableName, columnName, column.codecId, column.nullable, traits, operations, context);
1674
1711
  } });
1675
1712
  }
1676
- function resolveFieldTraits(contract, modelName, fieldName, context) {
1677
- const fieldType = modelOf(contract, modelName)?.fields?.[fieldName]?.type;
1678
- const codecId = fieldType?.kind === "scalar" ? fieldType.codecId : void 0;
1679
- if (!codecId) return [];
1680
- return context.codecs.traitsOf(codecId);
1681
- }
1682
- function resolveFieldCodecId(contract, tableName, columnName) {
1683
- return (contract.storage.tables?.[tableName])?.columns?.[columnName]?.codecId;
1713
+ function resolveColumn(contract, tableName, columnName) {
1714
+ const column = contract.storage.tables?.[tableName]?.columns?.[columnName];
1715
+ if (!column) return void 0;
1716
+ return {
1717
+ codecId: column.codecId,
1718
+ nullable: column.nullable
1719
+ };
1684
1720
  }
1685
- function createScalarFieldAccessor(tableName, columnName, codecId, traits, operations, context) {
1721
+ function createScalarFieldAccessor(tableName, columnName, codecId, nullable, traits, operations, context) {
1686
1722
  const column = ColumnRef.of(tableName, columnName);
1687
- const methods = {};
1723
+ const comparisonEntries = [];
1688
1724
  for (const [name, meta] of Object.entries(COMPARISON_METHODS_META)) {
1689
1725
  if (meta.traits.some((t) => !traits.includes(t))) continue;
1690
- methods[name] = meta.create(column, codecId);
1726
+ comparisonEntries.push([name, meta.create(column, codecId)]);
1691
1727
  }
1692
- for (const [name, entry] of operations) methods[name] = createExtensionMethodFactory(column, name, entry, context);
1693
- return methods;
1728
+ const accessor = {
1729
+ returnType: {
1730
+ codecId,
1731
+ nullable
1732
+ },
1733
+ buildAst: () => column,
1734
+ ...Object.fromEntries(comparisonEntries)
1735
+ };
1736
+ for (const [name, entry] of operations) accessor[name] = createExtensionMethodFactory(accessor, entry, context);
1737
+ return accessor;
1694
1738
  }
1695
- function createExtensionMethodFactory(column, methodName, entry, context) {
1696
- const returnTraits = context.codecs.traitsOf(entry.returns.codecId);
1697
- const isPredicate = returnTraits.includes("boolean");
1739
+ function createExtensionMethodFactory(selfExpr, entry, context) {
1698
1740
  return (...args) => {
1699
- const opExpr = new OperationExpr({
1700
- method: methodName,
1701
- self: column,
1702
- args: entry.args.slice(1).map((argSpec, i) => {
1703
- return ParamRef.of(args[i], argSpec.codecId ? { codecId: argSpec.codecId } : void 0);
1704
- }),
1705
- returns: entry.returns,
1706
- lowering: entry.lowering
1707
- });
1708
- if (isPredicate) return opExpr;
1741
+ const impl = entry.impl;
1742
+ const result = impl(selfExpr, ...args);
1743
+ const returnCodecId = result.returnType.codecId;
1744
+ const returnTraits = context.codecDescriptors.descriptorFor(returnCodecId)?.traits ?? [];
1745
+ if (returnTraits.includes("boolean")) return result.buildAst();
1746
+ const resultAst = result.buildAst();
1709
1747
  const methods = {};
1710
1748
  for (const [resultMethodName, meta] of Object.entries(COMPARISON_METHODS_META)) {
1711
1749
  if (meta.traits.some((t) => !returnTraits.includes(t))) continue;
1712
- methods[resultMethodName] = meta.create(opExpr, entry.returns.codecId);
1750
+ methods[resultMethodName] = meta.create(resultAst, returnCodecId);
1713
1751
  }
1714
1752
  return methods;
1715
1753
  };
@@ -1756,7 +1794,7 @@ function toRelationWhereExpr(context, relatedModelName, predicate) {
1756
1794
  for (const [fieldName, value] of Object.entries(predicate)) {
1757
1795
  if (value === void 0) continue;
1758
1796
  const fieldAccessor = accessor[fieldName];
1759
- if (!fieldAccessor) continue;
1797
+ if (!fieldAccessor) throw new Error(`Shorthand filter on "${relatedModelName}.${fieldName}": field is not defined on the model`);
1760
1798
  if (value === null) {
1761
1799
  if (!fieldAccessor.isNull) throw new Error(`Shorthand filter on "${relatedModelName}.${fieldName}": isNull is unexpectedly missing — this is a bug in trait gating`);
1762
1800
  exprs.push(fieldAccessor.isNull());
@@ -1791,7 +1829,6 @@ function firstTargetColumn(contract, relation) {
1791
1829
  if (!firstField) return;
1792
1830
  return resolveFieldToColumn(contract, relation.to, firstField);
1793
1831
  }
1794
-
1795
1832
  //#endregion
1796
1833
  //#region src/relation-mutator.ts
1797
1834
  function createRelationMutator() {
@@ -1826,7 +1863,6 @@ function isRelationMutationDescriptor(value) {
1826
1863
  function isRelationMutationCallback(value) {
1827
1864
  return typeof value === "function";
1828
1865
  }
1829
-
1830
1866
  //#endregion
1831
1867
  //#region src/mutation-executor.ts
1832
1868
  function hasNestedMutationCallbacks(contract, modelName, data) {
@@ -1895,9 +1931,16 @@ async function updateFirstGraph(scope, context, modelName, filters, input) {
1895
1931
  let parentRow = existingRow;
1896
1932
  const mappedUpdateData = mapModelDataToStorageRow(contract, modelName, scalarData);
1897
1933
  if (Object.keys(mappedUpdateData).length > 0) {
1934
+ const tableName = resolveModelTableName(contract, modelName);
1935
+ const appliedUpdateDefaults = context.applyMutationDefaults({
1936
+ op: "update",
1937
+ table: tableName,
1938
+ values: mappedUpdateData
1939
+ });
1940
+ for (const def of appliedUpdateDefaults) mappedUpdateData[def.column] = def.value;
1898
1941
  const pkWhere = shorthandToWhereExpr(context, modelName, buildPrimaryKeyFilterFromRow(contract, modelName, existingRow));
1899
1942
  if (!pkWhere) throw new Error(`Failed to build primary key filter for model "${modelName}"`);
1900
- const updatedRaw = (await executeQueryPlan(scope, compileUpdateReturning(contract, resolveModelTableName(contract, modelName), mappedUpdateData, [pkWhere], void 0)).toArray())[0];
1943
+ const updatedRaw = (await executeQueryPlan(scope, compileUpdateReturning(contract, tableName, mappedUpdateData, [pkWhere], void 0)).toArray())[0];
1901
1944
  if (updatedRaw) parentRow = mapStorageRowToModelFields(contract, modelName, updatedRaw);
1902
1945
  }
1903
1946
  for (const relationMutation of childOwned) await applyChildOwnedMutation(scope, context, modelName, parentRow, relationMutation.relation, relationMutation.mutation);
@@ -1990,9 +2033,9 @@ async function applyChildOwnedMutation(scope, context, parentModelName, parentRo
1990
2033
  for (const criterion of mutation.criteria) {
1991
2034
  const criterionWhere = shorthandToWhereExpr(context, relation.relatedModelName, criterion);
1992
2035
  if (!criterionWhere) throw new Error(`connect() nested mutation for relation "${relation.relationName}" requires non-empty criterion`);
1993
- const setValues$1 = {};
1994
- for (const [childColumn, parentValue] of parentValues.entries()) setValues$1[childColumn] = parentValue;
1995
- await executeUpdateCount(scope, contract, relation.relatedTableName, setValues$1, [criterionWhere]);
2036
+ const setValues = {};
2037
+ for (const [childColumn, parentValue] of parentValues.entries()) setValues[childColumn] = parentValue;
2038
+ await executeUpdateCount(scope, contract, relation.relatedTableName, setValues, [criterionWhere]);
1996
2039
  }
1997
2040
  return;
1998
2041
  }
@@ -2092,7 +2135,6 @@ function getRelationDefinitions(contract, modelName) {
2092
2135
  function toFieldName(contract, modelName, columnName) {
2093
2136
  return getColumnToFieldMap(contract, modelName)[columnName] ?? columnName;
2094
2137
  }
2095
-
2096
2138
  //#endregion
2097
2139
  //#region src/where-interop.ts
2098
2140
  function normalizeWhereArg(arg, options) {
@@ -2105,19 +2147,28 @@ function normalizeWhereArg(arg, options) {
2105
2147
  function isToWhereExpr(arg) {
2106
2148
  return typeof arg === "object" && arg !== null && "toWhereExpr" in arg && !isWhereExpr(arg);
2107
2149
  }
2108
-
2109
2150
  //#endregion
2110
2151
  //#region src/collection.ts
2111
2152
  function applyCreateDefaults(ctx, tableName, rows) {
2153
+ const defaultValueCache = rows.length > 1 ? /* @__PURE__ */ new Map() : void 0;
2112
2154
  for (const row of rows) {
2113
2155
  const applied = ctx.context.applyMutationDefaults({
2114
2156
  op: "create",
2115
2157
  table: tableName,
2116
- values: row
2158
+ values: row,
2159
+ ...defaultValueCache ? { defaultValueCache } : {}
2117
2160
  });
2118
2161
  for (const def of applied) row[def.column] = def.value;
2119
2162
  }
2120
2163
  }
2164
+ function applyUpdateDefaults(ctx, tableName, values) {
2165
+ const applied = ctx.context.applyMutationDefaults({
2166
+ op: "update",
2167
+ table: tableName,
2168
+ values
2169
+ });
2170
+ for (const def of applied) values[def.column] = def.value;
2171
+ }
2121
2172
  function isToWhereExprInput(value) {
2122
2173
  return typeof value === "object" && value !== null && "toWhereExpr" in value && typeof value.toWhereExpr === "function";
2123
2174
  }
@@ -2480,6 +2531,7 @@ var Collection = class Collection {
2480
2531
  applyCreateDefaults(this.ctx, this.tableName, [createValues]);
2481
2532
  const updateValues = mapModelDataToStorageRow(this.contract, this.modelName, input.update);
2482
2533
  const hasUpdateValues = Object.keys(updateValues).length > 0;
2534
+ if (hasUpdateValues) applyUpdateDefaults(this.ctx, this.tableName, updateValues);
2483
2535
  const conflictColumns = resolveUpsertConflictColumns(this.contract, this.modelName, input.conflictOn);
2484
2536
  if (conflictColumns.length === 0) throw new Error(`upsert() for model "${this.modelName}" requires conflict columns`);
2485
2537
  const parentJoinColumns = this.state.includes.map((include) => include.localColumn);
@@ -2517,7 +2569,12 @@ var Collection = class Collection {
2517
2569
  const pkCriterion = buildPrimaryKeyFilterFromRow(this.contract, this.modelName, updatedRow);
2518
2570
  return this.#reloadMutationRowByPrimaryKey(pkCriterion);
2519
2571
  }
2520
- return (await this.updateAll(data))[0] ?? null;
2572
+ return withMutationScope(this.ctx.runtime, async (scope) => {
2573
+ const scoped = this.#withRuntime(scope);
2574
+ const identityWhere = await scoped.#findFirstMatchingRowIdentityWhere();
2575
+ if (!identityWhere) return null;
2576
+ return (await scoped.#clone({ filters: [identityWhere] }).updateAll(data))[0] ?? null;
2577
+ });
2521
2578
  }
2522
2579
  updateAll(data) {
2523
2580
  assertReturningCapability(this.contract, "updateAll()");
@@ -2526,6 +2583,7 @@ var Collection = class Collection {
2526
2583
  const generator = async function* () {};
2527
2584
  return new AsyncIterableResult(generator());
2528
2585
  }
2586
+ applyUpdateDefaults(this.ctx, this.tableName, mappedData);
2529
2587
  const parentJoinColumns = this.state.includes.map((include) => include.localColumn);
2530
2588
  const { selectedForQuery: selectedForUpdate, hiddenColumns } = augmentSelectionForJoinColumns(this.state.selectedFields, parentJoinColumns);
2531
2589
  const compiled = compileUpdateReturning(this.contract, this.tableName, mappedData, this.state.filters, selectedForUpdate);
@@ -2542,6 +2600,7 @@ var Collection = class Collection {
2542
2600
  async updateCount(data) {
2543
2601
  const mappedData = mapModelDataToStorageRow(this.contract, this.modelName, data);
2544
2602
  if (Object.keys(mappedData).length === 0) return 0;
2603
+ applyUpdateDefaults(this.ctx, this.tableName, mappedData);
2545
2604
  const primaryKeyColumn = resolvePrimaryKeyColumn(this.contract, this.tableName);
2546
2605
  const countState = {
2547
2606
  ...emptyState(),
@@ -2556,10 +2615,18 @@ var Collection = class Collection {
2556
2615
  }
2557
2616
  async delete() {
2558
2617
  assertReturningCapability(this.contract, "delete()");
2559
- return (await this.deleteAll().toArray())[0] ?? null;
2618
+ return withMutationScope(this.ctx.runtime, async (scope) => {
2619
+ const scoped = this.#withRuntime(scope);
2620
+ const identityWhere = await scoped.#findFirstMatchingRowIdentityWhere();
2621
+ if (!identityWhere) return null;
2622
+ return (await scoped.#clone({ filters: [identityWhere] }).#executeDeleteReturning().toArray())[0] ?? null;
2623
+ });
2560
2624
  }
2561
2625
  deleteAll() {
2562
2626
  assertReturningCapability(this.contract, "deleteAll()");
2627
+ return this.#executeDeleteReturning();
2628
+ }
2629
+ #executeDeleteReturning() {
2563
2630
  const parentJoinColumns = this.state.includes.map((include) => include.localColumn);
2564
2631
  const { selectedForQuery: selectedForDelete, hiddenColumns } = augmentSelectionForJoinColumns(this.state.selectedFields, parentJoinColumns);
2565
2632
  const compiled = compileDeleteReturning(this.contract, this.tableName, this.state.filters, selectedForDelete);
@@ -2596,6 +2663,24 @@ var Collection = class Collection {
2596
2663
  }
2597
2664
  return criterion;
2598
2665
  }
2666
+ async #findFirstMatchingRowIdentityWhere() {
2667
+ const identityColumns = resolveRowIdentityColumns(this.contract, this.tableName);
2668
+ if (identityColumns.length === 0) throw new Error(`update()/delete() on model "${this.modelName}" requires the table to have a primary key or unique constraint`);
2669
+ const firstRow = await this.#clone({
2670
+ selectedFields: [...identityColumns],
2671
+ includes: []
2672
+ }).first();
2673
+ if (!firstRow) return null;
2674
+ const columnToField = getColumnToFieldMap(this.contract, this.modelName);
2675
+ const criterion = {};
2676
+ for (const column of identityColumns) {
2677
+ const fieldName = columnToField[column] ?? column;
2678
+ const value = firstRow[fieldName];
2679
+ if (value === void 0) throw new Error(`Missing identity field "${fieldName}" while resolving single-row scope for model "${this.modelName}"`);
2680
+ criterion[fieldName] = value;
2681
+ }
2682
+ return shorthandToWhereExpr(this.ctx.context, this.modelName, criterion) ?? null;
2683
+ }
2599
2684
  async #reloadMutationRowByPrimaryKey(criterion) {
2600
2685
  return this.#reloadMutationRowByCriterion(criterion, "primary key");
2601
2686
  }
@@ -2627,6 +2712,18 @@ var Collection = class Collection {
2627
2712
  ...overrides
2628
2713
  });
2629
2714
  }
2715
+ #withRuntime(runtime) {
2716
+ const Ctor = this.constructor;
2717
+ return new Ctor({
2718
+ ...this.ctx,
2719
+ runtime
2720
+ }, this.modelName, {
2721
+ tableName: this.tableName,
2722
+ state: this.state,
2723
+ registry: this.registry,
2724
+ includeRefinementMode: this.includeRefinementMode
2725
+ });
2726
+ }
2630
2727
  #cloneWithRow(overrides) {
2631
2728
  return this.#createSelf({
2632
2729
  ...this.state,
@@ -2660,7 +2757,6 @@ var Collection = class Collection {
2660
2757
  });
2661
2758
  }
2662
2759
  };
2663
-
2664
2760
  //#endregion
2665
2761
  //#region src/orm.ts
2666
2762
  function orm(options) {
@@ -2702,7 +2798,7 @@ function isCollectionClass(value) {
2702
2798
  if (!candidate.prototype || typeof candidate.prototype !== "object") return false;
2703
2799
  return candidate.prototype instanceof Collection;
2704
2800
  }
2705
-
2706
2801
  //#endregion
2707
2802
  export { Collection, GroupedCollection, all, and, emptyState, not, or, orm };
2803
+
2708
2804
  //# sourceMappingURL=index.mjs.map