@vibeorm/runtime 2.0.0 → 2.0.2

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.js CHANGED
@@ -304,7 +304,7 @@ function getFieldMeta(params) {
304
304
  return found;
305
305
  }
306
306
  // packages/runtime/src/codecs.ts
307
- import { parsePgArrayLiteral, toPgArrayLiteral } from "@vibeorm/sql";
307
+ import { JSON_TRANSPORT, projectionOutputName, parsePgArrayLiteral, toPgArrayLiteral } from "@vibeorm/sql";
308
308
  import { VibeError as VibeError3, isEnumType as isEnumType2 } from "@vibeorm/schema";
309
309
  import { DIALECT_CODECS, decodeIsNoop } from "@vibeorm/sql";
310
310
  import {
@@ -445,16 +445,16 @@ function buildDecodePlan(params) {
445
445
  const { dialect, model, wire } = params;
446
446
  const byColumn = new Map;
447
447
  const inPlace = [];
448
- let renamed = false;
448
+ const renamedColumns = [];
449
449
  for (const field of model.fields) {
450
450
  const decode = fieldDecoder({ dialect, field, ...wire === undefined ? {} : { wire } });
451
451
  byColumn.set(field.columnName, { name: field.name, decode });
452
452
  if (field.columnName !== field.name)
453
- renamed = true;
453
+ renamedColumns.push(field.columnName);
454
454
  if (decode !== null)
455
455
  inPlace.push({ column: field.columnName, decode });
456
456
  }
457
- return { byColumn, inPlace: renamed ? null : inPlace };
457
+ return { byColumn, renamedColumns, decoders: inPlace, inPlace: renamedColumns.length > 0 ? null : inPlace };
458
458
  }
459
459
  function viewWire(wire) {
460
460
  return wire?.json === "parsed" ? { json: "parsed" } : undefined;
@@ -488,6 +488,58 @@ function decodeRowWithPlan(plan, row) {
488
488
  }
489
489
  return out;
490
490
  }
491
+ var projectionBindings = new WeakMap;
492
+ function materializeProjection(params) {
493
+ const { plan, rows } = params;
494
+ let binding = projectionBindings.get(plan);
495
+ for (let index = 0;index < rows.length; index++) {
496
+ const row = rows[index];
497
+ if (plan.renamedColumns.length > 0 && !plan.renamedColumns.some((column) => Object.hasOwn(row, column))) {
498
+ for (const entry of plan.decoders) {
499
+ const value = row[entry.column];
500
+ if (value !== null && value !== undefined)
501
+ row[entry.column] = entry.decode(value);
502
+ }
503
+ continue;
504
+ }
505
+ const keys = Object.keys(row);
506
+ let matches = binding !== undefined && keys.length === binding.keys.length;
507
+ if (matches)
508
+ for (let i = 0;i < keys.length; i++) {
509
+ if (keys[i] !== binding?.keys[i]) {
510
+ matches = false;
511
+ break;
512
+ }
513
+ }
514
+ if (binding === undefined || !matches) {
515
+ const slots = keys.map((column) => {
516
+ const entry = plan.byColumn.get(column);
517
+ return { column, name: entry?.name ?? column, decode: entry?.decode ?? null };
518
+ });
519
+ const decoders = [];
520
+ for (const slot of slots)
521
+ if (slot.decode !== null)
522
+ decoders.push({ column: slot.column, decode: slot.decode });
523
+ binding = { keys, slots, decoders, renamed: slots.some((slot) => slot.column !== slot.name) };
524
+ projectionBindings.set(plan, binding);
525
+ }
526
+ if (binding.renamed) {
527
+ const out = {};
528
+ for (const slot of binding.slots) {
529
+ const value = row[slot.column];
530
+ out[slot.name] = value === null || value === undefined || slot.decode === null ? value : slot.decode(value);
531
+ }
532
+ rows[index] = out;
533
+ } else {
534
+ for (const slot of binding.decoders) {
535
+ const value = row[slot.column];
536
+ if (value !== null && value !== undefined)
537
+ row[slot.column] = slot.decode(value);
538
+ }
539
+ }
540
+ }
541
+ return rows;
542
+ }
491
543
  function decodeRow(params) {
492
544
  const { dialect, model, row, wire } = params;
493
545
  return decodeRowWithPlan(getDecodePlan({ dialect, model, wire }), row);
@@ -500,11 +552,10 @@ function decodeRows(params) {
500
552
  function materializeRows(params) {
501
553
  const { dialect, model, rows, wire } = params;
502
554
  const plan = getDecodePlan({ dialect, model, wire });
503
- if (plan.inPlace === null) {
504
- for (let index = 0;index < rows.length; index++) {
505
- rows[index] = decodeRowWithPlan(plan, rows[index]);
506
- }
555
+ if (rows.length === 0 || plan.inPlace?.length === 0)
507
556
  return rows;
557
+ if (plan.inPlace === null || Object.keys(rows[0]).length < plan.byColumn.size) {
558
+ return materializeProjection({ plan, rows });
508
559
  }
509
560
  const decoders = plan.inPlace;
510
561
  if (decoders.length === 0)
@@ -526,39 +577,58 @@ function getJsonDecodePlan(model) {
526
577
  return plan;
527
578
  const byColumn = new Map;
528
579
  const inPlace = [];
529
- let renamed = false;
580
+ const renamedColumns = [];
530
581
  for (const field of model.fields) {
531
582
  const decode = field.scalarType === "Json" ? null : field.scalarType === "Bytes" && !field.isList ? decodeBase64Bytes : fieldDecoder({ dialect: "postgres", field });
532
583
  byColumn.set(field.columnName, { name: field.name, decode });
533
584
  if (field.columnName !== field.name)
534
- renamed = true;
585
+ renamedColumns.push(field.columnName);
535
586
  if (decode !== null)
536
587
  inPlace.push({ column: field.columnName, decode });
537
588
  }
538
- plan = { byColumn, inPlace: renamed ? null : inPlace };
589
+ plan = { byColumn, renamedColumns, decoders: inPlace, inPlace: renamedColumns.length > 0 ? null : inPlace };
539
590
  jsonDecodePlanCache.set(model, plan);
540
591
  return plan;
541
592
  }
542
- function materializeJsonRows(params) {
543
- const { model, rows } = params;
593
+ function positionalMaterializer(params) {
594
+ const { model, columns } = params;
544
595
  const plan = getJsonDecodePlan(model);
545
- if (plan.inPlace === null) {
546
- for (let index = 0;index < rows.length; index++) {
547
- rows[index] = decodeRowWithPlan(plan, rows[index]);
596
+ const slots = columns.map((column) => {
597
+ const output = projectionOutputName({ column });
598
+ const entry = plan.byColumn.get(output);
599
+ if (entry === undefined)
600
+ throw new VibeError3({ code: "VIBE_INTERNAL", message: "positional projection has no schema field", meta: { model: model.name, column: output } });
601
+ return entry;
602
+ });
603
+ const width = slots.length;
604
+ const chunkSize = JSON_TRANSPORT.chunkSize;
605
+ const chunked = width > chunkSize;
606
+ const count = Math.ceil(width / chunkSize);
607
+ const invalid = () => {
608
+ throw new VibeError3({ code: "VIBE_INTERNAL", message: "positional relation payload does not match its SQL projection", meta: { model: model.name, expectedColumns: width } });
609
+ };
610
+ if (width === 0 || new Set(slots.map((slot) => slot.name)).size !== width)
611
+ return invalid();
612
+ return (value) => {
613
+ if (!Array.isArray(value) || value.length !== (chunked ? count : width))
614
+ return invalid();
615
+ if (chunked) {
616
+ for (let index = 0;index < count; index++) {
617
+ const chunk = value[index];
618
+ if (!Array.isArray(chunk) || chunk.length !== Math.min(chunkSize, width - index * chunkSize))
619
+ return invalid();
620
+ }
548
621
  }
549
- return rows;
550
- }
551
- const decoders = plan.inPlace;
552
- if (decoders.length === 0)
553
- return rows;
554
- for (const row of rows) {
555
- for (const entry of decoders) {
556
- const value = row[entry.column];
557
- if (value !== null && value !== undefined)
558
- row[entry.column] = entry.decode(value);
622
+ const out = {};
623
+ for (let index = 0;index < width; index++) {
624
+ const slot = slots[index];
625
+ const cell = chunked ? value[Math.floor(index / chunkSize)][index % chunkSize] : value[index];
626
+ if (cell === undefined)
627
+ return invalid();
628
+ out[slot.name] = cell === null || slot.decode === null ? cell : slot.decode(cell);
559
629
  }
560
- }
561
- return rows;
630
+ return out;
631
+ };
562
632
  }
563
633
  function decodeAggregateValue(params) {
564
634
  const { dialect, key, field, value } = params;
@@ -576,8 +646,73 @@ function decodeAggregateValue(params) {
576
646
  return String(value);
577
647
  return field === null ? value : decodeValue({ dialect, field, value });
578
648
  }
649
+ // packages/runtime/src/lateral-projection.ts
650
+ import { VibeError as VibeError4, unsupportedCapability } from "@vibeorm/schema";
651
+ function isPlainObject(value) {
652
+ return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
653
+ }
654
+ var LATERAL_CHILD_ALIAS = "__rel";
655
+ var projections = new WeakMap;
656
+ function lateralProjection(params) {
657
+ const { node } = params;
658
+ const cached = projections.get(node);
659
+ if (cached !== undefined)
660
+ return cached;
661
+ const model = node.link.target;
662
+ const hiddenOrder = lateralHiddenOrderFields({ node });
663
+ const names = node.args.projected === null ? model.fields.map((field) => field.name) : [...node.args.projected, ...hiddenOrder];
664
+ const columns = names.map((name) => {
665
+ const field = model.fieldByName.get(name);
666
+ if (field === undefined)
667
+ throw new VibeError4({ code: "VIBE_INTERNAL", message: "lateral projection references an unknown field", meta: { model: model.name, field: name } });
668
+ return Object.freeze(applyLateralTransform({ model, column: { table: LATERAL_CHILD_ALIAS, name: field.columnName } }));
669
+ });
670
+ const descriptor = Object.freeze({ columns: Object.freeze(columns), hiddenOrder: Object.freeze(hiddenOrder) });
671
+ projections.set(node, descriptor);
672
+ return descriptor;
673
+ }
674
+ function lateralHiddenOrderFields(params) {
675
+ const { node } = params;
676
+ const projected = node.args.projected;
677
+ if (projected === null || node.args.orderBy === undefined)
678
+ return [];
679
+ const target = node.link.target;
680
+ const hidden = [];
681
+ const items = Array.isArray(node.args.orderBy) ? node.args.orderBy : [node.args.orderBy];
682
+ for (const item of items) {
683
+ if (!isPlainObject(item))
684
+ continue;
685
+ for (const [name, value] of Object.entries(item)) {
686
+ if (value === undefined)
687
+ continue;
688
+ if (!target.fieldByName.has(name))
689
+ continue;
690
+ if (!projected.includes(name) && !hidden.includes(name))
691
+ hidden.push(name);
692
+ }
693
+ }
694
+ return hidden;
695
+ }
696
+ function applyLateralTransform(params) {
697
+ const { model, column } = params;
698
+ const field = model.fieldByColumn.get(column.name);
699
+ if (field === undefined)
700
+ return column;
701
+ const scalar = field.scalarType;
702
+ if (scalar !== "BigInt" && scalar !== "Decimal" && scalar !== "Bytes")
703
+ return column;
704
+ if (field.isList) {
705
+ throw unsupportedCapability({
706
+ dialect: "postgres",
707
+ feature: `join-strategy relation loading of ${model.name}.${field.name} (${scalar}[])`,
708
+ hint: 'list columns of BigInt/Decimal/Bytes have no faithful JSON wire form \u2014 use the portable "query" relation strategy'
709
+ });
710
+ }
711
+ return { ...column, transform: scalar === "Bytes" ? "encodeBase64" : "castText" };
712
+ }
713
+
579
714
  // packages/runtime/src/query-builder.ts
580
- import { VibeError as VibeError7, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
715
+ import { VibeError as VibeError8, unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
581
716
  import {
582
717
  SQL_DEFAULT_CELL,
583
718
  containsPattern,
@@ -590,9 +725,9 @@ import {
590
725
  } from "@vibeorm/sql";
591
726
 
592
727
  // packages/runtime/src/bulk-upsert.ts
593
- import { VibeError as VibeError4, unsupportedCapability } from "@vibeorm/schema";
728
+ import { VibeError as VibeError5, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
594
729
  function validationError(params) {
595
- return new VibeError4({
730
+ return new VibeError5({
596
731
  code: "VIBE_VALIDATION",
597
732
  message: params.message,
598
733
  ...params.meta === undefined ? {} : { meta: params.meta }
@@ -680,7 +815,7 @@ function assertConflictTargetIsEnforceable(params) {
680
815
  const groups = uniqueGroupSignatures({ model });
681
816
  if (groups.size <= 1)
682
817
  return;
683
- throw unsupportedCapability({
818
+ throw unsupportedCapability2({
684
819
  dialect: dialect.name,
685
820
  feature: `an enforced conflict target on ${model.clientName}.upsertMany() (${model.name} declares ${groups.size} unique groups)`,
686
821
  hint: "mysql's ON DUPLICATE KEY UPDATE fires on ANY unique index, so another one can decide the conflict \u2014 pass allowAnyUniqueConflict: true to accept that, or gate the call per dialect"
@@ -803,7 +938,7 @@ function assertNoDuplicateSecondaryUniques(params) {
803
938
  const key = conflictKeyOf({ row, columns });
804
939
  const first = firstIndexByKey.get(key);
805
940
  if (first !== undefined) {
806
- throw unsupportedCapability({
941
+ throw unsupportedCapability2({
807
942
  dialect: dialect.name,
808
943
  feature: `${model.clientName}.upsertMany() with rows ${first} and ${index} carrying the same ${fields.map((field) => field.name).join(" + ")}`,
809
944
  hint: `ON DUPLICATE KEY UPDATE has no conflict target: it resolves through whichever unique index an incoming row matches, so row ${index} would UPDATE row ${first} through unique (${columns.join(", ")}) and one of the two rows you submitted would silently not exist. Deduplicate the feed on those columns as well as on the conflict target, or submit the colliding rows in separate calls. allowAnyUniqueConflict widens which STORED row a single incoming row may replace; it does not authorise two submitted rows collapsing into one`
@@ -885,7 +1020,7 @@ function assertConflictKeysAreComparable(params) {
885
1020
  if (target.kind === "constraint") {
886
1021
  if (engineComparesEveryPair)
887
1022
  return;
888
- throw unsupportedCapability({
1023
+ throw unsupportedCapability2({
889
1024
  dialect: dialect.name,
890
1025
  feature: `a ${statementCount > 1 ? "chunked " : ""}${model.clientName}.upsertMany() batch arbitrated by constraint "${target.constraint}"`,
891
1026
  hint: statementCount > 1 ? `a named constraint hides which columns decide the conflict, so only the engine can tell whether two incoming rows address the same stored row \u2014 and it only compares rows inside ONE statement, while this batch of ${model.name} rows needs ${statementCount}. Submit fewer rows per call, or name the unique group's fields instead of the constraint` : "a named constraint hides which columns decide the conflict, so this dialect would silently keep the last of two rows that address the same stored row"
@@ -903,7 +1038,7 @@ function assertConflictKeysAreComparable(params) {
903
1038
  notes.push(`@collation("${field.collation}")`);
904
1039
  return notes.length === 0 ? `${field.name} (column "${field.columnName}")` : `${field.name} (column "${field.columnName}", ${notes.join(", ")})`;
905
1040
  }).join(", ");
906
- throw unsupportedCapability({
1041
+ throw unsupportedCapability2({
907
1042
  dialect: dialect.name,
908
1043
  feature: `${model.clientName}.upsertMany() on conflict target ${described}`,
909
1044
  hint: statementCount > 1 ? `only the database can tell whether two of these keys address the same stored row \u2014 its column type, collation, scale or precision decides \u2014 and it only compares rows inside ONE statement, while this batch of ${model.name} rows needs ${statementCount}. Submit fewer rows per call, or ${remedyFor({ dialect, fields: opaque })}` : `only the database can tell whether two of these keys address the same stored row \u2014 its column type, collation, scale or precision decides \u2014 and this dialect answers a doubled key by silently keeping the last row instead of refusing it. Deduplicate the feed against the same rule the column uses, or ${remedyFor({ dialect, fields: opaque })}`
@@ -911,7 +1046,7 @@ function assertConflictKeysAreComparable(params) {
911
1046
  }
912
1047
 
913
1048
  // packages/runtime/src/keyset.ts
914
- import { VibeError as VibeError5 } from "@vibeorm/schema";
1049
+ import { VibeError as VibeError6 } from "@vibeorm/schema";
915
1050
  var KEYSET_TOKEN_VERSION = 1;
916
1051
  var KEYSET_TOKEN_PREFIX = "vk1.";
917
1052
  var KEYSET_MAX_PAGE_SIZE = 1e4;
@@ -921,10 +1056,10 @@ var DATETIME_NEEDS_PRECISION_ASSERTION = Object.freeze({
921
1056
  sqlite: false
922
1057
  });
923
1058
  function keysetValidation(params) {
924
- return new VibeError5({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1059
+ return new VibeError6({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
925
1060
  }
926
1061
  function keysetUnsupported(params) {
927
- return new VibeError5({
1062
+ return new VibeError6({
928
1063
  code: "VIBE_UNSUPPORTED_CAPABILITY",
929
1064
  message: params.message,
930
1065
  meta: params.meta
@@ -1307,11 +1442,11 @@ function decodeKeysetToken(params) {
1307
1442
  }
1308
1443
 
1309
1444
  // packages/runtime/src/relation-plan.ts
1310
- import { VibeError as VibeError6, implicitJoinTable } from "@vibeorm/schema";
1445
+ import { VibeError as VibeError7, implicitJoinTable } from "@vibeorm/schema";
1311
1446
  function validationError2(params) {
1312
- return new VibeError6({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1447
+ return new VibeError7({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1313
1448
  }
1314
- function isPlainObject(value) {
1449
+ function isPlainObject2(value) {
1315
1450
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
1316
1451
  }
1317
1452
  function relationKeyOf(relation) {
@@ -1334,7 +1469,7 @@ function keyFields(params) {
1334
1469
  return names.map((name) => {
1335
1470
  const field = model.fieldByName.get(name);
1336
1471
  if (field === undefined) {
1337
- throw new VibeError6({
1472
+ throw new VibeError7({
1338
1473
  code: "VIBE_SCHEMA_INVALID",
1339
1474
  message: `relation "${relation.name}" on ${relation.model} names unknown ${what} field "${name}"`,
1340
1475
  meta: { model: relation.model, relation: relation.name }
@@ -1346,7 +1481,7 @@ function keyFields(params) {
1346
1481
  function singleKeyPair(params) {
1347
1482
  const { link, what } = params;
1348
1483
  if (link.parentKeys.length !== 1 || link.childKeys.length !== 1) {
1349
- throw new VibeError6({
1484
+ throw new VibeError7({
1350
1485
  code: "VIBE_UNSUPPORTED_CAPABILITY",
1351
1486
  message: `${what} on relation "${link.relation.name}" of ${link.relation.model} needs a single-column relation key \u2014 this relation links ${link.parentKeys.length} columns; use an explicit junction model`,
1352
1487
  meta: { model: link.relation.model, relation: link.relation.name, arity: link.parentKeys.length }
@@ -1385,7 +1520,7 @@ function resolveRelationLinkUncached(params) {
1385
1520
  const target = getModelMeta({ meta, model: relation.targetModel });
1386
1521
  if (relation.type === "manyToMany") {
1387
1522
  if (relation.model === relation.targetModel) {
1388
- throw new VibeError6({
1523
+ throw new VibeError7({
1389
1524
  code: "VIBE_SCHEMA_INVALID",
1390
1525
  message: `relation "${relation.name}" on ${relation.model} is an implicit self many-to-many: both sides own the ` + `same join-table column, so this field and its inverse would read the same rows. Model each direction ` + `with an explicit junction model.`,
1391
1526
  meta: { model: relation.model, relation: relation.name, targetModel: relation.targetModel }
@@ -1427,7 +1562,7 @@ function resolveRelationLinkUncached(params) {
1427
1562
  }
1428
1563
  const reverse = findReverseRelation({ relation, target });
1429
1564
  if (reverse === null || reverse.fields === undefined || reverse.fields.length === 0) {
1430
- throw new VibeError6({
1565
+ throw new VibeError7({
1431
1566
  code: "VIBE_SCHEMA_INVALID",
1432
1567
  message: `relation "${relation.name}" on ${relation.model} has no foreign-key counterpart on ${relation.targetModel}`,
1433
1568
  meta: { model: relation.model, relation: relation.name, target: relation.targetModel }
@@ -1452,7 +1587,7 @@ function pairedKeys(params) {
1452
1587
  const parentKeys = keyFields({ model, names: parentNames, relation, what: "fields" });
1453
1588
  const childKeys = keyFields({ model: target, names: childNames, relation, what: "references" });
1454
1589
  if (parentKeys.length !== childKeys.length) {
1455
- throw new VibeError6({
1590
+ throw new VibeError7({
1456
1591
  code: "VIBE_SCHEMA_INVALID",
1457
1592
  message: `relation "${relation.name}" on ${relation.model} links ${parentKeys.length} field(s) to ${childKeys.length} reference(s) \u2014 a foreign key must pair every column`,
1458
1593
  meta: { model: relation.model, relation: relation.name }
@@ -1508,7 +1643,7 @@ function compileChildSelect(params) {
1508
1643
  }
1509
1644
  function compileChildOmit(params) {
1510
1645
  const { model, omit, relation } = params;
1511
- if (!isPlainObject(omit)) {
1646
+ if (!isPlainObject2(omit)) {
1512
1647
  throw validationError2({
1513
1648
  message: `omit on relation "${relation}" must be an object`,
1514
1649
  meta: { model: model.name, relation }
@@ -1586,7 +1721,7 @@ function resolveCounts(params) {
1586
1721
  if (spec === true) {
1587
1722
  return model.relations.filter((relation) => relation.isList).map((relation) => resolveCountNode({ meta, model, name: relation.name, where: undefined }));
1588
1723
  }
1589
- if (!isPlainObject(spec) || !isPlainObject(spec.select)) {
1724
+ if (!isPlainObject2(spec) || !isPlainObject2(spec.select)) {
1590
1725
  throw validationError2({
1591
1726
  message: `_count on model "${model.name}" must be true or { select: { relation: true } }`,
1592
1727
  meta: { model: model.name }
@@ -1600,7 +1735,7 @@ function resolveCounts(params) {
1600
1735
  nodes.push(resolveCountNode({ meta, model, name, where: undefined }));
1601
1736
  continue;
1602
1737
  }
1603
- if (!isPlainObject(value)) {
1738
+ if (!isPlainObject2(value)) {
1604
1739
  throw validationError2({
1605
1740
  message: `_count.select.${name} on model "${model.name}" must be true, false, or { where: \u2026 }`,
1606
1741
  meta: { model: model.name, relation: name }
@@ -1617,7 +1752,7 @@ function resolveCounts(params) {
1617
1752
  }
1618
1753
  }
1619
1754
  const where = value.where;
1620
- if (where !== undefined && !isPlainObject(where)) {
1755
+ if (where !== undefined && !isPlainObject2(where)) {
1621
1756
  throw validationError2({
1622
1757
  message: `_count.select.${name}.where on model "${model.name}" must be an object`,
1623
1758
  meta: { model: model.name, relation: name }
@@ -1657,7 +1792,7 @@ function resolveNode(params) {
1657
1792
  });
1658
1793
  }
1659
1794
  const where = raw.where === undefined ? undefined : (() => {
1660
- if (!isPlainObject(raw.where)) {
1795
+ if (!isPlainObject2(raw.where)) {
1661
1796
  throw validationError2({
1662
1797
  message: `where on relation "${relation.name}" must be an object`,
1663
1798
  meta: { model: model.name, relation: relation.name }
@@ -1720,8 +1855,8 @@ function resolveNode(params) {
1720
1855
  }
1721
1856
  function resolveRelationSelection(params) {
1722
1857
  const { meta, model, select, include } = params;
1723
- const selectSpec = isPlainObject(select) ? select : null;
1724
- const includeSpec = isPlainObject(include) ? include : null;
1858
+ const selectSpec = isPlainObject2(select) ? select : null;
1859
+ const includeSpec = isPlainObject2(include) ? include : null;
1725
1860
  if (includeSpec !== null) {
1726
1861
  for (const [key, value] of Object.entries(includeSpec)) {
1727
1862
  if (value === undefined || value === false)
@@ -1735,7 +1870,7 @@ function resolveRelationSelection(params) {
1735
1870
  meta: { model: model.name, relation: key }
1736
1871
  });
1737
1872
  }
1738
- if (value !== true && !isPlainObject(value)) {
1873
+ if (value !== true && !isPlainObject2(value)) {
1739
1874
  throw validationError2({
1740
1875
  message: `include.${key} on model "${model.name}" must be true or an object`,
1741
1876
  meta: { model: model.name, relation: key }
@@ -1747,14 +1882,14 @@ function resolveRelationSelection(params) {
1747
1882
  if (selectSpec !== null) {
1748
1883
  for (const relation of model.relations) {
1749
1884
  const value = selectSpec[relation.name];
1750
- if (value === true || isPlainObject(value))
1885
+ if (value === true || isPlainObject2(value))
1751
1886
  specs.set(relation.name, value);
1752
1887
  }
1753
1888
  }
1754
1889
  if (includeSpec !== null) {
1755
1890
  for (const relation of model.relations) {
1756
1891
  const value = includeSpec[relation.name];
1757
- if (value === true || isPlainObject(value))
1892
+ if (value === true || isPlainObject2(value))
1758
1893
  specs.set(relation.name, value);
1759
1894
  }
1760
1895
  }
@@ -1802,9 +1937,9 @@ var AGG_FN_BY_KEY = {
1802
1937
  _max: "max"
1803
1938
  };
1804
1939
  function validationError3(params) {
1805
- return new VibeError7({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1940
+ return new VibeError8({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1806
1941
  }
1807
- function isPlainObject2(value) {
1942
+ function isPlainObject3(value) {
1808
1943
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
1809
1944
  }
1810
1945
  function isDirectValue(value) {
@@ -1855,7 +1990,7 @@ function compileLock(params) {
1855
1990
  function compileConflictTarget(params) {
1856
1991
  const { dialect, meta, model, method, target, where } = params;
1857
1992
  if ((target !== undefined || where !== undefined) && dialect.doNothingForm === "insertIgnore") {
1858
- throw unsupportedCapability2({
1993
+ throw unsupportedCapability3({
1859
1994
  dialect: dialect.name,
1860
1995
  feature: `a conflict target on ${model.clientName}.${method}()`,
1861
1996
  hint: "mysql's INSERT IGNORE / ON DUPLICATE KEY fire on ANY duplicate key \u2014 no target can be named; gate the call per dialect"
@@ -1871,9 +2006,9 @@ function compileConflictTarget(params) {
1871
2006
  });
1872
2007
  }
1873
2008
  out.columns = target.map((name) => getFieldMeta({ model, field: String(name), context: "onConflict target" }).columnName);
1874
- } else if (isPlainObject2(target) && typeof target.constraint === "string" && target.constraint.length > 0 && Object.keys(target).length === 1) {
2009
+ } else if (isPlainObject3(target) && typeof target.constraint === "string" && target.constraint.length > 0 && Object.keys(target).length === 1) {
1875
2010
  if (!dialect.capabilities.conflictTargetConstraint) {
1876
- throw unsupportedCapability2({
2011
+ throw unsupportedCapability3({
1877
2012
  dialect: dialect.name,
1878
2013
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
1879
2014
  hint: "only postgres supports ON CONSTRAINT targets \u2014 name the indexed columns instead"
@@ -1895,7 +2030,7 @@ function compileConflictTarget(params) {
1895
2030
  });
1896
2031
  }
1897
2032
  if (!dialect.capabilities.partialIndexes) {
1898
- throw unsupportedCapability2({
2033
+ throw unsupportedCapability3({
1899
2034
  dialect: dialect.name,
1900
2035
  feature: "a partial-index conflict target",
1901
2036
  hint: "this dialect has no partial indexes to target"
@@ -1939,7 +2074,7 @@ function compileDbNowFilter(params) {
1939
2074
  }
1940
2075
  function requireObject(params) {
1941
2076
  const { value, what, model } = params;
1942
- if (!isPlainObject2(value)) {
2077
+ if (!isPlainObject3(value)) {
1943
2078
  throw validationError3({
1944
2079
  message: `${what} must be an object on model "${model.name}"`,
1945
2080
  meta: { model: model.name, argument: what }
@@ -1981,7 +2116,7 @@ function assertListFilterable(params) {
1981
2116
  const { dialect, model, field, operator } = params;
1982
2117
  if (dialect.capabilities.scalarArrays)
1983
2118
  return;
1984
- throw unsupportedCapability2({
2119
+ throw unsupportedCapability3({
1985
2120
  dialect: dialect.name,
1986
2121
  feature: `scalar list filter "${operator}" on ${model.name}.${field.name}`,
1987
2122
  hint: "only postgres has native array columns \u2014 model the list as a related table instead"
@@ -1991,7 +2126,7 @@ function assertListWritable(params) {
1991
2126
  const { dialect, model, field } = params;
1992
2127
  if (!field.isList || dialect.capabilities.scalarArrays)
1993
2128
  return;
1994
- throw unsupportedCapability2({
2129
+ throw unsupportedCapability3({
1995
2130
  dialect: dialect.name,
1996
2131
  feature: `writing the scalar list ${model.name}.${field.name}`,
1997
2132
  hint: "only postgres has native array columns \u2014 model the list as a related table instead"
@@ -2108,7 +2243,7 @@ function compileJsonFieldFilter(params) {
2108
2243
  const requireOp = (op) => {
2109
2244
  if (dialect.supportedJsonOps.includes(op))
2110
2245
  return;
2111
- throw unsupportedCapability2({
2246
+ throw unsupportedCapability3({
2112
2247
  dialect: dialect.name,
2113
2248
  feature: `json filter "${op}" on ${model.name}.${field.name}`,
2114
2249
  hint: `this dialect can express: ${dialect.supportedJsonOps.join(", ")}`
@@ -2268,7 +2403,7 @@ function compileOperatorFilter(params) {
2268
2403
  });
2269
2404
  break;
2270
2405
  }
2271
- if (isPlainObject2(operand)) {
2406
+ if (isPlainObject3(operand)) {
2272
2407
  conditions.push({
2273
2408
  kind: "not",
2274
2409
  operand: compileOperatorFilter({ ctx, model, field, filter: operand, qualify })
@@ -2385,7 +2520,7 @@ function compileFieldFilter(params) {
2385
2520
  assertListFilterable({ dialect, model, field, operator: "equals" });
2386
2521
  return { kind: "cmp", op: "eq", column, value: encodeValue({ dialect: dialect.name, field, value }) };
2387
2522
  }
2388
- if (isPlainObject2(value)) {
2523
+ if (isPlainObject3(value)) {
2389
2524
  return compileOperatorFilter({ ctx, model, field, filter: value, qualify });
2390
2525
  }
2391
2526
  throw validationError3({
@@ -2471,7 +2606,7 @@ function buildRelationExists(params) {
2471
2606
  }
2472
2607
  function requirePredicateObject(params) {
2473
2608
  const { model, relation, operator, operand } = params;
2474
- if (!isPlainObject2(operand)) {
2609
+ if (!isPlainObject3(operand)) {
2475
2610
  throw validationError3({
2476
2611
  message: `"${operator}" on relation ${model.name}.${relation.name} expects a where object`,
2477
2612
  meta: { model: model.name, relation: relation.name, operator }
@@ -2484,7 +2619,7 @@ function compileRelationFilter(params) {
2484
2619
  const link = resolveRelationLink({ meta: ctx.meta, model, relation });
2485
2620
  const outer = qualify ?? model.tableName;
2486
2621
  if (relation.isList) {
2487
- if (!isPlainObject2(value)) {
2622
+ if (!isPlainObject3(value)) {
2488
2623
  throw validationError3({
2489
2624
  message: `filter on relation ${model.name}.${relation.name} must be { some | every | none: \u2026 }`,
2490
2625
  meta: { model: model.name, relation: relation.name }
@@ -2546,7 +2681,7 @@ function compileRelationFilter(params) {
2546
2681
  if (value === null) {
2547
2682
  isArm = null;
2548
2683
  hasIs = true;
2549
- } else if (isPlainObject2(value) && (("is" in value) || ("isNot" in value))) {
2684
+ } else if (isPlainObject3(value) && (("is" in value) || ("isNot" in value))) {
2550
2685
  for (const key of Object.keys(value)) {
2551
2686
  if (value[key] === undefined)
2552
2687
  continue;
@@ -2563,7 +2698,7 @@ function compileRelationFilter(params) {
2563
2698
  });
2564
2699
  }
2565
2700
  }
2566
- } else if (isPlainObject2(value)) {
2701
+ } else if (isPlainObject3(value)) {
2567
2702
  isArm = value;
2568
2703
  hasIs = true;
2569
2704
  } else {
@@ -2659,7 +2794,7 @@ function compileOrderTerm(params) {
2659
2794
  const column = dialect !== undefined ? compareColumnRef({ dialect, field, ...qualify === undefined ? {} : { table: qualify } }) : columnRef({ field, ...qualify === undefined ? {} : { table: qualify } });
2660
2795
  if (spec === "asc" || spec === "desc")
2661
2796
  return { column, direction: spec };
2662
- if (isPlainObject2(spec)) {
2797
+ if (isPlainObject3(spec)) {
2663
2798
  const { sort, nulls } = spec;
2664
2799
  if (sort !== "asc" && sort !== "desc") {
2665
2800
  throw validationError3({
@@ -2831,7 +2966,7 @@ function resolveComputedSelection(params) {
2831
2966
  const specs = model.computed;
2832
2967
  if (specs === null || aggregating)
2833
2968
  return null;
2834
- if (isPlainObject2(args.select)) {
2969
+ if (isPlainObject3(args.select)) {
2835
2970
  const names = [];
2836
2971
  for (const [name, value] of Object.entries(args.select)) {
2837
2972
  if (!specs.has(name) || value === undefined || value === false)
@@ -2846,7 +2981,7 @@ function resolveComputedSelection(params) {
2846
2981
  }
2847
2982
  return names.length === 0 ? null : names;
2848
2983
  }
2849
- if (isPlainObject2(args.omit)) {
2984
+ if (isPlainObject3(args.omit)) {
2850
2985
  const names = [];
2851
2986
  for (const name of specs.keys()) {
2852
2987
  const value = args.omit[name];
@@ -3154,7 +3289,7 @@ function compileUpdateSet(params) {
3154
3289
  meta: { model: model.name, field: field.name }
3155
3290
  });
3156
3291
  }
3157
- if (isPlainObject2(value) && field.scalarType !== "Json") {
3292
+ if (isPlainObject3(value) && field.scalarType !== "Json") {
3158
3293
  const entries = Object.entries(value).filter(([, operand2]) => operand2 !== undefined);
3159
3294
  if (entries.length !== 1) {
3160
3295
  throw validationError3({
@@ -3316,7 +3451,7 @@ function parseCursor(params) {
3316
3451
  function keysetFieldOfTerm(params) {
3317
3452
  const { dialect, model, term } = params;
3318
3453
  if (!("column" in term)) {
3319
- throw unsupportedCapability2({
3454
+ throw unsupportedCapability3({
3320
3455
  dialect: dialect.name,
3321
3456
  feature: `keyset pagination over a relation or aggregate orderBy on model "${model.name}"`,
3322
3457
  hint: "order by scalar columns plus a unique tie-breaker; a correlated subquery value is not on the row, so no position can describe it"
@@ -3324,7 +3459,7 @@ function keysetFieldOfTerm(params) {
3324
3459
  }
3325
3460
  const field = model.fieldByColumn.get(term.column.name);
3326
3461
  if (field === undefined) {
3327
- throw unsupportedCapability2({
3462
+ throw unsupportedCapability3({
3328
3463
  dialect: dialect.name,
3329
3464
  feature: `keyset pagination over a computed order key on model "${model.name}"`,
3330
3465
  hint: "order by scalar columns plus a unique tie-breaker"
@@ -3470,7 +3605,7 @@ function orderByScalarFields(params) {
3470
3605
  visit(entry);
3471
3606
  return;
3472
3607
  }
3473
- if (!isPlainObject2(value))
3608
+ if (!isPlainObject3(value))
3474
3609
  return;
3475
3610
  for (const [key, spec] of Object.entries(value)) {
3476
3611
  if (spec === undefined)
@@ -3667,7 +3802,10 @@ function buildFindShape(params) {
3667
3802
  const absTake = take === undefined ? undefined : Math.abs(take);
3668
3803
  const limit = firstLimit === undefined ? absTake : absTake === undefined ? firstLimit : Math.min(firstLimit, absTake);
3669
3804
  const offset = skip;
3670
- const lateralJoins = useLateral ? selection.relations.map((node, index) => buildLateralJoinClause({ dialect, meta, model, node, index })) : [];
3805
+ const lateralJoins = useLateral ? [
3806
+ ...selection.relations.map((node, index) => buildLateralJoinClause({ dialect, meta, model, node, index })),
3807
+ ...selection.counts.map((node, index) => buildLateralCount({ dialect, meta, model, node, index }))
3808
+ ] : [];
3671
3809
  const projected = projectionColumns({
3672
3810
  model,
3673
3811
  selectedFields,
@@ -3686,6 +3824,15 @@ function buildFindShape(params) {
3686
3824
  }
3687
3825
  return out;
3688
3826
  };
3827
+ const withKeyColumns = (inner, keys) => {
3828
+ const out = [...inner];
3829
+ for (const field of keys) {
3830
+ if (out.some((column) => column.name === field.columnName))
3831
+ continue;
3832
+ out.push(columnRef({ field, ...qualify === undefined ? {} : { table: qualify } }));
3833
+ }
3834
+ return out;
3835
+ };
3689
3836
  if (emulateDistinct) {
3690
3837
  const arranged = orderTerms;
3691
3838
  const windowOrder = arranged.slice(distinctLeadCount);
@@ -3738,6 +3885,30 @@ function buildFindShape(params) {
3738
3885
  };
3739
3886
  return { statement: statement2, reverseRows: backward, resortBy };
3740
3887
  }
3888
+ const pageFirst = lateralJoins.length > 0 && (limit !== undefined || offset !== undefined) && orderTerms.every((term) => ("column" in term));
3889
+ if (pageFirst && selection !== undefined) {
3890
+ const correlationKeys = [...selection.relations, ...selection.counts].flatMap((node) => node.link.parentKeys);
3891
+ const innerColumns = withOuterOrderKeys(withKeyColumns(projected, correlationKeys), orderTerms);
3892
+ const inner = {
3893
+ kind: "select",
3894
+ table: { name: model.tableName },
3895
+ columns: innerColumns,
3896
+ ...where === undefined ? {} : { where },
3897
+ ...distinctOn === undefined ? {} : { distinctOn },
3898
+ ...orderTerms.length === 0 ? {} : { orderBy: orderTerms },
3899
+ ...limit === undefined ? {} : { limit },
3900
+ ...offset === undefined ? {} : { offset }
3901
+ };
3902
+ const statement2 = {
3903
+ kind: "select",
3904
+ table: { name: model.tableName },
3905
+ columns,
3906
+ fromSelect: inner,
3907
+ lateralJoins,
3908
+ ...orderTerms.length === 0 ? {} : { orderBy: orderTerms }
3909
+ };
3910
+ return { statement: statement2, reverseRows: backward, resortBy };
3911
+ }
3741
3912
  const statement = {
3742
3913
  kind: "select",
3743
3914
  table: { name: model.tableName },
@@ -3751,73 +3922,66 @@ function buildFindShape(params) {
3751
3922
  };
3752
3923
  return { statement, reverseRows: backward, resortBy };
3753
3924
  }
3754
- var LATERAL_CHILD_ALIAS = "__rel";
3755
3925
  var DISTINCT_TABLE_ALIAS = "__dist";
3756
3926
  var DISTINCT_ROW_NUMBER_ALIAS = "__rn";
3757
3927
  function lateralColumnAlias(params) {
3758
3928
  return `__rel_${params.relation}`;
3759
3929
  }
3760
- function lateralHiddenOrderFields(params) {
3761
- const { node } = params;
3762
- const projected = node.args.projected;
3763
- if (projected === null || node.args.orderBy === undefined)
3764
- return [];
3765
- const target = node.link.target;
3766
- const hidden = [];
3767
- const items = Array.isArray(node.args.orderBy) ? node.args.orderBy : [node.args.orderBy];
3768
- for (const item of items) {
3769
- if (!isPlainObject2(item))
3770
- continue;
3771
- for (const [name, value] of Object.entries(item)) {
3772
- if (value === undefined)
3773
- continue;
3774
- if (!target.fieldByName.has(name))
3775
- continue;
3776
- if (!projected.includes(name) && !hidden.includes(name))
3777
- hidden.push(name);
3778
- }
3779
- }
3780
- return hidden;
3930
+ function lateralCountAlias(params) {
3931
+ return `__vibe_count_${params.index}`;
3781
3932
  }
3782
- var lateralFieldByColumn = new WeakMap;
3783
- function fieldByColumnName(model) {
3784
- let map = lateralFieldByColumn.get(model);
3785
- if (map === undefined) {
3786
- map = new Map(model.fields.map((field) => [field.columnName, field]));
3787
- lateralFieldByColumn.set(model, map);
3788
- }
3789
- return map;
3790
- }
3791
- function applyLateralTransform(params) {
3792
- const { model, column } = params;
3793
- const field = fieldByColumnName(model).get(column.name);
3794
- if (field === undefined)
3795
- return column;
3796
- const scalar = field.scalarType;
3797
- if (scalar !== "BigInt" && scalar !== "Decimal" && scalar !== "Bytes")
3798
- return column;
3799
- if (field.isList) {
3800
- throw unsupportedCapability2({
3801
- dialect: "postgres",
3802
- feature: `join-strategy relation loading of ${model.name}.${field.name} (${scalar}[])`,
3803
- hint: 'list columns of BigInt/Decimal/Bytes have no faithful JSON wire form \u2014 use the portable "query" relation strategy'
3804
- });
3933
+ function buildLateralCount(params) {
3934
+ const { dialect, meta, model, node, index } = params;
3935
+ const { link } = node;
3936
+ const childAlias = "__count_child";
3937
+ const joinAlias = "__count_join";
3938
+ const columnAlias = lateralCountAlias({ index });
3939
+ let select;
3940
+ const filter = node.where === undefined ? undefined : compileWhere({
3941
+ dialect,
3942
+ meta,
3943
+ model: link.target,
3944
+ where: node.where,
3945
+ qualify: childAlias
3946
+ });
3947
+ if (link.kind === "manyToMany") {
3948
+ const join = link.join;
3949
+ const pair = singleKeyPair({ link, what: "implicit many-to-many relation count" });
3950
+ const correlation = {
3951
+ kind: "cmpCol",
3952
+ op: "eq",
3953
+ left: { table: joinAlias, name: join.parentColumn },
3954
+ right: { table: model.tableName, name: pair.parent.columnName }
3955
+ };
3956
+ select = {
3957
+ kind: "select",
3958
+ table: filter === undefined ? { name: join.tableName, alias: joinAlias } : { name: link.target.tableName, alias: childAlias },
3959
+ columns: [],
3960
+ selectExpr: { kind: "countAll", alias: columnAlias },
3961
+ where: filter === undefined ? correlation : { kind: "and", operands: [correlation, filter] },
3962
+ ...filter === undefined ? {} : { innerJoins: [{
3963
+ table: { name: join.tableName, alias: joinAlias },
3964
+ on: [{ left: { table: joinAlias, name: join.childColumn }, right: { table: childAlias, name: pair.child.columnName } }]
3965
+ }] }
3966
+ };
3967
+ } else {
3968
+ const correlation = correlateLink({ link, childTable: childAlias, outerTable: model.tableName });
3969
+ select = {
3970
+ kind: "select",
3971
+ table: { name: link.target.tableName, alias: childAlias },
3972
+ columns: [],
3973
+ selectExpr: { kind: "countAll", alias: columnAlias },
3974
+ where: filter === undefined ? correlation : { kind: "and", operands: [correlation, filter] }
3975
+ };
3805
3976
  }
3806
- return { ...column, transform: scalar === "Bytes" ? "encodeBase64" : "castText" };
3977
+ return { alias: `__count_${index}`, columnAlias, agg: "count", select };
3807
3978
  }
3808
3979
  function buildLateralJoinClause(params) {
3809
3980
  const { dialect, meta, model, node, index } = params;
3810
3981
  const link = node.link;
3811
3982
  const target = link.target;
3812
3983
  const aliasSeq = { n: 0 };
3813
- const projected = node.args.projected;
3814
- const hiddenOrder = lateralHiddenOrderFields({ node });
3815
- const selectedWithOrder = projected === null || hiddenOrder.length === 0 ? projected : [...projected, ...hiddenOrder];
3816
- const columns = projectionColumns({
3817
- model: target,
3818
- selectedFields: selectedWithOrder,
3819
- table: LATERAL_CHILD_ALIAS
3820
- }).map((column) => applyLateralTransform({ model: target, column }));
3984
+ const { columns } = lateralProjection({ node });
3821
3985
  let correlate;
3822
3986
  if (link.kind === "manyToMany") {
3823
3987
  const join = link.join;
@@ -3876,6 +4040,7 @@ function buildLateralJoinClause(params) {
3876
4040
  alias: `__lat_${index}`,
3877
4041
  columnAlias: lateralColumnAlias({ relation: node.name }),
3878
4042
  agg: isList ? "jsonArray" : "jsonObject",
4043
+ rowFormat: "positional",
3879
4044
  select: {
3880
4045
  kind: "select",
3881
4046
  table: { name: target.tableName, alias: LATERAL_CHILD_ALIAS },
@@ -4037,7 +4202,7 @@ function compileAggregatePredicate(params) {
4037
4202
  const aggColumn = key === "_count" ? { name: field.columnName } : compareColumnRef({ dialect, field });
4038
4203
  agg = { fn: AGG_FN_BY_KEY[key], column: aggColumn };
4039
4204
  }
4040
- const spec = isPlainObject2(filter) ? filter : { equals: filter };
4205
+ const spec = isPlainObject3(filter) ? filter : { equals: filter };
4041
4206
  const out = [];
4042
4207
  for (const [operator, operand] of Object.entries(spec)) {
4043
4208
  if (operand === undefined)
@@ -4104,7 +4269,7 @@ function compileHaving(params) {
4104
4269
  meta: { model: model.name, field: field.name }
4105
4270
  });
4106
4271
  };
4107
- if (isPlainObject2(value)) {
4272
+ if (isPlainObject3(value)) {
4108
4273
  const plain = {};
4109
4274
  for (const [operator, operand] of Object.entries(value)) {
4110
4275
  if (operand === undefined)
@@ -4191,7 +4356,7 @@ function compileGroupByOrder(params) {
4191
4356
  function overlayRefetchWhere(params) {
4192
4357
  const { model, where, data } = params;
4193
4358
  const out = { ...where };
4194
- if (!isPlainObject2(data))
4359
+ if (!isPlainObject3(data))
4195
4360
  return out;
4196
4361
  for (const key of Object.keys(out)) {
4197
4362
  if (!model.fieldByName.has(key))
@@ -4203,7 +4368,7 @@ function overlayRefetchWhere(params) {
4203
4368
  out[key] = value;
4204
4369
  continue;
4205
4370
  }
4206
- if (isPlainObject2(value) && (isDirectValue(value.set) || value.set === null)) {
4371
+ if (isPlainObject3(value) && (isDirectValue(value.set) || value.set === null)) {
4207
4372
  out[key] = value.set;
4208
4373
  }
4209
4374
  }
@@ -4234,7 +4399,7 @@ function resolveStrategy(params) {
4234
4399
  const strategy = explicit ?? defaultRelationStrategy ?? "query";
4235
4400
  if (strategy === "join" && !dialect.capabilities.lateralJoin) {
4236
4401
  if (explicit !== undefined || hasRelations) {
4237
- throw unsupportedCapability2({
4402
+ throw unsupportedCapability3({
4238
4403
  dialect: dialect.name,
4239
4404
  feature: 'relationStrategy "join"',
4240
4405
  hint: 'lateral joins are postgres-only \u2014 use the portable "query" strategy on this dialect'
@@ -4271,7 +4436,7 @@ function buildQuery(params) {
4271
4436
  const aggregating = method === "aggregate" || method === "groupBy" || method === "count";
4272
4437
  const relationCapable = RELATION_CAPABLE_METHODS.includes(method);
4273
4438
  const relations = relationCapable ? resolveRelationSelection({ meta, model, select: args.select, include: args.include }) : null;
4274
- if (!relationCapable && !aggregating && isPlainObject2(args.select)) {
4439
+ if (!relationCapable && !aggregating && isPlainObject3(args.select)) {
4275
4440
  for (const key of Object.keys(args.select)) {
4276
4441
  if (args.select[key] === undefined || args.select[key] === false)
4277
4442
  continue;
@@ -4641,7 +4806,7 @@ function buildQuery(params) {
4641
4806
  case "createMany":
4642
4807
  case "createManyAndReturn": {
4643
4808
  if (method === "createManyAndReturn" && !dialect.capabilities.createManyAndReturn) {
4644
- throw unsupportedCapability2({
4809
+ throw unsupportedCapability3({
4645
4810
  dialect: dialect.name,
4646
4811
  feature: "createManyAndReturn",
4647
4812
  hint: "this dialect cannot RETURNING multiple inserted rows \u2014 use createMany then findMany"
@@ -4773,7 +4938,7 @@ function buildQuery(params) {
4773
4938
  case "updateManyAndReturn": {
4774
4939
  const isUnique = method === "update";
4775
4940
  if (method === "updateManyAndReturn" && !dialect.capabilities.returning) {
4776
- throw unsupportedCapability2({
4941
+ throw unsupportedCapability3({
4777
4942
  dialect: dialect.name,
4778
4943
  feature: "updateManyAndReturn",
4779
4944
  hint: "this dialect has no RETURNING \u2014 use updateMany then findMany, accepting that the read is a separate, unprotected statement"
@@ -4818,7 +4983,7 @@ function buildQuery(params) {
4818
4983
  });
4819
4984
  }
4820
4985
  if (!dialect.capabilities.conflictTargetConstraint) {
4821
- throw unsupportedCapability2({
4986
+ throw unsupportedCapability3({
4822
4987
  dialect: dialect.name,
4823
4988
  feature: "a named-constraint conflict target on upsert",
4824
4989
  hint: "only postgres supports ON CONFLICT ON CONSTRAINT \u2014 rely on the where-derived column target instead"
@@ -4836,7 +5001,7 @@ function buildQuery(params) {
4836
5001
  const targetFields = conflictTargetGroup({ model, where }).fields;
4837
5002
  for (const field of targetFields) {
4838
5003
  if (!(field.columnName in row.values)) {
4839
- throw new VibeError7({
5004
+ throw new VibeError8({
4840
5005
  code: "VIBE_INTERNAL",
4841
5006
  message: `upsert on ${model.name}: create data does not bind conflict-target column "${field.columnName}" \u2014 this shape must run the find-then-write fallback flow`,
4842
5007
  meta: { model: model.name, column: field.columnName }
@@ -4847,7 +5012,7 @@ function buildQuery(params) {
4847
5012
  for (const [key, value] of Object.entries(where)) {
4848
5013
  if (value === undefined || nativeTargetNames.has(key))
4849
5014
  continue;
4850
- throw new VibeError7({
5015
+ throw new VibeError8({
4851
5016
  code: "VIBE_INTERNAL",
4852
5017
  message: `upsert on ${model.name}: where field "${key}" is not part of the conflict target \u2014 guarded upserts must run the find-then-write fallback flow`,
4853
5018
  meta: { model: model.name, field: key }
@@ -4900,7 +5065,7 @@ function buildQuery(params) {
4900
5065
  import { getSqlDialect, renderStatement } from "@vibeorm/sql";
4901
5066
 
4902
5067
  // packages/runtime/src/relation-key.ts
4903
- import { VibeError as VibeError8 } from "@vibeorm/schema";
5068
+ import { VibeError as VibeError9 } from "@vibeorm/schema";
4904
5069
  function relationValueKey(params) {
4905
5070
  const { value } = params;
4906
5071
  if (value === null || value === undefined)
@@ -4933,7 +5098,7 @@ function relationTupleKey(params) {
4933
5098
  if (value === null || value === undefined)
4934
5099
  return null;
4935
5100
  if (typeof value === "object" && !(value instanceof Date) && !(value instanceof Uint8Array)) {
4936
- throw new VibeError8({
5101
+ throw new VibeError9({
4937
5102
  code: "VIBE_UNSUPPORTED_CAPABILITY",
4938
5103
  message: "a relation key of this type cannot be compared faithfully in memory \u2014 relation links support String, Int, BigInt, Boolean, DateTime and Bytes keys",
4939
5104
  meta: { received: Object.prototype.toString.call(value) }
@@ -4959,7 +5124,7 @@ function assertWriteScope(params) {
4959
5124
  }
4960
5125
 
4961
5126
  // packages/runtime/src/nested-writes.ts
4962
- import { VibeError as VibeError9 } from "@vibeorm/schema";
5127
+ import { VibeError as VibeError10 } from "@vibeorm/schema";
4963
5128
  function parentTupleOf(params) {
4964
5129
  const { link, row } = params;
4965
5130
  return link.parentKeys.map((field) => row[field.name]);
@@ -5006,12 +5171,12 @@ function detachAssignment(params) {
5006
5171
  return out;
5007
5172
  }
5008
5173
  function validationError4(params) {
5009
- return new VibeError9({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
5174
+ return new VibeError10({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
5010
5175
  }
5011
5176
  function notFound(params) {
5012
- return new VibeError9({ code: "VIBE_NOT_FOUND", message: params.message, meta: params.meta });
5177
+ return new VibeError10({ code: "VIBE_NOT_FOUND", message: params.message, meta: params.meta });
5013
5178
  }
5014
- function isPlainObject3(value) {
5179
+ function isPlainObject4(value) {
5015
5180
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
5016
5181
  }
5017
5182
  function isDirectValue2(value) {
@@ -5019,7 +5184,7 @@ function isDirectValue2(value) {
5019
5184
  }
5020
5185
  function hasNestedWrites(params) {
5021
5186
  const { model, data } = params;
5022
- if (!isPlainObject3(data))
5187
+ if (!isPlainObject4(data))
5023
5188
  return false;
5024
5189
  for (const [key, value] of Object.entries(data)) {
5025
5190
  if (value === undefined)
@@ -5059,7 +5224,7 @@ function partitionData(params) {
5059
5224
  scalars[key] = value;
5060
5225
  continue;
5061
5226
  }
5062
- if (!isPlainObject3(value)) {
5227
+ if (!isPlainObject4(value)) {
5063
5228
  throw validationError4({
5064
5229
  message: `relation ${model.name}.${key} in data must be an object of nested-write operations`,
5065
5230
  meta: { model: model.name, relation: key }
@@ -5101,7 +5266,7 @@ function toItems(params) {
5101
5266
  });
5102
5267
  }
5103
5268
  return list.map((item) => {
5104
- if (!isPlainObject3(item)) {
5269
+ if (!isPlainObject4(item)) {
5105
5270
  throw validationError4({
5106
5271
  message: `nested "${verb}" on ${op.link.relation.model}.${op.name} expects object items`,
5107
5272
  meta: { relation: op.name, verb }
@@ -5113,7 +5278,7 @@ function toItems(params) {
5113
5278
  function requireShape(params) {
5114
5279
  const { op, verb, item, keys } = params;
5115
5280
  for (const key of keys) {
5116
- if (!isPlainObject3(item[key])) {
5281
+ if (!isPlainObject4(item[key])) {
5117
5282
  throw validationError4({
5118
5283
  message: `nested "${verb}" on ${op.link.relation.model}.${op.name} needs an object "${key}"`,
5119
5284
  meta: { relation: op.name, verb, missing: key }
@@ -5368,7 +5533,7 @@ function createNeedsTransaction(params) {
5368
5533
  if (op.link.kind !== "toOneOwner")
5369
5534
  return true;
5370
5535
  const { verb, operand } = singleVerb({ op });
5371
- if (verb !== "connect" || !isPlainObject3(operand))
5536
+ if (verb !== "connect" || !isPlainObject4(operand))
5372
5537
  return true;
5373
5538
  const entries = Object.entries(operand).filter(([, value]) => value !== undefined);
5374
5539
  const named = new Map(entries);
@@ -5385,7 +5550,7 @@ async function createWithNested(params) {
5385
5550
  }
5386
5551
  function updateDataOf(params) {
5387
5552
  const { op, operand } = params;
5388
- if ("data" in operand && isPlainObject3(operand.data))
5553
+ if ("data" in operand && isPlainObject4(operand.data))
5389
5554
  return operand.data;
5390
5555
  if ("data" in operand) {
5391
5556
  throw validationError4({
@@ -5476,7 +5641,7 @@ async function resolveOwnerUpdateVerb(params) {
5476
5641
  return;
5477
5642
  }
5478
5643
  case "update": {
5479
- if (!isPlainObject3(operand)) {
5644
+ if (!isPlainObject4(operand)) {
5480
5645
  throw validationError4({
5481
5646
  message: `nested "update" on ${link.relation.model}.${op.name} expects an object`,
5482
5647
  meta: { relation: op.name }
@@ -5499,7 +5664,7 @@ async function resolveOwnerUpdateVerb(params) {
5499
5664
  return;
5500
5665
  }
5501
5666
  default: {
5502
- if (!isPlainObject3(operand)) {
5667
+ if (!isPlainObject4(operand)) {
5503
5668
  throw validationError4({
5504
5669
  message: `nested "upsert" on ${link.relation.model}.${op.name} expects an object`,
5505
5670
  meta: { relation: op.name }
@@ -5640,7 +5805,7 @@ async function applyChildUpdateOps(params) {
5640
5805
  }
5641
5806
  case "update": {
5642
5807
  if (link.kind === "toOneInverse") {
5643
- if (!isPlainObject3(operand)) {
5808
+ if (!isPlainObject4(operand)) {
5644
5809
  throw validationError4({
5645
5810
  message: `nested "update" on ${link.relation.model}.${op.name} expects an object`,
5646
5811
  meta: { relation: op.name }
@@ -5703,7 +5868,7 @@ async function applyChildUpdateOps(params) {
5703
5868
  const items = toItems({ op, verb, operand });
5704
5869
  for (const item of items) {
5705
5870
  const data = item.data;
5706
- if (!isPlainObject3(data)) {
5871
+ if (!isPlainObject4(data)) {
5707
5872
  throw validationError4({
5708
5873
  message: `nested "updateMany" on ${link.relation.model}.${op.name} needs an object "data"`,
5709
5874
  meta: { relation: op.name }
@@ -5774,7 +5939,7 @@ async function applyChildUpdateOps(params) {
5774
5939
  }
5775
5940
  default: {
5776
5941
  if (link.kind === "toOneInverse") {
5777
- if (!isPlainObject3(operand)) {
5942
+ if (!isPlainObject4(operand)) {
5778
5943
  throw validationError4({
5779
5944
  message: `nested "upsert" on ${link.relation.model}.${op.name} expects an object`,
5780
5945
  meta: { relation: op.name }
@@ -6113,7 +6278,7 @@ function previewOperation(params) {
6113
6278
  };
6114
6279
  }
6115
6280
  // packages/runtime/src/diagnostics/plan.ts
6116
- import { VibeError as VibeError10, unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
6281
+ import { VibeError as VibeError11, unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
6117
6282
  import { NATIVE_RLS, getSqlDialect as getSqlDialect2, renderPostgresExplain, renderPostgresWorkload, renderStatement as renderStatement2 } from "@vibeorm/sql";
6118
6283
  var PLAN_DEFAULTS = Object.freeze({ maxNodes: 200, maxConditionLength: 2000 });
6119
6284
  var CONDITION_KEYS = [
@@ -6132,7 +6297,7 @@ var CONDITION_KEYS = [
6132
6297
  "Output"
6133
6298
  ];
6134
6299
  function planError(params) {
6135
- return new VibeError10({ code: "VIBE_ADAPTER", message: params.message, meta: {} });
6300
+ return new VibeError11({ code: "VIBE_ADAPTER", message: params.message, meta: {} });
6136
6301
  }
6137
6302
  function asRecord(value) {
6138
6303
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
@@ -6165,14 +6330,14 @@ function parsePostgresPlan(params) {
6165
6330
  const maxNodes = params.maxNodes ?? PLAN_DEFAULTS.maxNodes;
6166
6331
  const includeLiterals = params.includeLiterals === true;
6167
6332
  if (!Number.isInteger(maxNodes) || maxNodes < 1) {
6168
- throw new VibeError10({ code: "VIBE_CONFIG", message: `maxNodes must be a positive integer, got ${String(maxNodes)}`, meta: { maxNodes } });
6333
+ throw new VibeError11({ code: "VIBE_CONFIG", message: `maxNodes must be a positive integer, got ${String(maxNodes)}`, meta: { maxNodes } });
6169
6334
  }
6170
6335
  let parsed = payload;
6171
6336
  if (typeof payload === "string") {
6172
6337
  try {
6173
6338
  parsed = JSON.parse(payload);
6174
6339
  } catch {
6175
- throw new VibeError10({
6340
+ throw new VibeError11({
6176
6341
  code: "VIBE_ADAPTER",
6177
6342
  message: "EXPLAIN returned malformed JSON",
6178
6343
  meta: { reason: "malformed-plan-json", mode }
@@ -6239,7 +6404,7 @@ function parsePostgresPlan(params) {
6239
6404
  }
6240
6405
  var WRITE_LEADERS = ["INSERT", "UPDATE", "DELETE", "MERGE", "TRUNCATE", "CREATE", "DROP", "ALTER", "GRANT", "REVOKE", "COPY", "CALL", "DO"];
6241
6406
  function refuse(params) {
6242
- return new VibeError10({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta ?? {} });
6407
+ return new VibeError11({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta ?? {} });
6243
6408
  }
6244
6409
  function assertExecutableRead(params) {
6245
6410
  const normalized = normalizeSql({ text: params.text });
@@ -6278,7 +6443,7 @@ async function readPlanContext(params) {
6278
6443
  }
6279
6444
  function requirePostgres(params) {
6280
6445
  if (params.dialect !== "postgres") {
6281
- throw unsupportedCapability3({
6446
+ throw unsupportedCapability4({
6282
6447
  dialect: params.dialect,
6283
6448
  feature: "query plan inspection",
6284
6449
  hint: "plan inspection is PostgreSQL-shaped; operation tracing (telemetry) works on every dialect"
@@ -6330,7 +6495,7 @@ async function explainSql(params) {
6330
6495
  const context = await readPlanContext({ executor, dialect, rlsContextKeys: contextKeys });
6331
6496
  if (!context.representative) {
6332
6497
  if (params.requireRepresentativeContext === true) {
6333
- throw new VibeError10({
6498
+ throw new VibeError11({
6334
6499
  code: "VIBE_RLS_CONTEXT",
6335
6500
  message: `refusing to report a plan taken under a non-representative identity: ${context.reason ?? "role bypasses row policies"}`,
6336
6501
  meta: { currentUser: context.currentUser, bypassRls: context.bypassRls, superuser: context.superuser }
@@ -6412,7 +6577,7 @@ async function explainOperation(params) {
6412
6577
  });
6413
6578
  }
6414
6579
  // packages/runtime/src/diagnostics/workload.ts
6415
- import { unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
6580
+ import { unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
6416
6581
  import { getSqlDialect as getSqlDialect3, renderPostgresWorkload as renderPostgresWorkload2 } from "@vibeorm/sql";
6417
6582
  var STATEMENTS_INFO_MIN_VERSION = 140000;
6418
6583
  var STATEMENTS_MIN_VERSION = 130000;
@@ -6445,7 +6610,7 @@ async function readAvailability(params) {
6445
6610
  async function readWorkloadStatistics(params) {
6446
6611
  const { executor } = params;
6447
6612
  if (params.dialect !== "postgres") {
6448
- throw unsupportedCapability4({
6613
+ throw unsupportedCapability5({
6449
6614
  dialect: params.dialect,
6450
6615
  feature: "workload statistics (pg_stat_statements)",
6451
6616
  hint: "server workload statistics are PostgreSQL-only; operation telemetry works on every dialect"
@@ -6523,7 +6688,7 @@ async function readWorkloadStatistics(params) {
6523
6688
  };
6524
6689
  }
6525
6690
  // packages/runtime/src/diagnostics/insight.ts
6526
- import { VibeError as VibeError11 } from "@vibeorm/schema";
6691
+ import { VibeError as VibeError12 } from "@vibeorm/schema";
6527
6692
  var DIAGNOSTIC_THRESHOLDS = Object.freeze({
6528
6693
  repeatThreshold: 5,
6529
6694
  minRepeatThreshold: 2,
@@ -6541,7 +6706,7 @@ function bounded(params) {
6541
6706
  if (value === undefined)
6542
6707
  return fallback;
6543
6708
  if (!Number.isInteger(value) || value < min || value > max) {
6544
- throw new VibeError11({
6709
+ throw new VibeError12({
6545
6710
  code: "VIBE_CONFIG",
6546
6711
  message: `${name} must be an integer between ${min} and ${max}, got ${String(value)}`,
6547
6712
  meta: { [name]: value }
@@ -6764,11 +6929,11 @@ function recommendFromPlan(params) {
6764
6929
  return findings.slice(0, maxFindings);
6765
6930
  }
6766
6931
  // packages/runtime/src/relation-loader.ts
6767
- import { VibeError as VibeError13 } from "@vibeorm/schema";
6932
+ import { VibeError as VibeError14 } from "@vibeorm/schema";
6768
6933
  import { renderStatement as renderStatement3 } from "@vibeorm/sql";
6769
6934
 
6770
6935
  // packages/runtime/src/computed.ts
6771
- import { VibeError as VibeError12 } from "@vibeorm/schema";
6936
+ import { VibeError as VibeError13 } from "@vibeorm/schema";
6772
6937
  function sameNeeds(a, b) {
6773
6938
  if (a.length !== b.length)
6774
6939
  return false;
@@ -6804,7 +6969,7 @@ function assertComputedManifest(params) {
6804
6969
  problems.push(`"${key}" is configured but missing from the generated client`);
6805
6970
  }
6806
6971
  if (problems.length > 0) {
6807
- throw new VibeError12({
6972
+ throw new VibeError13({
6808
6973
  code: "VIBE_STALE_CLIENT",
6809
6974
  message: `computed-field config changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
6810
6975
  meta: { problems }
@@ -6936,7 +7101,7 @@ function chunkKeys(params) {
6936
7101
  const rendered = renderStatement3({ dialect: ctx.dialect, statement: build(keys.slice(0, 1)) });
6937
7102
  fixed = rendered.values.length - (expands ? arity : 1);
6938
7103
  if (fixed + arity > budget) {
6939
- throw new VibeError13({
7104
+ throw new VibeError14({
6940
7105
  code: "VIBE_UNSUPPORTED_CAPABILITY",
6941
7106
  message: `relation "${relation}" needs ${fixed + arity} bound parameters for its own predicate, more than ${ctx.dialect.name}'s ${budget}-parameter statement budget \u2014 narrow the nested filter`,
6942
7107
  meta: { relation, dialect: ctx.dialect.name, required: fixed + arity, budget }
@@ -7066,7 +7231,7 @@ async function loadManyToMany(params) {
7066
7231
  const link = node.link;
7067
7232
  const join = link.join;
7068
7233
  if (join === null) {
7069
- throw new VibeError13({
7234
+ throw new VibeError14({
7070
7235
  code: "VIBE_INTERNAL",
7071
7236
  message: `manyToMany relation "${node.name}" resolved without join-table wiring`
7072
7237
  });
@@ -7327,14 +7492,15 @@ async function attachRelations(params) {
7327
7492
  await loadCounts({ ctx, model, rows, counts: selection.counts });
7328
7493
  }
7329
7494
  async function attachLateralRelations(params) {
7330
- const { dialect, meta, model, rows, selection, execute, wire } = params;
7495
+ const { dialect, meta, rows, selection, execute, wire } = params;
7331
7496
  if (rows.length === 0)
7332
7497
  return;
7333
7498
  const ctx = { dialect, meta, execute, wire };
7334
7499
  for (const node of selection.relations) {
7335
7500
  const column = lateralColumnAlias({ relation: node.name });
7336
7501
  const isList = node.link.relation.isList;
7337
- const hiddenOrder = lateralHiddenOrderFields({ node });
7502
+ const { columns, hiddenOrder } = lateralProjection({ node });
7503
+ const decodePosition = positionalMaterializer({ model: node.link.target, columns });
7338
7504
  const kept = [];
7339
7505
  for (const row of rows) {
7340
7506
  const raw = row[column];
@@ -7344,7 +7510,7 @@ async function attachLateralRelations(params) {
7344
7510
  try {
7345
7511
  parsed = JSON.parse(raw);
7346
7512
  } catch (cause) {
7347
- throw new VibeError13({
7513
+ throw new VibeError14({
7348
7514
  code: "VIBE_INTERNAL",
7349
7515
  message: `relation column "${column}" returned text that is not valid JSON`,
7350
7516
  meta: { relation: node.name },
@@ -7353,8 +7519,10 @@ async function attachLateralRelations(params) {
7353
7519
  }
7354
7520
  }
7355
7521
  if (isList) {
7356
- const list = Array.isArray(parsed) ? parsed : [];
7357
- const children = materializeJsonRows({ model: node.link.target, rows: list });
7522
+ if (!Array.isArray(parsed)) {
7523
+ throw new VibeError14({ code: "VIBE_INTERNAL", message: "to-many relation payload must be an array", meta: { relation: node.name } });
7524
+ }
7525
+ const children = parsed.map(decodePosition);
7358
7526
  for (const child of children) {
7359
7527
  for (const name of hiddenOrder)
7360
7528
  delete child[name];
@@ -7362,14 +7530,11 @@ async function attachLateralRelations(params) {
7362
7530
  row[node.name] = children;
7363
7531
  pushAll(kept, children);
7364
7532
  } else {
7365
- if (parsed === null || parsed === undefined || typeof parsed !== "object") {
7533
+ if (parsed === null) {
7366
7534
  row[node.name] = null;
7367
7535
  continue;
7368
7536
  }
7369
- const child = materializeJsonRows({
7370
- model: node.link.target,
7371
- rows: [parsed]
7372
- })[0];
7537
+ const child = decodePosition(parsed);
7373
7538
  for (const name of hiddenOrder)
7374
7539
  delete child[name];
7375
7540
  row[node.name] = child;
@@ -7378,10 +7543,25 @@ async function attachLateralRelations(params) {
7378
7543
  }
7379
7544
  await finishNode({ ctx, node, kept });
7380
7545
  }
7381
- await loadCounts({ ctx, model, rows, counts: selection.counts });
7546
+ if (selection.counts.length === 0)
7547
+ return;
7548
+ for (const row of rows) {
7549
+ const counts = {};
7550
+ for (let index = 0;index < selection.counts.length; index++) {
7551
+ const column = lateralCountAlias({ index });
7552
+ const raw = row[column];
7553
+ const count = typeof raw === "number" || typeof raw === "bigint" || typeof raw === "string" && /^\d+$/.test(raw) ? Number(raw) : NaN;
7554
+ if (!Number.isSafeInteger(count) || count < 0) {
7555
+ throw new VibeError14({ code: "VIBE_INTERNAL", message: "relation count payload must be a non-negative safe integer", meta: { relation: selection.counts[index]?.name } });
7556
+ }
7557
+ counts[selection.counts[index].name] = count;
7558
+ delete row[column];
7559
+ }
7560
+ row._count = counts;
7561
+ }
7382
7562
  }
7383
7563
  // packages/runtime/src/extensions.ts
7384
- import { VibeError as VibeError14, extensionManifestEntry } from "@vibeorm/schema";
7564
+ import { VibeError as VibeError15, extensionManifestEntry } from "@vibeorm/schema";
7385
7565
  function assertExtensionManifest(params) {
7386
7566
  const { manifest } = params;
7387
7567
  const instances = params.instances ?? [];
@@ -7404,7 +7584,7 @@ function assertExtensionManifest(params) {
7404
7584
  problems.push(`"${name}" is configured but missing from the generated client`);
7405
7585
  }
7406
7586
  if (problems.length > 0) {
7407
- throw new VibeError14({
7587
+ throw new VibeError15({
7408
7588
  code: "VIBE_STALE_CLIENT",
7409
7589
  message: `extension list changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
7410
7590
  meta: { manifest, problems }
@@ -7419,7 +7599,7 @@ function buildExtensionOperatorMap(params) {
7419
7599
  for (const operator of extension.sql?.whereOperators ?? []) {
7420
7600
  const existing = owner.get(operator.name);
7421
7601
  if (existing !== undefined) {
7422
- throw new VibeError14({
7602
+ throw new VibeError15({
7423
7603
  code: "VIBE_CONFIG",
7424
7604
  message: `where operator "${operator.name}" is claimed by both "${existing}" and "${extension.name}"`,
7425
7605
  meta: { operator: operator.name }
@@ -7435,7 +7615,7 @@ function buildExtensionOperatorMap(params) {
7435
7615
  options: extension.options
7436
7616
  });
7437
7617
  if ("error" in compiled) {
7438
- throw new VibeError14({
7618
+ throw new VibeError15({
7439
7619
  code: "VIBE_VALIDATION",
7440
7620
  message: `"${operator.name}" on ${model}.${field}: ${compiled.error}`,
7441
7621
  meta: { model, field, operator: operator.name, extension: extension.name }
@@ -7467,9 +7647,9 @@ function buildConnectHooks(params) {
7467
7647
  return hooks;
7468
7648
  }
7469
7649
  // packages/runtime/src/views.ts
7470
- import { VibeError as VibeError15 } from "@vibeorm/schema";
7650
+ import { VibeError as VibeError16 } from "@vibeorm/schema";
7471
7651
  function validationError5(params) {
7472
- return new VibeError15({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
7652
+ return new VibeError16({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
7473
7653
  }
7474
7654
 
7475
7655
  class ViewResult {
@@ -7518,12 +7698,12 @@ class ViewResult {
7518
7698
  return out;
7519
7699
  }
7520
7700
  }
7521
- function isPlainObject4(value) {
7701
+ function isPlainObject5(value) {
7522
7702
  return typeof value === "object" && value !== null && !Array.isArray(value);
7523
7703
  }
7524
7704
  function resolveDefinition(params) {
7525
7705
  const { meta, name, definition } = params;
7526
- if (!isPlainObject4(definition)) {
7706
+ if (!isPlainObject5(definition)) {
7527
7707
  throw validationError5({
7528
7708
  message: `view "${name}": definition must be an object of { modelKey: { field: true } }`,
7529
7709
  meta: { view: name }
@@ -7540,7 +7720,7 @@ function resolveDefinition(params) {
7540
7720
  meta: { view: name, model: key }
7541
7721
  });
7542
7722
  }
7543
- if (!isPlainObject4(fieldSpec)) {
7723
+ if (!isPlainObject5(fieldSpec)) {
7544
7724
  throw validationError5({
7545
7725
  message: `view "${name}": definition.${key} must be an object of { field: true }`,
7546
7726
  meta: { view: name, model: model.name }
@@ -7595,7 +7775,7 @@ function revealSet(params) {
7595
7775
  const revealed = new Set;
7596
7776
  if (reveal === undefined)
7597
7777
  return revealed;
7598
- if (!isPlainObject4(reveal)) {
7778
+ if (!isPlainObject5(reveal)) {
7599
7779
  throw validationError5({
7600
7780
  message: `view "${name}": reveal must be an object of { field: true }`,
7601
7781
  meta: { view: name, model: mask.model.name }
@@ -7633,13 +7813,13 @@ function mergeLevel(params) {
7633
7813
  const revealed = revealSet({ name, mask, reveal: args.reveal });
7634
7814
  let scalars;
7635
7815
  const userSelect = args.select;
7636
- if (userSelect !== undefined && !isPlainObject4(userSelect)) {
7816
+ if (userSelect !== undefined && !isPlainObject5(userSelect)) {
7637
7817
  throw validationError5({
7638
7818
  message: `view "${name}": select must be an object`,
7639
7819
  meta: { view: name, model: model.name }
7640
7820
  });
7641
7821
  }
7642
- if (isPlainObject4(userSelect)) {
7822
+ if (isPlainObject5(userSelect)) {
7643
7823
  scalars = new Set;
7644
7824
  for (const [key, flag] of Object.entries(userSelect)) {
7645
7825
  if (flag === false || flag === undefined)
@@ -7657,7 +7837,7 @@ function mergeLevel(params) {
7657
7837
  const collectRelations = (source, argName) => {
7658
7838
  if (source === undefined)
7659
7839
  return;
7660
- if (!isPlainObject4(source)) {
7840
+ if (!isPlainObject5(source)) {
7661
7841
  throw validationError5({
7662
7842
  message: `view "${name}": ${argName} must be an object`,
7663
7843
  meta: { view: name, model: model.name }
@@ -7705,7 +7885,7 @@ function mergeLevel(params) {
7705
7885
  relationPlans.set(relationName, child2.plan);
7706
7886
  continue;
7707
7887
  }
7708
- if (!isPlainObject4(spec)) {
7888
+ if (!isPlainObject5(spec)) {
7709
7889
  throw validationError5({
7710
7890
  message: `view "${name}": include.${relationName} must be true or an object`,
7711
7891
  meta: { view: name, model: model.name, relation: relationName }
@@ -7743,7 +7923,7 @@ function wrapRows(params) {
7743
7923
  const value = record[relationName];
7744
7924
  if (Array.isArray(value)) {
7745
7925
  record[relationName] = wrapRows({ rows: value, plan: childPlan });
7746
- } else if (isPlainObject4(value)) {
7926
+ } else if (isPlainObject5(value)) {
7747
7927
  record[relationName] = wrapRows({ rows: [value], plan: childPlan })[0];
7748
7928
  }
7749
7929
  }
@@ -7768,7 +7948,7 @@ function createViewDelegate(params) {
7768
7948
  const { meta, name, definition, mask, delegate } = params;
7769
7949
  const merge = (args) => {
7770
7950
  const raw = args ?? {};
7771
- if (!isPlainObject4(raw)) {
7951
+ if (!isPlainObject5(raw)) {
7772
7952
  throw validationError5({
7773
7953
  message: `view "${name}": query args must be an object`,
7774
7954
  meta: { view: name, model: mask.model.name }
@@ -7823,7 +8003,7 @@ function createView(params) {
7823
8003
  for (const [clientName, mask] of resolved) {
7824
8004
  const delegate = models[clientName];
7825
8005
  if (delegate === undefined) {
7826
- throw new VibeError15({
8006
+ throw new VibeError16({
7827
8007
  code: "VIBE_INTERNAL",
7828
8008
  message: `view "${name}": no delegate for model "${mask.model.name}"`,
7829
8009
  meta: { view: name, model: mask.model.name }
@@ -7839,9 +8019,9 @@ function attachDefineView(params) {
7839
8019
  Object.defineProperty(client, "$defineView", { value, enumerable: true });
7840
8020
  }
7841
8021
  // packages/runtime/src/validators.ts
7842
- import { VibeError as VibeError16 } from "@vibeorm/schema";
8022
+ import { VibeError as VibeError17 } from "@vibeorm/schema";
7843
8023
  function validationFailed(params) {
7844
- return new VibeError16({
8024
+ return new VibeError17({
7845
8025
  code: "VIBE_VALIDATION",
7846
8026
  message: `validation failed for ${params.model}.${params.field}: ${params.issues[0]?.message ?? "invalid value"}`,
7847
8027
  meta: { model: params.model, field: params.field, issues: params.issues }
@@ -7850,7 +8030,7 @@ function validationFailed(params) {
7850
8030
  function validateValue(params) {
7851
8031
  const result = params.schema["~standard"].validate(params.value);
7852
8032
  if (result instanceof Promise) {
7853
- throw new VibeError16({
8033
+ throw new VibeError17({
7854
8034
  code: "VIBE_VALIDATION",
7855
8035
  message: `async validator on ${params.model}.${params.field} \u2014 write validators must be synchronous (validate before calling the client for async rules)`,
7856
8036
  meta: { model: params.model, field: params.field }
@@ -7862,12 +8042,12 @@ function validateValue(params) {
7862
8042
  return result.value;
7863
8043
  }
7864
8044
  var ATOMIC_OPERATORS = ["set", "increment", "decrement", "multiply", "divide", "push"];
7865
- function isPlainObject5(value) {
8045
+ function isPlainObject6(value) {
7866
8046
  return typeof value === "object" && value !== null && !Array.isArray(value);
7867
8047
  }
7868
8048
  function operatorOf(params) {
7869
8049
  const { model, field, value } = params;
7870
- if (!isPlainObject5(value))
8050
+ if (!isPlainObject6(value))
7871
8051
  return;
7872
8052
  const meta = model.fieldByName.get(field);
7873
8053
  if (meta === undefined || meta.scalarType === "Json")
@@ -7911,10 +8091,10 @@ function validateWriteData(params) {
7911
8091
  }
7912
8092
  function validateNestedVerbs(params) {
7913
8093
  const { meta, target, verbs, validators } = params;
7914
- const one = (row) => isPlainObject5(row) ? validateWriteData({ meta, model: target, data: row, validators }) : row;
8094
+ const one = (row) => isPlainObject6(row) ? validateWriteData({ meta, model: target, data: row, validators }) : row;
7915
8095
  const many = (value) => Array.isArray(value) ? value.map(one) : one(value);
7916
8096
  const body = (value, arms) => {
7917
- if (!isPlainObject5(value))
8097
+ if (!isPlainObject6(value))
7918
8098
  return value;
7919
8099
  if (!arms.some((arm) => value[arm] !== undefined))
7920
8100
  return one(value);
@@ -7930,11 +8110,11 @@ function validateNestedVerbs(params) {
7930
8110
  if (verbs.create !== undefined)
7931
8111
  next.create = many(verbs.create);
7932
8112
  if (verbs.createMany !== undefined) {
7933
- next.createMany = isPlainObject5(verbs.createMany) ? { ...verbs.createMany, data: many(verbs.createMany.data) } : many(verbs.createMany);
8113
+ next.createMany = isPlainObject6(verbs.createMany) ? { ...verbs.createMany, data: many(verbs.createMany.data) } : many(verbs.createMany);
7934
8114
  }
7935
8115
  if (verbs.connectOrCreate !== undefined) {
7936
8116
  const entries = Array.isArray(verbs.connectOrCreate) ? verbs.connectOrCreate : [verbs.connectOrCreate];
7937
- const mapped = entries.map((entry) => isPlainObject5(entry) ? { ...entry, create: many(entry.create) } : entry);
8117
+ const mapped = entries.map((entry) => isPlainObject6(entry) ? { ...entry, create: many(entry.create) } : entry);
7938
8118
  next.connectOrCreate = Array.isArray(verbs.connectOrCreate) ? mapped : mapped[0];
7939
8119
  }
7940
8120
  if (verbs.update !== undefined)
@@ -7973,14 +8153,14 @@ function deferPolicyOperation(params) {
7973
8153
  }
7974
8154
 
7975
8155
  // packages/runtime/src/policy.ts
7976
- import { VibeError as VibeError17 } from "@vibeorm/schema";
8156
+ import { VibeError as VibeError18 } from "@vibeorm/schema";
7977
8157
  function policyError(message, meta) {
7978
- return new VibeError17({ code: "VIBE_VALIDATION", message, meta: meta ?? {} });
8158
+ return new VibeError18({ code: "VIBE_VALIDATION", message, meta: meta ?? {} });
7979
8159
  }
7980
8160
  function policiedModels(meta) {
7981
8161
  return meta.models.filter((model) => model.policy !== undefined);
7982
8162
  }
7983
- function isPlainObject6(value) {
8163
+ function isPlainObject7(value) {
7984
8164
  return typeof value === "object" && value !== null && !Array.isArray(value);
7985
8165
  }
7986
8166
  function deferred(build) {
@@ -8016,7 +8196,7 @@ function uniqueToFilter(params) {
8016
8196
  }
8017
8197
  function scopeWhere(params) {
8018
8198
  const { meta, model, where, context } = params;
8019
- if (!isPlainObject6(where))
8199
+ if (!isPlainObject7(where))
8020
8200
  return where;
8021
8201
  const out = {};
8022
8202
  const extra = [];
@@ -8036,10 +8216,10 @@ function scopeWhere(params) {
8036
8216
  }
8037
8217
  const scoped = target.policy !== undefined;
8038
8218
  const pred = () => predicateFor({ model: target, context });
8039
- const walkChild = (child) => isPlainObject6(child) ? scopeWhere({ meta, model: target, where: child, context }) : child;
8219
+ const walkChild = (child) => isPlainObject7(child) ? scopeWhere({ meta, model: target, where: child, context }) : child;
8040
8220
  const parts = [];
8041
8221
  if (relation.isList) {
8042
- if (!isPlainObject6(value)) {
8222
+ if (!isPlainObject7(value)) {
8043
8223
  out[key] = value;
8044
8224
  continue;
8045
8225
  }
@@ -8060,7 +8240,7 @@ function scopeWhere(params) {
8060
8240
  const arms = [];
8061
8241
  if (value === null) {
8062
8242
  arms.push({ arm: "is", operand: null });
8063
- } else if (isPlainObject6(value) && (("is" in value) || ("isNot" in value))) {
8243
+ } else if (isPlainObject7(value) && (("is" in value) || ("isNot" in value))) {
8064
8244
  let mixed = false;
8065
8245
  for (const [op, operand] of Object.entries(value)) {
8066
8246
  if (operand === undefined)
@@ -8074,7 +8254,7 @@ function scopeWhere(params) {
8074
8254
  out[key] = value;
8075
8255
  continue;
8076
8256
  }
8077
- } else if (isPlainObject6(value)) {
8257
+ } else if (isPlainObject7(value)) {
8078
8258
  arms.push({ arm: "is", operand: value });
8079
8259
  } else {
8080
8260
  out[key] = value;
@@ -8112,7 +8292,7 @@ function scopeWhere(params) {
8112
8292
  function assertPolicyOrder(params) {
8113
8293
  const { meta, model, orderBy } = params;
8114
8294
  for (const term of Array.isArray(orderBy) ? orderBy : [orderBy]) {
8115
- if (!isPlainObject6(term))
8295
+ if (!isPlainObject7(term))
8116
8296
  continue;
8117
8297
  for (const [name, spec] of Object.entries(term)) {
8118
8298
  if (spec === undefined)
@@ -8134,18 +8314,18 @@ function scopeSelection(params) {
8134
8314
  assertPolicyOrder({ meta, model, orderBy: args.orderBy });
8135
8315
  for (const key of ["include", "select"]) {
8136
8316
  const block = args[key];
8137
- if (!isPlainObject6(block))
8317
+ if (!isPlainObject7(block))
8138
8318
  continue;
8139
8319
  const next = { ...block };
8140
8320
  for (const [name, spec] of Object.entries(block)) {
8141
8321
  if (spec === undefined || spec === false)
8142
8322
  continue;
8143
8323
  if (name === "_count") {
8144
- const names = spec === true ? model.relations.filter((relation2) => relation2.isList).map((relation2) => relation2.name) : isPlainObject6(spec) && isPlainObject6(spec.select) ? Object.keys(spec.select).filter((key2) => {
8324
+ const names = spec === true ? model.relations.filter((relation2) => relation2.isList).map((relation2) => relation2.name) : isPlainObject7(spec) && isPlainObject7(spec.select) ? Object.keys(spec.select).filter((key2) => {
8145
8325
  const entry = spec.select[key2];
8146
- return entry === true || isPlainObject6(entry);
8326
+ return entry === true || isPlainObject7(entry);
8147
8327
  }) : [];
8148
- const countSelect = isPlainObject6(spec) && isPlainObject6(spec.select) ? { ...spec.select } : undefined;
8328
+ const countSelect = isPlainObject7(spec) && isPlainObject7(spec.select) ? { ...spec.select } : undefined;
8149
8329
  for (const counted of names) {
8150
8330
  const relation2 = model.relationByName.get(counted);
8151
8331
  const target2 = relation2 === undefined ? undefined : meta.modelByName.get(relation2.targetModel);
@@ -8153,7 +8333,7 @@ function scopeSelection(params) {
8153
8333
  throw policyError(`_count of ${model.name}.${counted} cannot enforce @@policy because relation counts take no filter \u2014 use a scoped child count query instead`, { model: model.name, relation: counted });
8154
8334
  }
8155
8335
  const entry = countSelect?.[counted];
8156
- if (target2 !== undefined && countSelect !== undefined && isPlainObject6(entry) && entry.where !== undefined) {
8336
+ if (target2 !== undefined && countSelect !== undefined && isPlainObject7(entry) && entry.where !== undefined) {
8157
8337
  countSelect[counted] = { ...entry, where: scopeWhere({ meta, model: target2, where: entry.where, context }) };
8158
8338
  }
8159
8339
  }
@@ -8195,7 +8375,7 @@ function forceWriteData(params) {
8195
8375
  if (model.policy !== undefined) {
8196
8376
  const value = predicateFor({ model, context })[model.policy.field];
8197
8377
  const given = data[model.policy.field];
8198
- const explicit = isPlainObject6(given) && "set" in given ? given.set : given;
8378
+ const explicit = isPlainObject7(given) && "set" in given ? given.set : given;
8199
8379
  if (explicit !== undefined && explicit !== value) {
8200
8380
  throw policyError(`${phase} on ${model.name}: data.${model.policy.field} conflicts with the bound policy context`, { model: model.name, field: model.policy.field, phase });
8201
8381
  }
@@ -8213,7 +8393,7 @@ function forceWriteData(params) {
8213
8393
  }
8214
8394
  for (const [name, spec] of Object.entries(data)) {
8215
8395
  const relation = model.relationByName.get(name);
8216
- if (relation === undefined || !isPlainObject6(spec))
8396
+ if (relation === undefined || !isPlainObject7(spec))
8217
8397
  continue;
8218
8398
  const target = meta.modelByName.get(relation.targetModel);
8219
8399
  if (target === undefined)
@@ -8245,10 +8425,10 @@ function forceWriteData(params) {
8245
8425
  }
8246
8426
  function forceNestedVerbs(params) {
8247
8427
  const { meta, target, verbs, context } = params;
8248
- const walk2 = (row, phase) => isPlainObject6(row) ? forceWriteData({ meta, model: target, data: row, context, phase }) : row;
8428
+ const walk2 = (row, phase) => isPlainObject7(row) ? forceWriteData({ meta, model: target, data: row, context, phase }) : row;
8249
8429
  const each = (value, phase) => Array.isArray(value) ? value.map((row) => walk2(row, phase)) : walk2(value, phase);
8250
8430
  const body = (value, keys) => {
8251
- if (!isPlainObject6(value))
8431
+ if (!isPlainObject7(value))
8252
8432
  return value;
8253
8433
  const hasArm = keys.some((key) => value[key] !== undefined);
8254
8434
  if (!hasArm)
@@ -8264,12 +8444,12 @@ function forceNestedVerbs(params) {
8264
8444
  const out = { ...verbs };
8265
8445
  if (verbs.create !== undefined)
8266
8446
  out.create = each(verbs.create, "create");
8267
- if (verbs.createMany !== undefined && isPlainObject6(verbs.createMany)) {
8447
+ if (verbs.createMany !== undefined && isPlainObject7(verbs.createMany)) {
8268
8448
  out.createMany = { ...verbs.createMany, data: each(verbs.createMany.data, "create") };
8269
8449
  }
8270
8450
  if (verbs.connectOrCreate !== undefined) {
8271
8451
  const entries = Array.isArray(verbs.connectOrCreate) ? verbs.connectOrCreate : [verbs.connectOrCreate];
8272
- const mapped = entries.map((entry) => isPlainObject6(entry) ? { ...entry, create: each(entry.create, "create") } : entry);
8452
+ const mapped = entries.map((entry) => isPlainObject7(entry) ? { ...entry, create: each(entry.create, "create") } : entry);
8273
8453
  out.connectOrCreate = Array.isArray(verbs.connectOrCreate) ? mapped : mapped[0];
8274
8454
  }
8275
8455
  if (verbs.update !== undefined) {
@@ -8319,7 +8499,7 @@ function scopedDelegate(params) {
8319
8499
  const pkWhere = pk.length === 1 ? { [pk[0]]: target[pk[0]] } : { [pkClientKey()]: Object.fromEntries(pk.map((name) => [name, target[name]])) };
8320
8500
  return write(raw, pkWhere);
8321
8501
  };
8322
- const notFound2 = (method) => new VibeError17({ code: "VIBE_NOT_FOUND", message: `${method} on ${model.name}: no row matches the unique where inside the bound policy context`, meta: { model: model.name, method } });
8502
+ const notFound2 = (method) => new VibeError18({ code: "VIBE_NOT_FOUND", message: `${method} on ${model.name}: no row matches the unique where inside the bound policy context`, meta: { model: model.name, method } });
8323
8503
  return {
8324
8504
  findMany: (args) => base.findMany(scopeFind(args)),
8325
8505
  findFirst: (args) => base.findFirst(scopeFind(args)),
@@ -8353,7 +8533,7 @@ function scopedDelegate(params) {
8353
8533
  })
8354
8534
  })),
8355
8535
  upsertMany: () => {
8356
- throw new VibeError17({
8536
+ throw new VibeError18({
8357
8537
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8358
8538
  message: "upsertMany is not available on a policy-bound client: its conflict target is not policy-checked yet",
8359
8539
  meta: { model: model.name }
@@ -8362,7 +8542,7 @@ function scopedDelegate(params) {
8362
8542
  updateManyAndReturn: (args) => deferred(() => {
8363
8543
  const run = base.updateManyAndReturn;
8364
8544
  if (run === undefined) {
8365
- throw new VibeError17({
8545
+ throw new VibeError18({
8366
8546
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8367
8547
  message: "updateManyAndReturn is not available on this delegate",
8368
8548
  meta: { model: model.name }
@@ -8433,7 +8613,7 @@ function globalDelegate(params) {
8433
8613
  updateManyAndReturn: (args) => deferred(() => {
8434
8614
  const run = base.updateManyAndReturn;
8435
8615
  if (run === undefined) {
8436
- throw new VibeError17({
8616
+ throw new VibeError18({
8437
8617
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8438
8618
  message: "updateManyAndReturn is not available on this delegate"
8439
8619
  });
@@ -8444,7 +8624,7 @@ function globalDelegate(params) {
8444
8624
  upsertMany: (args) => deferred(() => {
8445
8625
  if (params.policyBound === false)
8446
8626
  return base.upsertMany(args);
8447
- throw new VibeError17({
8627
+ throw new VibeError18({
8448
8628
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8449
8629
  message: "upsertMany is not available on a policy-bound client: its conflict target is not policy-checked yet"
8450
8630
  });
@@ -8491,7 +8671,7 @@ function attachPolicy(params) {
8491
8671
  const guardValue = (value, key) => {
8492
8672
  if (typeof value === "function")
8493
8673
  return guard(key);
8494
- if (!isPlainObject6(value))
8674
+ if (!isPlainObject7(value))
8495
8675
  return value;
8496
8676
  const namespace = {};
8497
8677
  for (const inner of Object.keys(value)) {
@@ -8599,11 +8779,11 @@ function attachPolicy(params) {
8599
8779
  });
8600
8780
  }
8601
8781
  // packages/runtime/src/scoped.ts
8602
- import { ScopedQueryError, VibeError as VibeError18 } from "@vibeorm/schema";
8782
+ import { ScopedQueryError, VibeError as VibeError19 } from "@vibeorm/schema";
8603
8783
  function refusal(params) {
8604
8784
  return new ScopedQueryError(params);
8605
8785
  }
8606
- function isPlainObject7(value) {
8786
+ function isPlainObject8(value) {
8607
8787
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
8608
8788
  }
8609
8789
  var SCOPEABLE_COLUMN_TYPES = ["String", "Int", "BigInt"];
@@ -8649,7 +8829,7 @@ function resolveScopedModels(params) {
8649
8829
  resolved.set(model.name, { kind: "none" });
8650
8830
  continue;
8651
8831
  }
8652
- if (!isPlainObject7(entry)) {
8832
+ if (!isPlainObject8(entry)) {
8653
8833
  throw refusal({
8654
8834
  model: model.name,
8655
8835
  path: "models",
@@ -8797,7 +8977,7 @@ function mergeWhere2(params) {
8797
8977
  }
8798
8978
  function injectUniqueWhere(params) {
8799
8979
  const { where, pred } = params;
8800
- if (!isPlainObject7(where))
8980
+ if (!isPlainObject8(where))
8801
8981
  return { AND: [pred] };
8802
8982
  const existing = where.AND === undefined ? [] : Array.isArray(where.AND) ? where.AND : [where.AND];
8803
8983
  return { ...where, AND: [...existing, pred] };
@@ -8808,7 +8988,7 @@ function flattenUniqueWhere(params) {
8808
8988
  return where;
8809
8989
  const out = {};
8810
8990
  for (const [key, value] of Object.entries(where)) {
8811
- if (isPlainObject7(value) && model.compoundUniques.has(key))
8991
+ if (isPlainObject8(value) && model.compoundUniques.has(key))
8812
8992
  Object.assign(out, value);
8813
8993
  else
8814
8994
  out[key] = value;
@@ -8828,7 +9008,7 @@ function walkWhere(params) {
8828
9008
  const out = {};
8829
9009
  const extra = [];
8830
9010
  const walkGroup = (value, key) => {
8831
- const walkItem = (item) => isPlainObject7(item) ? walkWhere({ ctx, model, where: item, path: `${path}.${key}` }) : item;
9011
+ const walkItem = (item) => isPlainObject8(item) ? walkWhere({ ctx, model, where: item, path: `${path}.${key}` }) : item;
8832
9012
  return Array.isArray(value) ? value.map(walkItem) : walkItem(value);
8833
9013
  };
8834
9014
  for (const [key, value] of Object.entries(where)) {
@@ -8848,10 +9028,10 @@ function walkWhere(params) {
8848
9028
  const scoped = targetEntry.kind !== "none";
8849
9029
  const childPath = `${path}.${key}`;
8850
9030
  const pred = () => requirePred({ ctx, model: target, path: childPath });
8851
- const walkChild = (child, op) => isPlainObject7(child) ? walkWhere({ ctx, model: target, where: child, path: `${childPath}.${op}` }) : child;
9031
+ const walkChild = (child, op) => isPlainObject8(child) ? walkWhere({ ctx, model: target, where: child, path: `${childPath}.${op}` }) : child;
8852
9032
  const parts = [];
8853
9033
  if (relation.isList) {
8854
- if (!isPlainObject7(value)) {
9034
+ if (!isPlainObject8(value)) {
8855
9035
  out[key] = value;
8856
9036
  continue;
8857
9037
  }
@@ -8872,7 +9052,7 @@ function walkWhere(params) {
8872
9052
  const arms = [];
8873
9053
  if (value === null) {
8874
9054
  arms.push({ arm: "is", operand: null });
8875
- } else if (isPlainObject7(value) && (("is" in value) || ("isNot" in value))) {
9055
+ } else if (isPlainObject8(value) && (("is" in value) || ("isNot" in value))) {
8876
9056
  let mixed = false;
8877
9057
  for (const [op, operand] of Object.entries(value)) {
8878
9058
  if (operand === undefined)
@@ -8886,7 +9066,7 @@ function walkWhere(params) {
8886
9066
  out[key] = value;
8887
9067
  continue;
8888
9068
  }
8889
- } else if (isPlainObject7(value)) {
9069
+ } else if (isPlainObject8(value)) {
8890
9070
  arms.push({ arm: "is", operand: value });
8891
9071
  } else {
8892
9072
  out[key] = value;
@@ -8941,7 +9121,7 @@ function walkOrderTerm(params) {
8941
9121
  message: `$scoped: orderBy through ${model.name}.${key} reaches scoped model ${target.name} \u2014 ordering by a foreign row's values would leak; order by own columns instead`
8942
9122
  });
8943
9123
  }
8944
- if (isPlainObject7(spec)) {
9124
+ if (isPlainObject8(spec)) {
8945
9125
  walkOrderTerm({ ctx, model: target, term: spec, path: `${path}.${key}` });
8946
9126
  }
8947
9127
  }
@@ -8950,7 +9130,7 @@ function walkOrderBy(params) {
8950
9130
  const { ctx, model, orderBy, path } = params;
8951
9131
  const terms = Array.isArray(orderBy) ? orderBy : [orderBy];
8952
9132
  for (const term of terms) {
8953
- if (isPlainObject7(term))
9133
+ if (isPlainObject8(term))
8954
9134
  walkOrderTerm({ ctx, model, term, path });
8955
9135
  }
8956
9136
  }
@@ -8977,7 +9157,7 @@ function walkCountSpec(params) {
8977
9157
  }
8978
9158
  return spec;
8979
9159
  }
8980
- if (!isPlainObject7(spec) || !isPlainObject7(spec.select))
9160
+ if (!isPlainObject8(spec) || !isPlainObject8(spec.select))
8981
9161
  return spec;
8982
9162
  const select = { ...spec.select };
8983
9163
  for (const [name, flag] of Object.entries(select)) {
@@ -8987,7 +9167,7 @@ function walkCountSpec(params) {
8987
9167
  if (relation === undefined)
8988
9168
  continue;
8989
9169
  guard(relation);
8990
- if (isPlainObject7(flag) && isPlainObject7(flag.where)) {
9170
+ if (isPlainObject8(flag) && isPlainObject8(flag.where)) {
8991
9171
  const target = ctx.meta.modelByName.get(relation.targetModel);
8992
9172
  select[name] = { ...flag, where: walkWhere({ ctx, model: target, where: flag.where, path: `${path}.${name}.where` }) };
8993
9173
  }
@@ -8998,7 +9178,7 @@ function walkSelection(params) {
8998
9178
  const { ctx, model, args, path } = params;
8999
9179
  for (const blockKey of ["include", "select"]) {
9000
9180
  const block = args[blockKey];
9001
- if (!isPlainObject7(block))
9181
+ if (!isPlainObject8(block))
9002
9182
  continue;
9003
9183
  const next = { ...block };
9004
9184
  for (const [name, spec] of Object.entries(block)) {
@@ -9018,10 +9198,10 @@ function walkSelection(params) {
9018
9198
  const childPath = `${blockPath}.${name}`;
9019
9199
  if (spec === true && !scoped)
9020
9200
  continue;
9021
- const child = spec === true ? {} : isPlainObject7(spec) ? { ...spec } : spec;
9022
- if (!isPlainObject7(child))
9201
+ const child = spec === true ? {} : isPlainObject8(spec) ? { ...spec } : spec;
9202
+ if (!isPlainObject8(child))
9023
9203
  continue;
9024
- const walkedWhere = isPlainObject7(child.where) ? walkWhere({ ctx, model: target, where: child.where, path: `${childPath}.where` }) : child.where;
9204
+ const walkedWhere = isPlainObject8(child.where) ? walkWhere({ ctx, model: target, where: child.where, path: `${childPath}.where` }) : child.where;
9025
9205
  if (scoped) {
9026
9206
  child.where = mergeWhere2({ where: walkedWhere, pred: requirePred({ ctx, model: target, path: childPath }) });
9027
9207
  } else if (walkedWhere !== undefined) {
@@ -9068,7 +9248,7 @@ function protectScopedData(params) {
9068
9248
  }
9069
9249
  for (const [name, verbs] of Object.entries(out)) {
9070
9250
  const relation = model.relationByName.get(name);
9071
- if (relation === undefined || !isPlainObject7(verbs))
9251
+ if (relation === undefined || !isPlainObject8(verbs))
9072
9252
  continue;
9073
9253
  const target = ctx.meta.modelByName.get(relation.targetModel);
9074
9254
  const targetEntry = entryOf(ctx, target);
@@ -9086,7 +9266,7 @@ function mapItems(value, fn) {
9086
9266
  }
9087
9267
  function walkCreateData(params) {
9088
9268
  const { ctx, model, data, path } = params;
9089
- if (!isPlainObject7(data))
9269
+ if (!isPlainObject8(data))
9090
9270
  return data;
9091
9271
  const entry = entryOf(ctx, model);
9092
9272
  const out = { ...data };
@@ -9126,7 +9306,7 @@ function walkCreateData(params) {
9126
9306
  }
9127
9307
  for (const [key, value] of Object.entries(out)) {
9128
9308
  const relation = model.relationByName.get(key);
9129
- if (relation === undefined || !isPlainObject7(value))
9309
+ if (relation === undefined || !isPlainObject8(value))
9130
9310
  continue;
9131
9311
  out[key] = walkNestedVerbs({ ctx, model, relation, verbs: value, path: `${path}.${key}`, phase: "create" });
9132
9312
  }
@@ -9134,14 +9314,14 @@ function walkCreateData(params) {
9134
9314
  }
9135
9315
  function walkUpdateData(params) {
9136
9316
  const { ctx, model, data, path } = params;
9137
- if (!isPlainObject7(data))
9317
+ if (!isPlainObject8(data))
9138
9318
  return data;
9139
9319
  const entry = entryOf(ctx, model);
9140
9320
  const out = { ...data };
9141
9321
  if (entry.kind === "column") {
9142
9322
  const given = out[entry.field.name];
9143
9323
  if (given !== undefined) {
9144
- const setValue = isPlainObject7(given) && Object.keys(given).length === 1 ? given.set : given;
9324
+ const setValue = isPlainObject8(given) && Object.keys(given).length === 1 ? given.set : given;
9145
9325
  if (setValue !== ctx.scope) {
9146
9326
  throw refusal({
9147
9327
  model: model.name,
@@ -9166,7 +9346,7 @@ function walkUpdateData(params) {
9166
9346
  }
9167
9347
  for (const [key, value] of Object.entries(out)) {
9168
9348
  const relation = model.relationByName.get(key);
9169
- if (relation === undefined || !isPlainObject7(value))
9349
+ if (relation === undefined || !isPlainObject8(value))
9170
9350
  continue;
9171
9351
  out[key] = walkNestedVerbs({ ctx, model, relation, verbs: value, path: `${path}.${key}`, phase: "update" });
9172
9352
  }
@@ -9192,7 +9372,7 @@ function walkNestedVerbs(params) {
9192
9372
  message: `$scoped: nested "${verb}" on ${model.name}.${relation.name} (${target.name}) ${why}`
9193
9373
  });
9194
9374
  };
9195
- const walkTargetWhere = (where, verbPath) => isPlainObject7(where) ? walkWhere({ ctx, model: target, where, path: verbPath }) : where;
9375
+ const walkTargetWhere = (where, verbPath) => isPlainObject8(where) ? walkWhere({ ctx, model: target, where, path: verbPath }) : where;
9196
9376
  const uniqueWhere = (where, verbPath) => {
9197
9377
  const walked = walkTargetWhere(where, verbPath);
9198
9378
  return scoped ? injectUniqueWhere({ where: walked, pred: pred(verbPath) }) : walked;
@@ -9217,10 +9397,10 @@ function walkNestedVerbs(params) {
9217
9397
  }
9218
9398
  switch (verb) {
9219
9399
  case "connect":
9220
- out[verb] = mapItems(operand, (item) => isPlainObject7(item) ? uniqueWhere(item, verbPath) : item);
9400
+ out[verb] = mapItems(operand, (item) => isPlainObject8(item) ? uniqueWhere(item, verbPath) : item);
9221
9401
  break;
9222
9402
  case "connectOrCreate":
9223
- out[verb] = mapItems(operand, (item) => isPlainObject7(item) ? { ...item, where: uniqueWhere(item.where, verbPath), create: walkChildCreate(item.create, verbPath) } : item);
9403
+ out[verb] = mapItems(operand, (item) => isPlainObject8(item) ? { ...item, where: uniqueWhere(item.where, verbPath), create: walkChildCreate(item.create, verbPath) } : item);
9224
9404
  break;
9225
9405
  case "set": {
9226
9406
  if (phase === "create")
@@ -9232,7 +9412,7 @@ function walkNestedVerbs(params) {
9232
9412
  if (!implicitSafe)
9233
9413
  refuse2(verb, "first detaches EVERY current child, which cannot be tenant-filtered on this edge; disconnect explicit rows instead");
9234
9414
  }
9235
- out[verb] = mapItems(operand ?? [], (item) => isPlainObject7(item) ? uniqueWhere(item, verbPath) : item);
9415
+ out[verb] = mapItems(operand ?? [], (item) => isPlainObject8(item) ? uniqueWhere(item, verbPath) : item);
9236
9416
  break;
9237
9417
  }
9238
9418
  case "disconnect": {
@@ -9249,7 +9429,7 @@ function walkNestedVerbs(params) {
9249
9429
  }
9250
9430
  break;
9251
9431
  }
9252
- out[verb] = mapItems(operand, (item) => isPlainObject7(item) ? uniqueWhere(item, verbPath) : item);
9432
+ out[verb] = mapItems(operand, (item) => isPlainObject8(item) ? uniqueWhere(item, verbPath) : item);
9253
9433
  break;
9254
9434
  }
9255
9435
  case "delete": {
@@ -9259,7 +9439,7 @@ function walkNestedVerbs(params) {
9259
9439
  }
9260
9440
  break;
9261
9441
  }
9262
- out[verb] = mapItems(operand, (item) => isPlainObject7(item) ? uniqueWhere(item, verbPath) : item);
9442
+ out[verb] = mapItems(operand, (item) => isPlainObject8(item) ? uniqueWhere(item, verbPath) : item);
9263
9443
  break;
9264
9444
  }
9265
9445
  case "update": {
@@ -9267,14 +9447,14 @@ function walkNestedVerbs(params) {
9267
9447
  if (scoped && !implicitSafe) {
9268
9448
  refuse2(verb, "targets whatever row is currently connected \u2014 not tenant-verifiable on this edge; update it through its own delegate");
9269
9449
  }
9270
- out[verb] = isPlainObject7(operand) ? "data" in operand && isPlainObject7(operand.data) ? { ...operand, data: walkChildUpdate(operand.data, verbPath) } : walkChildUpdate(operand, verbPath) : operand;
9450
+ out[verb] = isPlainObject8(operand) ? "data" in operand && isPlainObject8(operand.data) ? { ...operand, data: walkChildUpdate(operand.data, verbPath) } : walkChildUpdate(operand, verbPath) : operand;
9271
9451
  break;
9272
9452
  }
9273
- out[verb] = mapItems(operand, (item) => isPlainObject7(item) ? { ...item, where: uniqueWhere(item.where, verbPath), data: walkChildUpdate(item.data, verbPath) } : item);
9453
+ out[verb] = mapItems(operand, (item) => isPlainObject8(item) ? { ...item, where: uniqueWhere(item.where, verbPath), data: walkChildUpdate(item.data, verbPath) } : item);
9274
9454
  break;
9275
9455
  }
9276
9456
  case "updateMany":
9277
- out[verb] = mapItems(operand, (item) => isPlainObject7(item) ? { ...item, where: filterWhere(item.where, verbPath), data: walkChildUpdate(item.data, verbPath) } : item);
9457
+ out[verb] = mapItems(operand, (item) => isPlainObject8(item) ? { ...item, where: filterWhere(item.where, verbPath), data: walkChildUpdate(item.data, verbPath) } : item);
9278
9458
  break;
9279
9459
  case "deleteMany":
9280
9460
  out[verb] = mapItems(operand, (item) => filterWhere(item, verbPath));
@@ -9284,14 +9464,14 @@ function walkNestedVerbs(params) {
9284
9464
  if (scoped && !implicitSafe) {
9285
9465
  refuse2(verb, "updates whatever row is currently connected \u2014 not tenant-verifiable on this edge");
9286
9466
  }
9287
- out[verb] = isPlainObject7(operand) ? {
9467
+ out[verb] = isPlainObject8(operand) ? {
9288
9468
  ...operand,
9289
9469
  create: walkChildCreate(operand.create, verbPath),
9290
9470
  update: walkChildUpdate(operand.update, verbPath)
9291
9471
  } : operand;
9292
9472
  break;
9293
9473
  }
9294
- out[verb] = mapItems(operand, (item) => isPlainObject7(item) ? {
9474
+ out[verb] = mapItems(operand, (item) => isPlainObject8(item) ? {
9295
9475
  ...item,
9296
9476
  where: uniqueWhere(item.where, verbPath),
9297
9477
  create: walkChildCreate(item.create, verbPath),
@@ -9333,7 +9513,7 @@ function rewriteScopedCall(params) {
9333
9513
  const root = `${model.clientName}.${method}`;
9334
9514
  const args = { ...params.args ?? {} };
9335
9515
  const ownPred = () => requirePred({ ctx, model, path: root });
9336
- const walkOwnWhere = (where, path) => isPlainObject7(where) ? walkWhere({ ctx, model, where, path }) : where;
9516
+ const walkOwnWhere = (where, path) => isPlainObject8(where) ? walkWhere({ ctx, model, where, path }) : where;
9337
9517
  if (FIND_LIKE.includes(method)) {
9338
9518
  const walked = walkOwnWhere(args.where, `${root}.where`);
9339
9519
  if (scoped)
@@ -9355,8 +9535,8 @@ function rewriteScopedCall(params) {
9355
9535
  args.where = walked2;
9356
9536
  return { model: model.name, method, args };
9357
9537
  }
9358
- const flat = isPlainObject7(args.where) ? flattenUniqueWhere({ model, where: args.where }) : args.where;
9359
- const walked = isPlainObject7(flat) ? walkWhere({ ctx, model, where: flat, path: `${root}.where` }) : flat;
9538
+ const flat = isPlainObject8(args.where) ? flattenUniqueWhere({ model, where: args.where }) : args.where;
9539
+ const walked = isPlainObject8(flat) ? walkWhere({ ctx, model, where: flat, path: `${root}.where` }) : flat;
9360
9540
  args.where = mergeWhere2({ where: walked, pred: ownPred() });
9361
9541
  return {
9362
9542
  model: model.name,
@@ -9483,14 +9663,14 @@ function scopeExtensionProps2(params) {
9483
9663
  out[key] = (args) => {
9484
9664
  try {
9485
9665
  const path = `${model.clientName}.${key}`;
9486
- const walked = isPlainObject7(args?.where) ? walkWhere({ ctx, model, where: args.where, path: `${path}.where` }) : args?.where;
9666
+ const walked = isPlainObject8(args?.where) ? walkWhere({ ctx, model, where: args.where, path: `${path}.where` }) : args?.where;
9487
9667
  const where = entry.kind === "none" ? walked : mergeWhere2({ where: walked, pred: requirePred({ ctx, model, path }) });
9488
9668
  return prop({ ...args ?? {}, ...where === undefined ? {} : { where } });
9489
9669
  } catch (error) {
9490
9670
  return Promise.reject(error);
9491
9671
  }
9492
9672
  };
9493
- } else if (isPlainObject7(prop)) {
9673
+ } else if (isPlainObject8(prop)) {
9494
9674
  out[key] = scopeExtensionProps2({
9495
9675
  ctx,
9496
9676
  model,
@@ -9552,8 +9732,8 @@ function attachScoped(params) {
9552
9732
  const { client, meta } = params;
9553
9733
  Object.defineProperty(client, "$scoped", {
9554
9734
  value: (config) => {
9555
- if (!isPlainObject7(config) || !isPlainObject7(config.models)) {
9556
- throw new VibeError18({
9735
+ if (!isPlainObject8(config) || !isPlainObject8(config.models)) {
9736
+ throw new VibeError19({
9557
9737
  code: "VIBE_VALIDATION",
9558
9738
  message: "$scoped takes { scope: string | number | bigint | null, models: { <Model>: \u2026 } }"
9559
9739
  });
@@ -9569,7 +9749,7 @@ function attachScoped(params) {
9569
9749
  }
9570
9750
  const scope = config.scope;
9571
9751
  if (scope !== null && typeof scope !== "string" && typeof scope !== "number" && typeof scope !== "bigint") {
9572
- throw new VibeError18({
9752
+ throw new VibeError19({
9573
9753
  code: "VIBE_VALIDATION",
9574
9754
  message: `$scoped: scope must be a string, number, bigint or null \u2014 got ${typeof scope}`
9575
9755
  });
@@ -9585,7 +9765,7 @@ function attachScoped(params) {
9585
9765
  });
9586
9766
  }
9587
9767
  // packages/runtime/src/rls-readiness.ts
9588
- import { VibeError as VibeError19, assertValidSchemaIR, unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
9768
+ import { VibeError as VibeError20, assertValidSchemaIR, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
9589
9769
  import { NATIVE_RLS as NATIVE_RLS2, getSqlDialect as getSqlDialect4, renderRlsPolicy, renderRlsRole, NATIVE_RLS_PROBE_POLICY, renderRlsReadinessQuery, renderRlsProbeTable } from "@vibeorm/sql";
9590
9770
  import { RLS_READINESS_SQL, NATIVE_RLS_PROBE_POLICY as NATIVE_RLS_PROBE_POLICY2 } from "@vibeorm/sql";
9591
9771
  function textOf(params) {
@@ -9636,10 +9816,10 @@ function unprotectedTablesOf(params) {
9636
9816
  function requireNativeRls(params) {
9637
9817
  const dialect = getSqlDialect4({ dialect: params.adapter.dialect });
9638
9818
  if (!dialect.capabilities.nativeRowLevelSecurity) {
9639
- throw unsupportedCapability5({ dialect: dialect.name, feature: "nativeRowLevelSecurity" });
9819
+ throw unsupportedCapability6({ dialect: dialect.name, feature: "nativeRowLevelSecurity" });
9640
9820
  }
9641
9821
  if (params.schema.rls === undefined) {
9642
- throw new VibeError19({
9822
+ throw new VibeError20({
9643
9823
  code: "VIBE_CONFIG",
9644
9824
  message: "Native row-level security readiness requires a schema that declares native RLS"
9645
9825
  });
@@ -10051,7 +10231,7 @@ async function inspectNativeRls(params) {
10051
10231
  function requireEmbeddedRole(params) {
10052
10232
  const { role } = params;
10053
10233
  if (typeof role !== "string" || role.length === 0 || role.includes("\x00")) {
10054
- throw new VibeError19({
10234
+ throw new VibeError20({
10055
10235
  code: "VIBE_CONFIG",
10056
10236
  message: "PGlite native row-level security requires a pre-existing restricted runtime role (nativeRls.runtimeRole) \u2014 its default embedded session is privileged and bypasses every policy",
10057
10237
  meta: { provider: "pglite" }
@@ -10063,7 +10243,7 @@ function assertNativeRlsReady(params) {
10063
10243
  const { report } = params;
10064
10244
  if (report.ready)
10065
10245
  return;
10066
- throw new VibeError19({
10246
+ throw new VibeError20({
10067
10247
  code: "VIBE_CONFIG",
10068
10248
  message: `Native row-level security is not ready on this database: ${report.problems.map((problem) => `${problem.name} (${problem.status})`).join(", ")}`,
10069
10249
  meta: {
@@ -10088,7 +10268,7 @@ function formatNativeRlsReport(params) {
10088
10268
  return lines;
10089
10269
  }
10090
10270
  // packages/runtime/src/client.ts
10091
- import { RlsContextError as RlsContextError2, VibeError as VibeError23, assertValidSchemaIR as assertValidSchemaIR2, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
10271
+ import { RlsContextError as RlsContextError2, VibeError as VibeError24, assertValidSchemaIR as assertValidSchemaIR2, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
10092
10272
  import { ParamCollector, getSqlDialect as getSqlDialect5, renderRlsContext, renderRlsPolicy as renderRlsPolicy2, renderRlsRole as renderRlsRole2 } from "@vibeorm/sql";
10093
10273
 
10094
10274
  // packages/runtime/src/render-cache.ts
@@ -10105,12 +10285,25 @@ var MAX_ENTRIES = 512;
10105
10285
 
10106
10286
  class StatementRenderCache {
10107
10287
  entries = new Map;
10288
+ get size() {
10289
+ return this.entries.size;
10290
+ }
10108
10291
  get(key) {
10109
- return this.entries.get(key);
10292
+ const slot = this.entries.get(key);
10293
+ if (slot === undefined)
10294
+ return;
10295
+ this.entries.delete(key);
10296
+ this.entries.set(key, slot);
10297
+ return slot;
10110
10298
  }
10111
10299
  set(key, slot) {
10112
- if (this.entries.size >= MAX_ENTRIES && !this.entries.has(key))
10113
- this.entries.clear();
10300
+ if (this.entries.has(key)) {
10301
+ this.entries.delete(key);
10302
+ } else if (this.entries.size >= MAX_ENTRIES) {
10303
+ const oldest = this.entries.keys().next();
10304
+ if (!oldest.done)
10305
+ this.entries.delete(oldest.value);
10306
+ }
10114
10307
  this.entries.set(key, slot);
10115
10308
  }
10116
10309
  }
@@ -10384,7 +10577,8 @@ function walkSelect(walk2, dialect, statement) {
10384
10577
  }
10385
10578
  if (statement.lateralJoins !== undefined && statement.lateralJoins.length > 0) {
10386
10579
  for (const join of statement.lateralJoins) {
10387
- walk2.key += join.agg === "jsonArray" ? "Ja(" : "Jo(";
10580
+ walk2.key += join.agg === "count" ? "Jc(" : join.agg === "jsonArray" ? "Ja(" : "Jo(";
10581
+ walk2.key += join.rowFormat === "positional" ? "pos;" : "obj;";
10388
10582
  if (!walkSelect(walk2, dialect, join.select))
10389
10583
  return false;
10390
10584
  addIdent(walk2, join.columnAlias);
@@ -10579,17 +10773,17 @@ function renderStatementCached(params) {
10579
10773
  }
10580
10774
 
10581
10775
  // packages/runtime/src/strict-args.ts
10582
- import { VibeError as VibeError20 } from "@vibeorm/schema";
10776
+ import { VibeError as VibeError21 } from "@vibeorm/schema";
10583
10777
  var SKIP = Symbol.for("vibeorm.skip");
10584
10778
  function explicitUndefined(params) {
10585
10779
  const { model, method, path } = params;
10586
- return new VibeError20({
10780
+ return new VibeError21({
10587
10781
  code: "VIBE_VALIDATION",
10588
10782
  message: `${model}.${method}(): explicit undefined at ${path} \u2014 an undefined value is refused ` + `because it would silently drop the property (widening a filter or losing a write). ` + `Omit the property, or pass SKIP to omit it deliberately; ` + `VibeClient({ strictArguments: false }) restores the legacy behaviour.`,
10589
10783
  meta: { model, method, path }
10590
10784
  });
10591
10785
  }
10592
- function isPlainObject8(value) {
10786
+ function isPlainObject9(value) {
10593
10787
  if (typeof value !== "object" || value === null || Array.isArray(value))
10594
10788
  return false;
10595
10789
  const proto = Object.getPrototypeOf(value);
@@ -10621,7 +10815,7 @@ function walkNode(params) {
10621
10815
  const filter = params.filter === true;
10622
10816
  if (Array.isArray(value))
10623
10817
  return walkArray({ ctx, model, value, path, filter });
10624
- if (!isPlainObject8(value))
10818
+ if (!isPlainObject9(value))
10625
10819
  return value;
10626
10820
  let out = null;
10627
10821
  for (const key of Object.keys(value)) {
@@ -10637,7 +10831,7 @@ function walkNode(params) {
10637
10831
  }
10638
10832
  const target = descend({ ctx, model, key });
10639
10833
  if (target === "json") {
10640
- if (filter && isPlainObject8(child)) {
10834
+ if (filter && isPlainObject9(child)) {
10641
10835
  let filtered = null;
10642
10836
  for (const operator of Object.keys(child)) {
10643
10837
  if (child[operator] === undefined) {
@@ -10760,10 +10954,10 @@ function bindRlsContext(params) {
10760
10954
  }
10761
10955
 
10762
10956
  // packages/runtime/src/telemetry/recorder.ts
10763
- import { VibeError as VibeError22 } from "@vibeorm/schema";
10957
+ import { VibeError as VibeError23 } from "@vibeorm/schema";
10764
10958
 
10765
10959
  // packages/runtime/src/telemetry/config.ts
10766
- import { VibeError as VibeError21 } from "@vibeorm/schema";
10960
+ import { VibeError as VibeError22 } from "@vibeorm/schema";
10767
10961
  var TELEMETRY_DEFAULTS = Object.freeze({
10768
10962
  captureSql: false,
10769
10963
  captureParameterValues: false,
@@ -10778,7 +10972,7 @@ var TELEMETRY_DEFAULTS = Object.freeze({
10778
10972
  latencyBucketsMs: Object.freeze([1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000])
10779
10973
  });
10780
10974
  function configError(params) {
10781
- return new VibeError21({
10975
+ return new VibeError22({
10782
10976
  code: "VIBE_CONFIG",
10783
10977
  message: `telemetry: ${params.message}`,
10784
10978
  ...params.meta === undefined ? {} : { meta: params.meta }
@@ -10866,7 +11060,7 @@ function deriveStage(params) {
10866
11060
  return "decode";
10867
11061
  }
10868
11062
  function errorCodeOf(error) {
10869
- return error instanceof VibeError22 ? error.code : undefined;
11063
+ return error instanceof VibeError23 ? error.code : undefined;
10870
11064
  }
10871
11065
  function newHistogram(bounds) {
10872
11066
  return { counts: new Array(bounds.length + 1).fill(0), sum: 0, count: 0 };
@@ -11240,7 +11434,7 @@ async function instrumentStatement(params) {
11240
11434
  // packages/runtime/src/client.ts
11241
11435
  function notFound2(params) {
11242
11436
  const { model, method } = params;
11243
- return new VibeError23({
11437
+ return new VibeError24({
11244
11438
  code: "VIBE_NOT_FOUND",
11245
11439
  message: `no ${model} record matched the where clause of ${method}()`,
11246
11440
  meta: { model, method }
@@ -11248,7 +11442,7 @@ function notFound2(params) {
11248
11442
  }
11249
11443
  function viewIsReadOnly(params) {
11250
11444
  const { model, method } = params;
11251
- return new VibeError23({
11445
+ return new VibeError24({
11252
11446
  code: "VIBE_VALIDATION",
11253
11447
  message: `${model} is a view \u2014 ${method}() is not available on views (they are read-only); write to the underlying table instead`,
11254
11448
  meta: { model, method }
@@ -11354,7 +11548,7 @@ async function engineTransaction(params) {
11354
11548
  const support = engine.adapter.budgets?.transactionDeadline;
11355
11549
  const requested = options.deadline.enforcement ?? "between-statements";
11356
11550
  if (support === undefined || support === "unsupported" || requested === "cancel-running-statements" && support !== "cancel-running-statements") {
11357
- throw new VibeError23({
11551
+ throw new VibeError24({
11358
11552
  code: "VIBE_UNSUPPORTED_CAPABILITY",
11359
11553
  message: "the adapter does not declare support for the requested transaction deadline",
11360
11554
  meta: { provider: engine.adapter.provider, feature: "transactionDeadline", requested, supported: support ?? "undeclared" }
@@ -11521,7 +11715,7 @@ class QueryPromise extends Promise {
11521
11715
  }
11522
11716
  claim() {
11523
11717
  if (this.started) {
11524
- throw new VibeError23({
11718
+ throw new VibeError24({
11525
11719
  code: "VIBE_VALIDATION",
11526
11720
  message: "$transaction([...]) needs operations that have not dispatched \u2014 pass delegate calls in the same tick they are created (this one was awaited or created earlier and already executed outside the transaction)"
11527
11721
  });
@@ -11622,7 +11816,7 @@ async function runAffected(params) {
11622
11816
  function plan(params) {
11623
11817
  const { engine, model, method, args } = params;
11624
11818
  if (args !== undefined && args["lock"] !== undefined && engine.inTransaction !== true) {
11625
- throw new VibeError23({
11819
+ throw new VibeError24({
11626
11820
  code: "VIBE_VALIDATION",
11627
11821
  message: `lock on ${model}.${method}() requires a transaction \u2014 call it on the client $transaction hands its callback (outside one, the lock would release at statement end)`,
11628
11822
  meta: { model, method }
@@ -11677,7 +11871,7 @@ async function insertedKeyWhere(params) {
11677
11871
  const keyField = source.model.primaryKey[0];
11678
11872
  const lastInsertIdSql = engine.dialect.lastInsertIdSql;
11679
11873
  if (lastInsertIdSql === null || source.model.primaryKey.length !== 1 || keyField === undefined) {
11680
- throw unsupportedCapability6({
11874
+ throw unsupportedCapability7({
11681
11875
  dialect: engine.dialect.name,
11682
11876
  feature: `${source.method} on ${source.model.name} without RETURNING`,
11683
11877
  hint: "this model's key is assigned by the database and cannot be read back on this dialect"
@@ -11695,7 +11889,7 @@ async function insertedKeyWhere(params) {
11695
11889
  });
11696
11890
  const id = result.rows[0]?.id;
11697
11891
  if (id === undefined || id === null) {
11698
- throw new VibeError23({
11892
+ throw new VibeError24({
11699
11893
  code: "VIBE_INTERNAL",
11700
11894
  message: `could not read back the generated key for ${source.model.name}`,
11701
11895
  meta: { model: source.model.name }
@@ -12012,7 +12206,7 @@ function createDelegate(params) {
12012
12206
  const skipping = args.onConflict !== undefined;
12013
12207
  if (hasNestedWrites({ model: modelMeta, data: args.data })) {
12014
12208
  if (skipping) {
12015
- throw new VibeError23({
12209
+ throw new VibeError24({
12016
12210
  code: "VIBE_VALIDATION",
12017
12211
  message: `onConflict cannot combine with nested writes on ${modelMeta.name}.create() \u2014 insert the parent row alone, then connect relations`,
12018
12212
  meta: { model: modelMeta.name }
@@ -12113,7 +12307,7 @@ function createDelegate(params) {
12113
12307
  const upsert = async (target, args) => {
12114
12308
  const modelMeta = getModelMeta({ meta: target.meta, model });
12115
12309
  if (hasNestedWrites({ model: modelMeta, data: args.create }) || hasNestedWrites({ model: modelMeta, data: args.update })) {
12116
- throw new VibeError23({
12310
+ throw new VibeError24({
12117
12311
  code: "VIBE_VALIDATION",
12118
12312
  message: `nested writes are not supported inside upsert arms on ${modelMeta.name} \u2014 use create()/update() with nested data`,
12119
12313
  meta: { model: modelMeta.name }
@@ -12121,7 +12315,7 @@ function createDelegate(params) {
12121
12315
  }
12122
12316
  if (!upsertUsesNativeStatement({ model: modelMeta, args })) {
12123
12317
  if (args.onConflict !== undefined) {
12124
- throw new VibeError23({
12318
+ throw new VibeError24({
12125
12319
  code: "VIBE_VALIDATION",
12126
12320
  message: `onConflict on ${modelMeta.name}.upsert() needs the native single-statement path \u2014 this shape (a guard field beyond the conflict target, or create data not binding the where's key) runs the find-then-write fallback, which cannot honor an explicit conflict target`,
12127
12321
  meta: { model: modelMeta.name }
@@ -12363,7 +12557,7 @@ function buildClient(params) {
12363
12557
  }
12364
12558
  for (const operation of operations) {
12365
12559
  if (!(operation instanceof QueryPromise)) {
12366
- claimError = new VibeError23({
12560
+ claimError = new VibeError24({
12367
12561
  code: "VIBE_VALIDATION",
12368
12562
  message: "$transaction([...]) takes query promises from model delegate calls \u2014 a foreign promise is already running and cannot join the transaction"
12369
12563
  });
@@ -12428,7 +12622,7 @@ function buildClient(params) {
12428
12622
  $models: models,
12429
12623
  $on: (event, listener) => {
12430
12624
  if (event !== "query") {
12431
- throw new VibeError23({
12625
+ throw new VibeError24({
12432
12626
  code: "VIBE_VALIDATION",
12433
12627
  message: `unknown client event "${String(event)}" \u2014 supported: "query"`,
12434
12628
  meta: { event }
@@ -12494,7 +12688,7 @@ function buildClient(params) {
12494
12688
  attachPolicy({ client, meta: engine.meta, ...wrapDelegate === undefined ? {} : { wrapDelegate } });
12495
12689
  } else {
12496
12690
  const refuseLegacy = () => {
12497
- throw new VibeError23({ code: "VIBE_CONFIG", message: "Native row-level security cannot be combined with application policy or tenant scoping" });
12691
+ throw new VibeError24({ code: "VIBE_CONFIG", message: "Native row-level security cannot be combined with application policy or tenant scoping" });
12498
12692
  };
12499
12693
  Object.defineProperties(client, {
12500
12694
  $withContext: { enumerable: true, value: (context) => {
@@ -12556,7 +12750,7 @@ function makeMountContext(params) {
12556
12750
  compileWhere: ({ where, model: modelName, qualify }) => {
12557
12751
  const target = modelName === undefined ? model : getModelMeta({ meta: engine.meta, model: modelName });
12558
12752
  if (target === null) {
12559
- throw new VibeError23({
12753
+ throw new VibeError24({
12560
12754
  code: "VIBE_INTERNAL",
12561
12755
  message: `extension "${extension}" compiled a where with no model in scope`,
12562
12756
  meta: { extension }
@@ -12585,24 +12779,24 @@ function createMountSurface(params) {
12585
12779
  for (const key of path) {
12586
12780
  receiver = current;
12587
12781
  if (current === null || typeof current !== "object" && typeof current !== "function")
12588
- throw new VibeError23({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
12782
+ throw new VibeError24({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
12589
12783
  current = Reflect.get(current, key);
12590
12784
  }
12591
12785
  if (typeof current !== "function")
12592
- throw new VibeError23({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
12786
+ throw new VibeError24({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
12593
12787
  return Reflect.apply(current, receiver, args);
12594
12788
  } });
12595
12789
  }
12596
12790
  if (value === null || typeof value !== "object")
12597
12791
  return value;
12598
12792
  if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
12599
- throw new VibeError23({ code: "VIBE_CONFIG", message: "Native RLS extension mounts must expose plain objects and methods" });
12793
+ throw new VibeError24({ code: "VIBE_CONFIG", message: "Native RLS extension mounts must expose plain objects and methods" });
12600
12794
  }
12601
12795
  const result = {};
12602
12796
  for (const key of Reflect.ownKeys(value)) {
12603
12797
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
12604
12798
  if (!("value" in descriptor))
12605
- throw new VibeError23({ code: "VIBE_CONFIG", message: "Native RLS extension mount accessors are unsupported" });
12799
+ throw new VibeError24({ code: "VIBE_CONFIG", message: "Native RLS extension mount accessors are unsupported" });
12606
12800
  Object.defineProperty(result, key, { value: wrap(descriptor.value, [...path, key]), enumerable: descriptor.enumerable });
12607
12801
  }
12608
12802
  return Object.freeze(result);
@@ -12619,7 +12813,7 @@ function attachExtensionMounts(params) {
12619
12813
  const model = getModelMeta({ meta: engine.meta, model: mount.model ?? "" });
12620
12814
  const delegate = models[model.name];
12621
12815
  if (delegate === undefined || mount.property in delegate) {
12622
- throw new VibeError23({
12816
+ throw new VibeError24({
12623
12817
  code: "VIBE_CONFIG",
12624
12818
  message: `extension "${mount.extension}" cannot mount "${mount.property}" on ${model.name} \u2014 the name is taken`,
12625
12819
  meta: { extension: mount.extension, model: model.name, property: mount.property }
@@ -12630,7 +12824,7 @@ function attachExtensionMounts(params) {
12630
12824
  continue;
12631
12825
  }
12632
12826
  if (!mount.property.startsWith("$") || mount.property in client) {
12633
- throw new VibeError23({
12827
+ throw new VibeError24({
12634
12828
  code: "VIBE_CONFIG",
12635
12829
  message: `extension "${mount.extension}" cannot mount client property "${mount.property}" \u2014 root mounts are $-prefixed and must not collide`,
12636
12830
  meta: { extension: mount.extension, property: mount.property }
@@ -12647,7 +12841,7 @@ function createClient(params) {
12647
12841
  if (schema.rls !== undefined) {
12648
12842
  const dialect = getSqlDialect5({ dialect: dialectName });
12649
12843
  if (!dialect.capabilities.nativeRowLevelSecurity)
12650
- throw unsupportedCapability6({ dialect: dialectName, feature: "nativeRowLevelSecurity" });
12844
+ throw unsupportedCapability7({ dialect: dialectName, feature: "nativeRowLevelSecurity" });
12651
12845
  assertValidSchemaIR2({ schema });
12652
12846
  for (const model of schema.models) {
12653
12847
  if (model.rls !== "unprotected")
@@ -12656,9 +12850,9 @@ function createClient(params) {
12656
12850
  const role = options?.nativeRls?.runtimeRole;
12657
12851
  if (adapter.provider === "pglite") {
12658
12852
  if (typeof role !== "string" || role.length === 0 || role.includes("\x00"))
12659
- throw new VibeError23({ code: "VIBE_CONFIG", message: "PGlite native row-level security requires a pre-existing restricted runtimeRole; embedded binding is not a boundary against the host process" });
12853
+ throw new VibeError24({ code: "VIBE_CONFIG", message: "PGlite native row-level security requires a pre-existing restricted runtimeRole; embedded binding is not a boundary against the host process" });
12660
12854
  } else if (role !== undefined) {
12661
- throw new VibeError23({ code: "VIBE_CONFIG", message: "Server native row-level security must use the runtime login role, not a runtimeRole switch" });
12855
+ throw new VibeError24({ code: "VIBE_CONFIG", message: "Server native row-level security must use the runtime login role, not a runtimeRole switch" });
12662
12856
  }
12663
12857
  }
12664
12858
  if (extensions?.manifest !== undefined) {
@@ -12668,7 +12862,7 @@ function createClient(params) {
12668
12862
  assertComputedManifest({ manifest: computed.manifest, specs: computed.specs });
12669
12863
  }
12670
12864
  if (computed === undefined && options !== undefined && options["computed"] !== undefined) {
12671
- throw new VibeError23({
12865
+ throw new VibeError24({
12672
12866
  code: "VIBE_CONFIG",
12673
12867
  message: "this client was generated without computed fields \u2014 declare them in vibeorm.config.ts and re-run vibeorm generate",
12674
12868
  meta: {}
@@ -12699,9 +12893,9 @@ function createClient(params) {
12699
12893
  });
12700
12894
  }
12701
12895
  // packages/runtime/src/keyset-iterator.ts
12702
- import { VibeError as VibeError24 } from "@vibeorm/schema";
12896
+ import { VibeError as VibeError25 } from "@vibeorm/schema";
12703
12897
  function validation(params) {
12704
- return new VibeError24({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
12898
+ return new VibeError25({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
12705
12899
  }
12706
12900
  function projectionFor(params) {
12707
12901
  const { plan: plan2, select, omit } = params;
@@ -12778,7 +12972,7 @@ function iterateKeyset(options) {
12778
12972
  });
12779
12973
  if (advanced === false) {
12780
12974
  stop();
12781
- throw new VibeError24({
12975
+ throw new VibeError25({
12782
12976
  code: "VIBE_DATA_INVALID",
12783
12977
  message: `keyset traversal of "${options.model}" is not advancing: the engine returned a row that does not sort after the previous position. ` + "The ordering is not total, or a cursor value lost precision \u2014 a DateTime column finer than milliseconds asserted as millisecond-exact does this.",
12784
12978
  meta: { model: options.model, order: plan2.signature, reason: "keyset-position-not-advancing" }
@@ -12851,7 +13045,7 @@ function iterateKeyset(options) {
12851
13045
  return iterator;
12852
13046
  }
12853
13047
  // packages/runtime/src/telemetry/collector.ts
12854
- import { VibeError as VibeError25 } from "@vibeorm/schema";
13048
+ import { VibeError as VibeError26 } from "@vibeorm/schema";
12855
13049
  function createTelemetryCollector(params) {
12856
13050
  const throwOn = new Set(params?.throwOn ?? []);
12857
13051
  const operations = [];
@@ -12861,7 +13055,7 @@ function createTelemetryCollector(params) {
12861
13055
  let shutdowns = 0;
12862
13056
  const hostile = (phase) => {
12863
13057
  if (throwOn.has(phase)) {
12864
- throw new VibeError25({
13058
+ throw new VibeError26({
12865
13059
  code: "VIBE_INTERNAL",
12866
13060
  message: `telemetry collector: deliberate ${phase} failure`,
12867
13061
  meta: { reason: "telemetry-collector-deliberate-failure", phase }
@@ -12917,7 +13111,7 @@ function createTelemetryCollector(params) {
12917
13111
  operationFor: (lookup) => {
12918
13112
  const matches = operations.filter((event) => event.method === lookup.method && (lookup.model === undefined || event.model === lookup.model));
12919
13113
  if (matches.length > 1) {
12920
- throw new VibeError25({
13114
+ throw new VibeError26({
12921
13115
  code: "VIBE_VALIDATION",
12922
13116
  message: `telemetry collector: ${matches.length} operations match ${lookup.model ?? "*"}.${lookup.method}`,
12923
13117
  meta: {
@@ -13021,4 +13215,4 @@ export {
13021
13215
  COUNT_ALIAS
13022
13216
  };
13023
13217
 
13024
- //# debugId=08CBEBB868708E5A64756E2164756E21
13218
+ //# debugId=ADD0DD01D02385B864756E2164756E21