@vibeorm/runtime 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,54 @@
1
1
  // @bun
2
+ // packages/runtime/src/index.ts
3
+ import { sql as sql2, decode as decode2 } from "@vibeorm/sql";
4
+
5
+ // packages/runtime/src/sql.ts
6
+ import { VibeError, isEnumType } from "@vibeorm/schema";
7
+ import { decode, sql } from "@vibeorm/sql";
8
+ function sqlProjection(params) {
9
+ const entries = Object.entries(params.fields);
10
+ if (entries.length === 0)
11
+ throw new VibeError({ code: "VIBE_VALIDATION", message: "A SQL projection needs at least one field" });
12
+ return Object.freeze({
13
+ sql: sql.join({ items: entries.map(([name, column]) => sql`${column.sql} AS ${sql.ident({ parts: [name] })}`) }),
14
+ decoder: decode.object({ fields: Object.fromEntries(entries.map(([name, column]) => [name, column.decoder])) })
15
+ });
16
+ }
17
+ var SCALAR_DECODERS = Object.freeze({
18
+ String: decode.string,
19
+ Boolean: decode.boolean,
20
+ Int: decode.int,
21
+ Float: decode.float,
22
+ BigInt: decode.bigInt,
23
+ Decimal: decode.decimal,
24
+ DateTime: decode.dateTime,
25
+ Json: decode.json,
26
+ Bytes: decode.bytes
27
+ });
28
+ function createSqlModel(params) {
29
+ const model = params.schema.models.find((entry) => entry.name === params.model);
30
+ if (model === undefined)
31
+ throw new VibeError({ code: "VIBE_VALIDATION", message: "Unknown SQL model" });
32
+ const tableName = model.dbName ?? model.name;
33
+ const table = sql.ident({ parts: [tableName] });
34
+ const fields = Object.fromEntries(model.fields.filter((field) => field.isUnsupported !== true).map((field) => {
35
+ let decoder;
36
+ if (isEnumType(field.type)) {
37
+ const name = field.type.enum;
38
+ const declared = params.schema.enums.find((entry) => entry.name === name);
39
+ if (declared === undefined)
40
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Unknown enum in SQL model" });
41
+ decoder = decode.enum({ values: declared.values });
42
+ } else
43
+ decoder = SCALAR_DECODERS[field.type];
44
+ if (field.isList)
45
+ decoder = decode.array({ decoder });
46
+ if (field.isOptional)
47
+ decoder = decode.nullable({ decoder });
48
+ return [field.name, Object.freeze({ sql: sql.ident({ parts: [params.alias ?? tableName, field.dbName ?? field.name] }), decoder })];
49
+ }));
50
+ return Object.freeze({ table: params.alias === undefined ? table : sql`${table} AS ${sql.ident({ parts: [params.alias] })}`, fields: Object.freeze(fields) });
51
+ }
2
52
  // packages/runtime/src/adapter.ts
3
53
  function toSqlExecutor(params) {
4
54
  const { adapter } = params;
@@ -11,11 +61,11 @@ function toSqlExecutor(params) {
11
61
  };
12
62
  }
13
63
  // packages/runtime/src/db-now.ts
14
- import { VibeError } from "@vibeorm/schema";
64
+ import { VibeError as VibeError2 } from "@vibeorm/schema";
15
65
  function dbNow(options) {
16
66
  const offset = options?.plusSeconds ?? 0;
17
67
  if (typeof offset !== "number" || !Number.isFinite(offset)) {
18
- throw new VibeError({
68
+ throw new VibeError2({
19
69
  code: "VIBE_VALIDATION",
20
70
  message: `dbNow plusSeconds must be a finite number of seconds (fractional is fine), got ${String(offset)}`,
21
71
  meta: { plusSeconds: offset }
@@ -33,8 +83,116 @@ function isDbNow(value) {
33
83
  const candidate = value;
34
84
  return candidate.__vibeDbNow === true && typeof candidate.offsetSeconds === "number" && Number.isFinite(candidate.offsetSeconds);
35
85
  }
86
+ // packages/runtime/src/advisory-lock.ts
87
+ import { VibeError as VibeError3, unsupportedCapability } from "@vibeorm/schema";
88
+ import { ADVISORY_KEY_BOUNDS } from "@vibeorm/sql";
89
+ function validationError(params) {
90
+ return new VibeError3({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
91
+ }
92
+ function readInt32(params) {
93
+ const { value, member, method } = params;
94
+ if (typeof value !== "number" || !Number.isInteger(value) || value < ADVISORY_KEY_BOUNDS.pair.min || value > ADVISORY_KEY_BOUNDS.pair.max) {
95
+ throw validationError({
96
+ message: `${method}({ ${member} }) must be a 32-bit integer ` + `(${ADVISORY_KEY_BOUNDS.pair.min} \u2026 ${ADVISORY_KEY_BOUNDS.pair.max}), got ${String(value)}`,
97
+ meta: { method, member, value: typeof value === "number" ? value : String(value) }
98
+ });
99
+ }
100
+ return value;
101
+ }
102
+ function readInt64(params) {
103
+ const { value, method } = params;
104
+ if (typeof value === "bigint") {
105
+ if (value < ADVISORY_KEY_BOUNDS.single.min || value > ADVISORY_KEY_BOUNDS.single.max) {
106
+ throw validationError({
107
+ message: `${method}({ key }) must fit a 64-bit integer, got ${value.toString()}`,
108
+ meta: { method, member: "key", value: value.toString() }
109
+ });
110
+ }
111
+ return value;
112
+ }
113
+ if (typeof value === "number" && Number.isSafeInteger(value))
114
+ return BigInt(value);
115
+ throw validationError({
116
+ message: `${method}({ key }) must be a bigint or a safe integer, got ${String(value)} \u2014 ` + `a number past 2^53 cannot name one specific lock key, so pass a bigint (e.g. 9007199254740993n)`,
117
+ meta: { method, member: "key", value: String(value) }
118
+ });
119
+ }
120
+ function compileAdvisoryLock(params) {
121
+ const { dialect, method, inTransaction, options } = params;
122
+ if (!dialect.capabilities.advisoryLocks) {
123
+ throw unsupportedCapability({
124
+ dialect: dialect.name,
125
+ feature: `${method}()`,
126
+ hint: dialect.name === "mysql" ? "mysql's GET_LOCK is SESSION-scoped \u2014 it survives a rollback, so it cannot be released with the transaction. Serialize on a real row instead (a lock table with one row per resource)" : "serialize on a real row instead (a lock table with one row per resource)"
127
+ });
128
+ }
129
+ if (!inTransaction) {
130
+ throw validationError({
131
+ message: `${method}() requires a transaction \u2014 call it on the client $transaction hands its callback. ` + `Outside one, the lock is released before the next statement runs`,
132
+ meta: { method }
133
+ });
134
+ }
135
+ if (typeof options !== "object" || options === null || Array.isArray(options)) {
136
+ throw validationError({
137
+ message: `${method}() takes { key } or { classId, objectId }, plus an optional mode, got ${String(options)}`,
138
+ meta: { method }
139
+ });
140
+ }
141
+ const request = options;
142
+ const hasSingle = "key" in request;
143
+ const hasPair = "classId" in request || "objectId" in request;
144
+ if (hasSingle && hasPair) {
145
+ throw validationError({
146
+ message: `${method}() takes ONE key form: { key } or { classId, objectId }. They are different lock spaces, ` + `so naming both cannot be resolved`,
147
+ meta: { method }
148
+ });
149
+ }
150
+ for (const member of Object.keys(request)) {
151
+ if (member === "mode" || member === "key" || member === "classId" || member === "objectId")
152
+ continue;
153
+ throw validationError({
154
+ message: `${method}() has no "${member}" option \u2014 it takes { key } or { classId, objectId } and an optional mode`,
155
+ meta: { method, member }
156
+ });
157
+ }
158
+ const mode = readMode({ value: request.mode, method });
159
+ if (hasSingle) {
160
+ const key2 = { key: readInt64({ value: request.key, method }) };
161
+ return statementFor({ method, mode, key: key2 });
162
+ }
163
+ if (!("classId" in request) || !("objectId" in request)) {
164
+ throw validationError({
165
+ message: `${method}({ classId, objectId }) needs BOTH halves of the two-key form`,
166
+ meta: { method }
167
+ });
168
+ }
169
+ const key = {
170
+ classId: readInt32({ value: request.classId, member: "classId", method }),
171
+ objectId: readInt32({ value: request.objectId, member: "objectId", method })
172
+ };
173
+ return statementFor({ method, mode, key });
174
+ }
175
+ function readMode(params) {
176
+ const { value, method } = params;
177
+ if (value === undefined || value === "exclusive")
178
+ return "exclusive";
179
+ if (value === "shared")
180
+ return "shared";
181
+ throw validationError({
182
+ message: `${method}({ mode }) must be "exclusive" or "shared", got ${String(value)}`,
183
+ meta: { method, mode: String(value) }
184
+ });
185
+ }
186
+ function statementFor(params) {
187
+ return {
188
+ kind: "advisoryLock",
189
+ mode: params.mode,
190
+ wait: params.method === "$tryAdvisoryLock" ? "nowait" : "wait",
191
+ key: params.key
192
+ };
193
+ }
36
194
  // packages/runtime/src/model-meta.ts
37
- import { VibeError as VibeError2, enumStorageOf, isEnumType, validateComputedConfig } from "@vibeorm/schema";
195
+ import { VibeError as VibeError4, enumStorageOf, isEnumType as isEnumType2, validateComputedConfig } from "@vibeorm/schema";
38
196
  import { enumMembershipCodec } from "@vibeorm/sql";
39
197
  function toClientName(params) {
40
198
  const { name } = params;
@@ -131,7 +289,7 @@ function defaultOriginOf(params) {
131
289
  }
132
290
  function buildFieldMeta(params) {
133
291
  const { model, field, enums } = params;
134
- const enumName = isEnumType(field.type) ? field.type.enum : null;
292
+ const enumName = isEnumType2(field.type) ? field.type.enum : null;
135
293
  const enumDef = enumName === null ? undefined : enums.get(enumName);
136
294
  const enumStorage = enumName === null ? null : enumStorageOf({ enumDef: enumDef ?? {} });
137
295
  return {
@@ -163,7 +321,7 @@ function buildModelMeta(params) {
163
321
  for (const name of model.primaryKey.fields) {
164
322
  const field = fieldByName.get(name);
165
323
  if (field === undefined) {
166
- throw new VibeError2({
324
+ throw new VibeError4({
167
325
  code: "VIBE_SCHEMA_INVALID",
168
326
  message: `model "${model.name}" declares composite primary-key field "${name}", which does not exist`,
169
327
  meta: { model: model.name, field: name }
@@ -269,7 +427,7 @@ function getModelMeta(params) {
269
427
  const { meta, model } = params;
270
428
  const found = meta.modelByName.get(model);
271
429
  if (found === undefined) {
272
- throw new VibeError2({
430
+ throw new VibeError4({
273
431
  code: "VIBE_VALIDATION",
274
432
  message: `unknown model "${model}"`,
275
433
  meta: { model, known: [...new Set(meta.models.map((entry) => entry.name))] }
@@ -282,20 +440,20 @@ function getFieldMeta(params) {
282
440
  const found = model.fieldByName.get(field);
283
441
  if (found === undefined) {
284
442
  if (model.computed !== null && model.computed.has(field)) {
285
- throw new VibeError2({
443
+ throw new VibeError4({
286
444
  code: "VIBE_VALIDATION",
287
445
  message: `"${field}" on model "${model.name}" is a computed field \u2014 computed fields appear in results (select/omit) only, not in ${context}`,
288
446
  meta: { model: model.name, field, context, computed: true }
289
447
  });
290
448
  }
291
449
  if (model.relationByName.has(field)) {
292
- throw new VibeError2({
450
+ throw new VibeError4({
293
451
  code: "VIBE_VALIDATION",
294
452
  message: `"${field}" on model "${model.name}" is a relation \u2014 not valid in ${context} here (relation writes use nested-write verbs, relation reads use select/include)`,
295
453
  meta: { model: model.name, field, context }
296
454
  });
297
455
  }
298
- throw new VibeError2({
456
+ throw new VibeError4({
299
457
  code: "VIBE_VALIDATION",
300
458
  message: `unknown field "${field}" on model "${model.name}" in ${context}`,
301
459
  meta: { model: model.name, field, context }
@@ -305,7 +463,7 @@ function getFieldMeta(params) {
305
463
  }
306
464
  // packages/runtime/src/codecs.ts
307
465
  import { JSON_TRANSPORT, projectionOutputName, parsePgArrayLiteral, toPgArrayLiteral } from "@vibeorm/sql";
308
- import { VibeError as VibeError3, isEnumType as isEnumType2 } from "@vibeorm/schema";
466
+ import { VibeError as VibeError5, isEnumType as isEnumType3 } from "@vibeorm/schema";
309
467
  import { DIALECT_CODECS, decodeIsNoop } from "@vibeorm/sql";
310
468
  import {
311
469
  DIALECT_CODECS as DIALECT_CODECS2,
@@ -317,7 +475,7 @@ import {
317
475
  function getCodec(params) {
318
476
  const { dialect, type } = params;
319
477
  const table = DIALECT_CODECS[dialect];
320
- return isEnumType2(type) ? table.String : table[type];
478
+ return isEnumType3(type) ? table.String : table[type];
321
479
  }
322
480
  function fieldCodec(params) {
323
481
  const { dialect, field } = params;
@@ -357,7 +515,7 @@ var DECIMAL_LITERAL = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
357
515
  function toRealNumber(params) {
358
516
  const { field, value } = params;
359
517
  const refuse = () => {
360
- throw new VibeError3({
518
+ throw new VibeError5({
361
519
  code: "VIBE_VALIDATION",
362
520
  message: `"${String(value)}" is not a numeric value for the Decimal field "${field.name}"`,
363
521
  meta: { field: field.name, value }
@@ -426,20 +584,20 @@ function wirePlanKey(wire) {
426
584
  }
427
585
  function fieldDecoder(params) {
428
586
  const { dialect, field, wire } = params;
429
- const decode = fieldCodec({ dialect, field }).decode;
587
+ const decode2 = fieldCodec({ dialect, field }).decode;
430
588
  if (isPostgresEnumList({ dialect, field })) {
431
589
  return (value) => decodeValue({ dialect, field, value });
432
590
  }
433
- if (decode === IDENTITY_DECODE)
591
+ if (decode2 === IDENTITY_DECODE)
434
592
  return null;
435
593
  if (field.isList) {
436
594
  if (field.scalarType === "Json" && decodeIsNoop({ scalarType: "Json", wire }))
437
595
  return null;
438
- return (value) => Array.isArray(value) ? value.map((element) => element === null || element === undefined ? element : decode(element)) : decode(value);
596
+ return (value) => Array.isArray(value) ? value.map((element) => element === null || element === undefined ? element : decode2(element)) : decode2(value);
439
597
  }
440
598
  if (field.scalarType !== null && decodeIsNoop({ scalarType: field.scalarType, wire }))
441
599
  return null;
442
- return decode;
600
+ return decode2;
443
601
  }
444
602
  function buildDecodePlan(params) {
445
603
  const { dialect, model, wire } = params;
@@ -447,12 +605,12 @@ function buildDecodePlan(params) {
447
605
  const inPlace = [];
448
606
  const renamedColumns = [];
449
607
  for (const field of model.fields) {
450
- const decode = fieldDecoder({ dialect, field, ...wire === undefined ? {} : { wire } });
451
- byColumn.set(field.columnName, { name: field.name, decode });
608
+ const decode2 = fieldDecoder({ dialect, field, ...wire === undefined ? {} : { wire } });
609
+ byColumn.set(field.columnName, { name: field.name, decode: decode2 });
452
610
  if (field.columnName !== field.name)
453
611
  renamedColumns.push(field.columnName);
454
- if (decode !== null)
455
- inPlace.push({ column: field.columnName, decode });
612
+ if (decode2 !== null)
613
+ inPlace.push({ column: field.columnName, decode: decode2 });
456
614
  }
457
615
  return { byColumn, renamedColumns, decoders: inPlace, inPlace: renamedColumns.length > 0 ? null : inPlace };
458
616
  }
@@ -579,12 +737,12 @@ function getJsonDecodePlan(model) {
579
737
  const inPlace = [];
580
738
  const renamedColumns = [];
581
739
  for (const field of model.fields) {
582
- const decode = field.scalarType === "Json" ? null : field.scalarType === "Bytes" && !field.isList ? decodeBase64Bytes : fieldDecoder({ dialect: "postgres", field });
583
- byColumn.set(field.columnName, { name: field.name, decode });
740
+ const decode2 = field.scalarType === "Json" ? null : field.scalarType === "Bytes" && !field.isList ? decodeBase64Bytes : fieldDecoder({ dialect: "postgres", field });
741
+ byColumn.set(field.columnName, { name: field.name, decode: decode2 });
584
742
  if (field.columnName !== field.name)
585
743
  renamedColumns.push(field.columnName);
586
- if (decode !== null)
587
- inPlace.push({ column: field.columnName, decode });
744
+ if (decode2 !== null)
745
+ inPlace.push({ column: field.columnName, decode: decode2 });
588
746
  }
589
747
  plan = { byColumn, renamedColumns, decoders: inPlace, inPlace: renamedColumns.length > 0 ? null : inPlace };
590
748
  jsonDecodePlanCache.set(model, plan);
@@ -597,7 +755,7 @@ function positionalMaterializer(params) {
597
755
  const output = projectionOutputName({ column });
598
756
  const entry = plan.byColumn.get(output);
599
757
  if (entry === undefined)
600
- throw new VibeError3({ code: "VIBE_INTERNAL", message: "positional projection has no schema field", meta: { model: model.name, column: output } });
758
+ throw new VibeError5({ code: "VIBE_INTERNAL", message: "positional projection has no schema field", meta: { model: model.name, column: output } });
601
759
  return entry;
602
760
  });
603
761
  const width = slots.length;
@@ -605,7 +763,7 @@ function positionalMaterializer(params) {
605
763
  const chunked = width > chunkSize;
606
764
  const count = Math.ceil(width / chunkSize);
607
765
  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 } });
766
+ throw new VibeError5({ code: "VIBE_INTERNAL", message: "positional relation payload does not match its SQL projection", meta: { model: model.name, expectedColumns: width } });
609
767
  };
610
768
  if (width === 0 || new Set(slots.map((slot) => slot.name)).size !== width)
611
769
  return invalid();
@@ -647,7 +805,7 @@ function decodeAggregateValue(params) {
647
805
  return field === null ? value : decodeValue({ dialect, field, value });
648
806
  }
649
807
  // packages/runtime/src/lateral-projection.ts
650
- import { VibeError as VibeError4, unsupportedCapability } from "@vibeorm/schema";
808
+ import { VibeError as VibeError6, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
651
809
  function isPlainObject(value) {
652
810
  return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
653
811
  }
@@ -664,7 +822,7 @@ function lateralProjection(params) {
664
822
  const columns = names.map((name) => {
665
823
  const field = model.fieldByName.get(name);
666
824
  if (field === undefined)
667
- throw new VibeError4({ code: "VIBE_INTERNAL", message: "lateral projection references an unknown field", meta: { model: model.name, field: name } });
825
+ throw new VibeError6({ code: "VIBE_INTERNAL", message: "lateral projection references an unknown field", meta: { model: model.name, field: name } });
668
826
  return Object.freeze(applyLateralTransform({ model, column: { table: LATERAL_CHILD_ALIAS, name: field.columnName } }));
669
827
  });
670
828
  const descriptor = Object.freeze({ columns: Object.freeze(columns), hiddenOrder: Object.freeze(hiddenOrder) });
@@ -702,7 +860,7 @@ function applyLateralTransform(params) {
702
860
  if (scalar !== "BigInt" && scalar !== "Decimal" && scalar !== "Bytes")
703
861
  return column;
704
862
  if (field.isList) {
705
- throw unsupportedCapability({
863
+ throw unsupportedCapability2({
706
864
  dialect: "postgres",
707
865
  feature: `join-strategy relation loading of ${model.name}.${field.name} (${scalar}[])`,
708
866
  hint: 'list columns of BigInt/Decimal/Bytes have no faithful JSON wire form \u2014 use the portable "query" relation strategy'
@@ -712,7 +870,8 @@ function applyLateralTransform(params) {
712
870
  }
713
871
 
714
872
  // packages/runtime/src/query-builder.ts
715
- import { VibeError as VibeError8, unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
873
+ import { isSqlFragment, mapConflictColumns } from "@vibeorm/sql";
874
+ import { VibeError as VibeError10, unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
716
875
  import {
717
876
  SQL_DEFAULT_CELL,
718
877
  containsPattern,
@@ -723,11 +882,12 @@ import {
723
882
  jsonParamCell,
724
883
  startsWithPattern
725
884
  } from "@vibeorm/sql";
885
+ import { describeRowLock } from "@vibeorm/sql";
726
886
 
727
887
  // packages/runtime/src/bulk-upsert.ts
728
- import { VibeError as VibeError5, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
729
- function validationError(params) {
730
- return new VibeError5({
888
+ import { VibeError as VibeError7, unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
889
+ function validationError2(params) {
890
+ return new VibeError7({
731
891
  code: "VIBE_VALIDATION",
732
892
  message: params.message,
733
893
  ...params.meta === undefined ? {} : { meta: params.meta }
@@ -767,7 +927,7 @@ function uniqueGroupsOf(params) {
767
927
  function resolveUpsertConflictTarget(params) {
768
928
  const { model, target } = params;
769
929
  if (target === undefined) {
770
- throw validationError({
930
+ throw validationError2({
771
931
  message: `upsertMany on ${model.clientName} needs an explicit conflictTarget \u2014 the unique group that decides which stored row an incoming row replaces`,
772
932
  meta: { model: model.name }
773
933
  });
@@ -775,7 +935,7 @@ function resolveUpsertConflictTarget(params) {
775
935
  if (typeof target === "object" && target !== null && !Array.isArray(target) && typeof target.constraint === "string") {
776
936
  const constraint = target.constraint;
777
937
  if (constraint.length === 0 || Object.keys(target).length !== 1) {
778
- throw validationError({
938
+ throw validationError2({
779
939
  message: `conflictTarget on ${model.clientName}.upsertMany() must be a field-name array or { constraint: string }`,
780
940
  meta: { model: model.name }
781
941
  });
@@ -783,7 +943,7 @@ function resolveUpsertConflictTarget(params) {
783
943
  return { kind: "constraint", constraint };
784
944
  }
785
945
  if (!Array.isArray(target) || target.length === 0 || target.some((name) => typeof name !== "string")) {
786
- throw validationError({
946
+ throw validationError2({
787
947
  message: `conflictTarget on ${model.clientName}.upsertMany() must be a non-empty field-name array or { constraint: string }`,
788
948
  meta: { model: model.name }
789
949
  });
@@ -792,7 +952,7 @@ function resolveUpsertConflictTarget(params) {
792
952
  for (const name of target) {
793
953
  const field = model.fieldByName.get(name);
794
954
  if (field === undefined) {
795
- throw validationError({
955
+ throw validationError2({
796
956
  message: `unknown field "${name}" in conflictTarget on ${model.clientName}.upsertMany()`,
797
957
  meta: { model: model.name, field: name }
798
958
  });
@@ -801,7 +961,7 @@ function resolveUpsertConflictTarget(params) {
801
961
  }
802
962
  const signature = [...fields.map((field) => field.columnName)].sort((left, right) => left < right ? -1 : 1).join("\x00");
803
963
  if (!uniqueGroupSignatures({ model }).has(signature)) {
804
- throw validationError({
964
+ throw validationError2({
805
965
  message: `conflictTarget [${fields.map((field) => field.name).join(", ")}] on ${model.clientName}.upsertMany() is not a declared unique group of ${model.name} \u2014 an arbiter without a unique index cannot decide which row an incoming row replaces`,
806
966
  meta: { model: model.name, fields: fields.map((field) => field.name) }
807
967
  });
@@ -815,7 +975,7 @@ function assertConflictTargetIsEnforceable(params) {
815
975
  const groups = uniqueGroupSignatures({ model });
816
976
  if (groups.size <= 1)
817
977
  return;
818
- throw unsupportedCapability2({
978
+ throw unsupportedCapability3({
819
979
  dialect: dialect.name,
820
980
  feature: `an enforced conflict target on ${model.clientName}.upsertMany() (${model.name} declares ${groups.size} unique groups)`,
821
981
  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"
@@ -824,7 +984,7 @@ function assertConflictTargetIsEnforceable(params) {
824
984
  function resolveUpsertUpdateFields(params) {
825
985
  const { model, update, targetColumns, skipUpdatedAt } = params;
826
986
  if (!Array.isArray(update) || update.length === 0 || update.some((name) => typeof name !== "string")) {
827
- throw validationError({
987
+ throw validationError2({
828
988
  message: `update on ${model.clientName}.upsertMany() must be a non-empty array of field names \u2014 the columns a conflicting row takes from the incoming row`,
829
989
  meta: { model: model.name }
830
990
  });
@@ -834,19 +994,19 @@ function resolveUpsertUpdateFields(params) {
834
994
  for (const name of update) {
835
995
  const field = model.fieldByName.get(name);
836
996
  if (field === undefined) {
837
- throw validationError({
997
+ throw validationError2({
838
998
  message: `unknown field "${name}" in update on ${model.clientName}.upsertMany()`,
839
999
  meta: { model: model.name, field: name }
840
1000
  });
841
1001
  }
842
1002
  if (targetColumns.has(field.columnName)) {
843
- throw validationError({
1003
+ throw validationError2({
844
1004
  message: `update on ${model.clientName}.upsertMany() cannot rewrite "${field.name}": it is part of the conflict target, the value that decides which row is being replaced`,
845
1005
  meta: { model: model.name, field: field.name }
846
1006
  });
847
1007
  }
848
1008
  if (model.policy !== undefined && model.policy.field === field.name) {
849
- throw validationError({
1009
+ throw validationError2({
850
1010
  message: `update on ${model.clientName}.upsertMany() cannot rewrite "${field.name}": it is ${model.name}'s tenant field (@@policy), and a batch that moves rows between tenants is never what an ingestion feed meant`,
851
1011
  meta: { model: model.name, field: field.name }
852
1012
  });
@@ -873,7 +1033,7 @@ function assertUpsertRowsBindColumns(params) {
873
1033
  for (const column of targetColumns) {
874
1034
  if (column in row)
875
1035
  continue;
876
- throw validationError({
1036
+ throw validationError2({
877
1037
  message: `row ${index} of ${model.clientName}.upsertMany() does not set conflict-target column "${column}" \u2014 an incoming row without the arbiter's value cannot replace anything`,
878
1038
  meta: { model: model.name, row: index, column }
879
1039
  });
@@ -881,7 +1041,7 @@ function assertUpsertRowsBindColumns(params) {
881
1041
  for (const field of updateFields) {
882
1042
  if (field.columnName in row)
883
1043
  continue;
884
- throw validationError({
1044
+ throw validationError2({
885
1045
  message: `row ${index} of ${model.clientName}.upsertMany() does not set "${field.name}", which the update list overwrites \u2014 the conflict arm would write the column default over a stored value; give every row a value or drop "${field.name}" from update`,
886
1046
  meta: { model: model.name, row: index, field: field.name }
887
1047
  });
@@ -913,7 +1073,7 @@ function assertNoDuplicateConflictKeys(params) {
913
1073
  const key = conflictKeyOf({ row: compiled[index], columns: targetColumns });
914
1074
  const first = firstIndexByKey.get(key);
915
1075
  if (first !== undefined) {
916
- throw validationError({
1076
+ throw validationError2({
917
1077
  message: `${model.clientName}.upsertMany() got the same conflict key twice \u2014 rows ${first} and ${index} both address (${targetColumns.join(", ")}); deduplicate the feed before submitting it, because the databases disagree about what one statement writing a key twice means (postgres refuses it, mysql keeps the last one)`,
918
1078
  meta: { model: model.name, rows: [first, index], columns: [...targetColumns] }
919
1079
  });
@@ -938,7 +1098,7 @@ function assertNoDuplicateSecondaryUniques(params) {
938
1098
  const key = conflictKeyOf({ row, columns });
939
1099
  const first = firstIndexByKey.get(key);
940
1100
  if (first !== undefined) {
941
- throw unsupportedCapability2({
1101
+ throw unsupportedCapability3({
942
1102
  dialect: dialect.name,
943
1103
  feature: `${model.clientName}.upsertMany() with rows ${first} and ${index} carrying the same ${fields.map((field) => field.name).join(" + ")}`,
944
1104
  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`
@@ -978,7 +1138,7 @@ function assertConflictKeysFit(params) {
978
1138
  const length = typeof value === "string" ? Array.from(value).length : value instanceof Uint8Array ? value.byteLength : 0;
979
1139
  if (length <= capacity)
980
1140
  continue;
981
- throw validationError({
1141
+ throw validationError2({
982
1142
  message: `${params.model.clientName}.upsertMany() row ${index} conflict key "${field.name}" exceeds its column capacity ${capacity}. The database can truncate it and merge distinct input keys; shorten the value or migrate to a wider column.`,
983
1143
  meta: { model: params.model.name, field: field.name, row: index, capacity }
984
1144
  });
@@ -1020,7 +1180,7 @@ function assertConflictKeysAreComparable(params) {
1020
1180
  if (target.kind === "constraint") {
1021
1181
  if (engineComparesEveryPair)
1022
1182
  return;
1023
- throw unsupportedCapability2({
1183
+ throw unsupportedCapability3({
1024
1184
  dialect: dialect.name,
1025
1185
  feature: `a ${statementCount > 1 ? "chunked " : ""}${model.clientName}.upsertMany() batch arbitrated by constraint "${target.constraint}"`,
1026
1186
  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"
@@ -1038,7 +1198,7 @@ function assertConflictKeysAreComparable(params) {
1038
1198
  notes.push(`@collation("${field.collation}")`);
1039
1199
  return notes.length === 0 ? `${field.name} (column "${field.columnName}")` : `${field.name} (column "${field.columnName}", ${notes.join(", ")})`;
1040
1200
  }).join(", ");
1041
- throw unsupportedCapability2({
1201
+ throw unsupportedCapability3({
1042
1202
  dialect: dialect.name,
1043
1203
  feature: `${model.clientName}.upsertMany() on conflict target ${described}`,
1044
1204
  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 })}`
@@ -1046,7 +1206,7 @@ function assertConflictKeysAreComparable(params) {
1046
1206
  }
1047
1207
 
1048
1208
  // packages/runtime/src/keyset.ts
1049
- import { VibeError as VibeError6 } from "@vibeorm/schema";
1209
+ import { VibeError as VibeError8 } from "@vibeorm/schema";
1050
1210
  var KEYSET_TOKEN_VERSION = 1;
1051
1211
  var KEYSET_TOKEN_PREFIX = "vk1.";
1052
1212
  var KEYSET_MAX_PAGE_SIZE = 1e4;
@@ -1056,10 +1216,10 @@ var DATETIME_NEEDS_PRECISION_ASSERTION = Object.freeze({
1056
1216
  sqlite: false
1057
1217
  });
1058
1218
  function keysetValidation(params) {
1059
- return new VibeError6({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1219
+ return new VibeError8({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1060
1220
  }
1061
1221
  function keysetUnsupported(params) {
1062
- return new VibeError6({
1222
+ return new VibeError8({
1063
1223
  code: "VIBE_UNSUPPORTED_CAPABILITY",
1064
1224
  message: params.message,
1065
1225
  meta: params.meta
@@ -1442,9 +1602,9 @@ function decodeKeysetToken(params) {
1442
1602
  }
1443
1603
 
1444
1604
  // packages/runtime/src/relation-plan.ts
1445
- import { VibeError as VibeError7, implicitJoinTable } from "@vibeorm/schema";
1446
- function validationError2(params) {
1447
- return new VibeError7({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1605
+ import { VibeError as VibeError9, implicitJoinTable } from "@vibeorm/schema";
1606
+ function validationError3(params) {
1607
+ return new VibeError9({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1448
1608
  }
1449
1609
  function isPlainObject2(value) {
1450
1610
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
@@ -1461,7 +1621,7 @@ function findReverseRelation(params) {
1461
1621
  function keyFields(params) {
1462
1622
  const { model, names, relation, what } = params;
1463
1623
  if (names === undefined || names.length === 0) {
1464
- throw validationError2({
1624
+ throw validationError3({
1465
1625
  message: `relation "${relation.name}" on ${relation.model} has no ${what} \u2014 the schema cannot be loaded through`,
1466
1626
  meta: { model: relation.model, relation: relation.name }
1467
1627
  });
@@ -1469,7 +1629,7 @@ function keyFields(params) {
1469
1629
  return names.map((name) => {
1470
1630
  const field = model.fieldByName.get(name);
1471
1631
  if (field === undefined) {
1472
- throw new VibeError7({
1632
+ throw new VibeError9({
1473
1633
  code: "VIBE_SCHEMA_INVALID",
1474
1634
  message: `relation "${relation.name}" on ${relation.model} names unknown ${what} field "${name}"`,
1475
1635
  meta: { model: relation.model, relation: relation.name }
@@ -1481,7 +1641,7 @@ function keyFields(params) {
1481
1641
  function singleKeyPair(params) {
1482
1642
  const { link, what } = params;
1483
1643
  if (link.parentKeys.length !== 1 || link.childKeys.length !== 1) {
1484
- throw new VibeError7({
1644
+ throw new VibeError9({
1485
1645
  code: "VIBE_UNSUPPORTED_CAPABILITY",
1486
1646
  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`,
1487
1647
  meta: { model: link.relation.model, relation: link.relation.name, arity: link.parentKeys.length }
@@ -1493,7 +1653,7 @@ function singlePrimaryKey(params) {
1493
1653
  const { model, relation } = params;
1494
1654
  const pk = model.primaryKey;
1495
1655
  if (pk.length !== 1) {
1496
- throw validationError2({
1656
+ throw validationError3({
1497
1657
  message: `implicit many-to-many relation "${relation.name}" needs a single-column primary key on ${model.name}`,
1498
1658
  meta: { model: model.name, relation: relation.name }
1499
1659
  });
@@ -1520,7 +1680,7 @@ function resolveRelationLinkUncached(params) {
1520
1680
  const target = getModelMeta({ meta, model: relation.targetModel });
1521
1681
  if (relation.type === "manyToMany") {
1522
1682
  if (relation.model === relation.targetModel) {
1523
- throw new VibeError7({
1683
+ throw new VibeError9({
1524
1684
  code: "VIBE_SCHEMA_INVALID",
1525
1685
  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.`,
1526
1686
  meta: { model: relation.model, relation: relation.name, targetModel: relation.targetModel }
@@ -1562,7 +1722,7 @@ function resolveRelationLinkUncached(params) {
1562
1722
  }
1563
1723
  const reverse = findReverseRelation({ relation, target });
1564
1724
  if (reverse === null || reverse.fields === undefined || reverse.fields.length === 0) {
1565
- throw new VibeError7({
1725
+ throw new VibeError9({
1566
1726
  code: "VIBE_SCHEMA_INVALID",
1567
1727
  message: `relation "${relation.name}" on ${relation.model} has no foreign-key counterpart on ${relation.targetModel}`,
1568
1728
  meta: { model: relation.model, relation: relation.name, target: relation.targetModel }
@@ -1587,7 +1747,7 @@ function pairedKeys(params) {
1587
1747
  const parentKeys = keyFields({ model, names: parentNames, relation, what: "fields" });
1588
1748
  const childKeys = keyFields({ model: target, names: childNames, relation, what: "references" });
1589
1749
  if (parentKeys.length !== childKeys.length) {
1590
- throw new VibeError7({
1750
+ throw new VibeError9({
1591
1751
  code: "VIBE_SCHEMA_INVALID",
1592
1752
  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`,
1593
1753
  meta: { model: relation.model, relation: relation.name }
@@ -1600,7 +1760,7 @@ var TO_ONE_ARG_KEYS = ["where", "select", "include", "omit"];
1600
1760
  function toNonNegativeInteger(params) {
1601
1761
  const { value, what, relation } = params;
1602
1762
  if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
1603
- throw validationError2({
1763
+ throw validationError3({
1604
1764
  message: `${what} on relation "${relation}" must be a non-negative integer`,
1605
1765
  meta: { relation, argument: what, value }
1606
1766
  });
@@ -1617,7 +1777,7 @@ function compileChildSelect(params) {
1617
1777
  continue;
1618
1778
  if (model.computed !== null && model.computed.has(name)) {
1619
1779
  if (value !== true) {
1620
- throw validationError2({
1780
+ throw validationError3({
1621
1781
  message: `select.${name} must be true or false on model "${model.name}"`,
1622
1782
  meta: { model: model.name, field: name }
1623
1783
  });
@@ -1626,13 +1786,13 @@ function compileChildSelect(params) {
1626
1786
  }
1627
1787
  const field = model.fieldByName.get(name);
1628
1788
  if (field === undefined) {
1629
- throw validationError2({
1789
+ throw validationError3({
1630
1790
  message: `unknown field "${name}" on model "${model.name}" in select`,
1631
1791
  meta: { model: model.name, field: name }
1632
1792
  });
1633
1793
  }
1634
1794
  if (value !== true) {
1635
- throw validationError2({
1795
+ throw validationError3({
1636
1796
  message: `select.${name} must be true or false on model "${model.name}"`,
1637
1797
  meta: { model: model.name, field: name }
1638
1798
  });
@@ -1644,7 +1804,7 @@ function compileChildSelect(params) {
1644
1804
  function compileChildOmit(params) {
1645
1805
  const { model, omit, relation } = params;
1646
1806
  if (!isPlainObject2(omit)) {
1647
- throw validationError2({
1807
+ throw validationError3({
1648
1808
  message: `omit on relation "${relation}" must be an object`,
1649
1809
  meta: { model: model.name, relation }
1650
1810
  });
@@ -1654,13 +1814,13 @@ function compileChildOmit(params) {
1654
1814
  if (value === undefined || value === false)
1655
1815
  continue;
1656
1816
  if (value !== true) {
1657
- throw validationError2({
1817
+ throw validationError3({
1658
1818
  message: `omit.${name} must be true or false on model "${model.name}"`,
1659
1819
  meta: { model: model.name, relation, field: name }
1660
1820
  });
1661
1821
  }
1662
1822
  if (model.relationByName.has(name)) {
1663
- throw validationError2({
1823
+ throw validationError3({
1664
1824
  message: `omit.${name} on model "${model.name}" \u2014 omit takes scalar fields; a relation is excluded by not including it`,
1665
1825
  meta: { model: model.name, relation, field: name }
1666
1826
  });
@@ -1668,7 +1828,7 @@ function compileChildOmit(params) {
1668
1828
  if (model.computed !== null && model.computed.has(name))
1669
1829
  continue;
1670
1830
  if (!model.fieldByName.has(name)) {
1671
- throw validationError2({
1831
+ throw validationError3({
1672
1832
  message: `unknown field "${name}" on model "${model.name}" in omit`,
1673
1833
  meta: { model: model.name, relation, field: name }
1674
1834
  });
@@ -1677,7 +1837,7 @@ function compileChildOmit(params) {
1677
1837
  }
1678
1838
  const kept = model.fields.filter((field) => !omitted.has(field.name)).map((field) => field.name);
1679
1839
  if (kept.length === 0) {
1680
- throw validationError2({
1840
+ throw validationError3({
1681
1841
  message: `omit on relation "${relation}" removes every field of model "${model.name}"`,
1682
1842
  meta: { model: model.name, relation }
1683
1843
  });
@@ -1702,14 +1862,14 @@ function resolveCountNode(params) {
1702
1862
  const { meta, model, name, where } = params;
1703
1863
  const relation = model.relationByName.get(name);
1704
1864
  if (relation === undefined || !relation.isList) {
1705
- throw validationError2({
1865
+ throw validationError3({
1706
1866
  message: `_count.select.${name} on model "${model.name}" must name a list relation`,
1707
1867
  meta: { model: model.name, relation: name }
1708
1868
  });
1709
1869
  }
1710
1870
  const link = resolveRelationLink({ meta, model, relation });
1711
1871
  if (where !== undefined && link.target.policy !== undefined) {
1712
- throw validationError2({
1872
+ throw validationError3({
1713
1873
  message: `_count of ${model.name}.${name} cannot take a filter because ${link.target.name} declares @@policy \u2014 a filtered count would not apply the policy predicate; count the relation with a scoped child query instead`,
1714
1874
  meta: { model: model.name, relation: name }
1715
1875
  });
@@ -1722,7 +1882,7 @@ function resolveCounts(params) {
1722
1882
  return model.relations.filter((relation) => relation.isList).map((relation) => resolveCountNode({ meta, model, name: relation.name, where: undefined }));
1723
1883
  }
1724
1884
  if (!isPlainObject2(spec) || !isPlainObject2(spec.select)) {
1725
- throw validationError2({
1885
+ throw validationError3({
1726
1886
  message: `_count on model "${model.name}" must be true or { select: { relation: true } }`,
1727
1887
  meta: { model: model.name }
1728
1888
  });
@@ -1736,7 +1896,7 @@ function resolveCounts(params) {
1736
1896
  continue;
1737
1897
  }
1738
1898
  if (!isPlainObject2(value)) {
1739
- throw validationError2({
1899
+ throw validationError3({
1740
1900
  message: `_count.select.${name} on model "${model.name}" must be true, false, or { where: \u2026 }`,
1741
1901
  meta: { model: model.name, relation: name }
1742
1902
  });
@@ -1745,7 +1905,7 @@ function resolveCounts(params) {
1745
1905
  if (value[key] === undefined)
1746
1906
  continue;
1747
1907
  if (!COUNT_ENTRY_KEYS.includes(key)) {
1748
- throw validationError2({
1908
+ throw validationError3({
1749
1909
  message: `_count.select.${name} on model "${model.name}" takes only "where" \u2014 got "${key}"`,
1750
1910
  meta: { model: model.name, relation: name, argument: key }
1751
1911
  });
@@ -1753,7 +1913,7 @@ function resolveCounts(params) {
1753
1913
  }
1754
1914
  const where = value.where;
1755
1915
  if (where !== undefined && !isPlainObject2(where)) {
1756
- throw validationError2({
1916
+ throw validationError3({
1757
1917
  message: `_count.select.${name}.where on model "${model.name}" must be an object`,
1758
1918
  meta: { model: model.name, relation: name }
1759
1919
  });
@@ -1773,27 +1933,27 @@ function resolveNode(params) {
1773
1933
  if (raw[key] === undefined)
1774
1934
  continue;
1775
1935
  if (!allowed.includes(key)) {
1776
- throw validationError2({
1936
+ throw validationError3({
1777
1937
  message: `unsupported argument "${key}" on relation "${relation.name}" \u2014 supported: ${allowed.join(", ")}`,
1778
1938
  meta: { model: model.name, relation: relation.name, argument: key }
1779
1939
  });
1780
1940
  }
1781
1941
  }
1782
1942
  if (raw.select !== undefined && raw.include !== undefined) {
1783
- throw validationError2({
1943
+ throw validationError3({
1784
1944
  message: `relation "${relation.name}" cannot use select and include together`,
1785
1945
  meta: { model: model.name, relation: relation.name }
1786
1946
  });
1787
1947
  }
1788
1948
  if (raw.select !== undefined && raw.omit !== undefined) {
1789
- throw validationError2({
1949
+ throw validationError3({
1790
1950
  message: `relation "${relation.name}" cannot use select and omit together \u2014 select names fields to keep, omit fields to drop`,
1791
1951
  meta: { model: model.name, relation: relation.name }
1792
1952
  });
1793
1953
  }
1794
1954
  const where = raw.where === undefined ? undefined : (() => {
1795
1955
  if (!isPlainObject2(raw.where)) {
1796
- throw validationError2({
1956
+ throw validationError3({
1797
1957
  message: `where on relation "${relation.name}" must be an object`,
1798
1958
  meta: { model: model.name, relation: relation.name }
1799
1959
  });
@@ -1865,13 +2025,13 @@ function resolveRelationSelection(params) {
1865
2025
  continue;
1866
2026
  if (!model.relationByName.has(key)) {
1867
2027
  const isScalar = model.fieldByName.has(key);
1868
- throw validationError2({
2028
+ throw validationError3({
1869
2029
  message: isScalar ? `"${key}" on model "${model.name}" is a scalar field \u2014 include takes relations, use select for scalars` : `unknown relation "${key}" on model "${model.name}" in include`,
1870
2030
  meta: { model: model.name, relation: key }
1871
2031
  });
1872
2032
  }
1873
2033
  if (value !== true && !isPlainObject2(value)) {
1874
- throw validationError2({
2034
+ throw validationError3({
1875
2035
  message: `include.${key} on model "${model.name}" must be true or an object`,
1876
2036
  meta: { model: model.name, relation: key }
1877
2037
  });
@@ -1936,8 +2096,8 @@ var AGG_FN_BY_KEY = {
1936
2096
  _min: "min",
1937
2097
  _max: "max"
1938
2098
  };
1939
- function validationError3(params) {
1940
- return new VibeError8({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
2099
+ function validationError4(params) {
2100
+ return new VibeError10({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
1941
2101
  }
1942
2102
  function isPlainObject3(value) {
1943
2103
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
@@ -1957,40 +2117,115 @@ function columnRef(params) {
1957
2117
  const { field, table } = params;
1958
2118
  return table === undefined ? { name: field.columnName } : { table, name: field.columnName };
1959
2119
  }
2120
+ var ROW_LOCK_STRENGTHS = Object.freeze([
2121
+ "update",
2122
+ "noKeyUpdate",
2123
+ "share",
2124
+ "keyShare"
2125
+ ]);
2126
+ var ROW_LOCK_WAIT_POLICIES = Object.freeze([
2127
+ "wait",
2128
+ "nowait",
2129
+ "skipLocked"
2130
+ ]);
2131
+ var DEFAULT_ROW_LOCK = Object.freeze({ strength: "update", wait: "wait" });
2132
+ function readLockRequest(params) {
2133
+ const { value, model, method } = params;
2134
+ const where = `lock on ${model.clientName}.${method}()`;
2135
+ if (value === "update")
2136
+ return DEFAULT_ROW_LOCK;
2137
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2138
+ throw validationError4({
2139
+ message: `${where} must be "update" or { strength, wait? }, got ${JSON.stringify(value) ?? String(value)}`,
2140
+ meta: { model: model.name, method, lock: value }
2141
+ });
2142
+ }
2143
+ const request = value;
2144
+ for (const key of Object.keys(request)) {
2145
+ if (key === "strength" || key === "wait")
2146
+ continue;
2147
+ throw validationError4({
2148
+ message: `${where} has no "${key}" member \u2014 a lock is { strength, wait? }, where wait is ` + `one of ${ROW_LOCK_WAIT_POLICIES.map((policy) => `"${policy}"`).join(" | ")} ` + `(one policy, never several flags)`,
2149
+ meta: { model: model.name, method, member: key }
2150
+ });
2151
+ }
2152
+ const strength = request.strength;
2153
+ if (typeof strength !== "string" || !ROW_LOCK_STRENGTHS.includes(strength)) {
2154
+ throw validationError4({
2155
+ message: `${where} needs strength: ${ROW_LOCK_STRENGTHS.map((item) => `"${item}"`).join(" | ")}, got ${String(strength)}`,
2156
+ meta: { model: model.name, method, strength }
2157
+ });
2158
+ }
2159
+ const wait = request.wait;
2160
+ if (wait !== undefined && (typeof wait !== "string" || !ROW_LOCK_WAIT_POLICIES.includes(wait))) {
2161
+ throw validationError4({
2162
+ message: `${where} needs wait: ${ROW_LOCK_WAIT_POLICIES.map((item) => `"${item}"`).join(" | ")}, got ${String(wait)}`,
2163
+ meta: { model: model.name, method, wait }
2164
+ });
2165
+ }
2166
+ return {
2167
+ strength,
2168
+ wait: wait === undefined ? "wait" : wait
2169
+ };
2170
+ }
1960
2171
  function compileLock(params) {
1961
- const { args, model, method, hasRelations } = params;
2172
+ const { dialect, args, model, method, hasRelations } = params;
1962
2173
  if (args.lock === undefined)
1963
- return false;
2174
+ return null;
1964
2175
  if (model.isView) {
1965
- throw validationError3({
2176
+ throw validationError4({
1966
2177
  message: `lock on ${model.clientName}.${method}() \u2014 a view row cannot be locked; lock the underlying table's rows instead`,
1967
2178
  meta: { model: model.name, method }
1968
2179
  });
1969
2180
  }
1970
- if (args.lock !== "update") {
1971
- throw validationError3({
1972
- message: `lock on ${model.clientName}.${method}() must be "update", got ${String(args.lock)}`,
1973
- meta: { model: model.name, method, lock: args.lock }
1974
- });
1975
- }
2181
+ const lock = readLockRequest({ value: args.lock, model, method });
1976
2182
  if (args.distinct !== undefined) {
1977
- throw validationError3({
2183
+ throw validationError4({
1978
2184
  message: `lock cannot combine with distinct on ${model.clientName}.${method}() \u2014 postgres rejects FOR UPDATE with DISTINCT`,
1979
2185
  meta: { model: model.name, method }
1980
2186
  });
1981
2187
  }
1982
2188
  if (hasRelations) {
1983
- throw validationError3({
2189
+ throw validationError4({
1984
2190
  message: `lock cannot combine with relation loading on ${model.clientName}.${method}() \u2014 lock the parent row alone, then read relations inside the same transaction`,
1985
2191
  meta: { model: model.name, method }
1986
2192
  });
1987
2193
  }
1988
- return true;
2194
+ assertLockIsExpressible({ dialect, lock, model, method });
2195
+ return lock;
2196
+ }
2197
+ function assertLockIsExpressible(params) {
2198
+ const { dialect, lock, model, method } = params;
2199
+ const { rowLockStrengths, rowLockWaitPolicies } = dialect.capabilities;
2200
+ const site = `${describeRowLock({ lock })} on ${model.clientName}.${method}()`;
2201
+ if (rowLockStrengths.length === 0) {
2202
+ if (lock.strength === "update" && lock.wait === "wait")
2203
+ return;
2204
+ throw unsupportedCapability4({
2205
+ dialect: dialect.name,
2206
+ feature: site,
2207
+ hint: `${dialect.name} has no locking-read clause at all. Only lock: "update" is accepted there, ` + `as the documented no-op its single-writer transactions already provide`
2208
+ });
2209
+ }
2210
+ if (!rowLockStrengths.includes(lock.strength)) {
2211
+ throw unsupportedCapability4({
2212
+ dialect: dialect.name,
2213
+ feature: site,
2214
+ hint: `${dialect.name} has these lock strengths: ${rowLockStrengths.join(", ")}`
2215
+ });
2216
+ }
2217
+ if (!rowLockWaitPolicies.includes(lock.wait)) {
2218
+ throw unsupportedCapability4({
2219
+ dialect: dialect.name,
2220
+ feature: site,
2221
+ hint: `${dialect.name} has these lock wait policies: ${rowLockWaitPolicies.join(", ")}`
2222
+ });
2223
+ }
1989
2224
  }
1990
2225
  function compileConflictTarget(params) {
1991
2226
  const { dialect, meta, model, method, target, where } = params;
1992
2227
  if ((target !== undefined || where !== undefined) && dialect.doNothingForm === "insertIgnore") {
1993
- throw unsupportedCapability3({
2228
+ throw unsupportedCapability4({
1994
2229
  dialect: dialect.name,
1995
2230
  feature: `a conflict target on ${model.clientName}.${method}()`,
1996
2231
  hint: "mysql's INSERT IGNORE / ON DUPLICATE KEY fire on ANY duplicate key \u2014 no target can be named; gate the call per dialect"
@@ -2000,7 +2235,7 @@ function compileConflictTarget(params) {
2000
2235
  if (target !== undefined) {
2001
2236
  if (Array.isArray(target)) {
2002
2237
  if (target.length === 0) {
2003
- throw validationError3({
2238
+ throw validationError4({
2004
2239
  message: `onConflict target on ${model.clientName}.${method}() must name at least one field`,
2005
2240
  meta: { model: model.name, method }
2006
2241
  });
@@ -2008,7 +2243,7 @@ function compileConflictTarget(params) {
2008
2243
  out.columns = target.map((name) => getFieldMeta({ model, field: String(name), context: "onConflict target" }).columnName);
2009
2244
  } else if (isPlainObject3(target) && typeof target.constraint === "string" && target.constraint.length > 0 && Object.keys(target).length === 1) {
2010
2245
  if (!dialect.capabilities.conflictTargetConstraint) {
2011
- throw unsupportedCapability3({
2246
+ throw unsupportedCapability4({
2012
2247
  dialect: dialect.name,
2013
2248
  feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
2014
2249
  hint: "only postgres supports ON CONSTRAINT targets \u2014 name the indexed columns instead"
@@ -2016,7 +2251,7 @@ function compileConflictTarget(params) {
2016
2251
  }
2017
2252
  out.constraint = target.constraint;
2018
2253
  } else {
2019
- throw validationError3({
2254
+ throw validationError4({
2020
2255
  message: `onConflict target on ${model.clientName}.${method}() must be a field-name array or { constraint: string }`,
2021
2256
  meta: { model: model.name, method }
2022
2257
  });
@@ -2024,20 +2259,20 @@ function compileConflictTarget(params) {
2024
2259
  }
2025
2260
  if (where !== undefined) {
2026
2261
  if (out.columns === undefined) {
2027
- throw validationError3({
2262
+ throw validationError4({
2028
2263
  message: `onConflict where on ${model.clientName}.${method}() needs a column-list target \u2014 the predicate refines "(columns) WHERE \u2026"`,
2029
2264
  meta: { model: model.name, method }
2030
2265
  });
2031
2266
  }
2032
2267
  if (!dialect.capabilities.partialIndexes) {
2033
- throw unsupportedCapability3({
2268
+ throw unsupportedCapability4({
2034
2269
  dialect: dialect.name,
2035
2270
  feature: "a partial-index conflict target",
2036
2271
  hint: "this dialect has no partial indexes to target"
2037
2272
  });
2038
2273
  }
2039
2274
  if (typeof where !== "string" || where.trim().length === 0) {
2040
- throw validationError3({
2275
+ throw validationError4({
2041
2276
  message: `onConflict where on ${model.clientName}.${method}() must be the partial index's raw predicate string (the schema declaration's where: text)`,
2042
2277
  meta: { model: model.name, method }
2043
2278
  });
@@ -2049,13 +2284,13 @@ function compileConflictTarget(params) {
2049
2284
  function compileDbNowWrite(params) {
2050
2285
  const { model, field, value } = params;
2051
2286
  if (field.scalarType !== "DateTime" || field.isList) {
2052
- throw validationError3({
2287
+ throw validationError4({
2053
2288
  message: `dbNow() writes the database clock into a DateTime field \u2014 ${model.name}.${field.name} is ${String(field.enumName ?? field.scalarType)}${field.isList ? "[]" : ""}`,
2054
2289
  meta: { model: model.name, field: field.name }
2055
2290
  });
2056
2291
  }
2057
2292
  if (field.isId || field.isUnique || model.primaryKey.some((candidate) => candidate.name === field.name)) {
2058
- throw validationError3({
2293
+ throw validationError4({
2059
2294
  message: `dbNow() on ${model.name}.${field.name} \u2014 a key field cannot take a database-computed value; the client could not re-address the written row`,
2060
2295
  meta: { model: model.name, field: field.name }
2061
2296
  });
@@ -2065,7 +2300,7 @@ function compileDbNowWrite(params) {
2065
2300
  function compileDbNowFilter(params) {
2066
2301
  const { model, field, value } = params;
2067
2302
  if (field.scalarType !== "DateTime" || field.isList) {
2068
- throw validationError3({
2303
+ throw validationError4({
2069
2304
  message: `dbNow() compares against a DateTime field \u2014 ${model.name}.${field.name} is ${String(field.enumName ?? field.scalarType)}${field.isList ? "[]" : ""}`,
2070
2305
  meta: { model: model.name, field: field.name }
2071
2306
  });
@@ -2075,7 +2310,7 @@ function compileDbNowFilter(params) {
2075
2310
  function requireObject(params) {
2076
2311
  const { value, what, model } = params;
2077
2312
  if (!isPlainObject3(value)) {
2078
- throw validationError3({
2313
+ throw validationError4({
2079
2314
  message: `${what} must be an object on model "${model.name}"`,
2080
2315
  meta: { model: model.name, argument: what }
2081
2316
  });
@@ -2085,13 +2320,13 @@ function requireObject(params) {
2085
2320
  function toNonNegativeInteger2(params) {
2086
2321
  const { value, what, model } = params;
2087
2322
  if (typeof value !== "number" || !Number.isSafeInteger(value)) {
2088
- throw validationError3({
2323
+ throw validationError4({
2089
2324
  message: `${what} must be an integer on model "${model.name}", got ${String(value)}`,
2090
2325
  meta: { model: model.name, argument: what, value }
2091
2326
  });
2092
2327
  }
2093
2328
  if (value < 0) {
2094
- throw validationError3({
2329
+ throw validationError4({
2095
2330
  message: `negative ${what} is not supported here \u2014 only findMany's take may be negative (backwards pagination)`,
2096
2331
  meta: { model: model.name, argument: what, value }
2097
2332
  });
@@ -2105,7 +2340,7 @@ function assertArgs(params) {
2105
2340
  if (args[key] === undefined)
2106
2341
  continue;
2107
2342
  if (!allowed.includes(key)) {
2108
- throw validationError3({
2343
+ throw validationError4({
2109
2344
  message: `unsupported argument "${key}" for ${model.clientName}.${method}() \u2014 supported: ${allowed.join(", ")}`,
2110
2345
  meta: { model: model.name, method, argument: key, allowed }
2111
2346
  });
@@ -2116,7 +2351,7 @@ function assertListFilterable(params) {
2116
2351
  const { dialect, model, field, operator } = params;
2117
2352
  if (dialect.capabilities.scalarArrays)
2118
2353
  return;
2119
- throw unsupportedCapability3({
2354
+ throw unsupportedCapability4({
2120
2355
  dialect: dialect.name,
2121
2356
  feature: `scalar list filter "${operator}" on ${model.name}.${field.name}`,
2122
2357
  hint: "only postgres has native array columns \u2014 model the list as a related table instead"
@@ -2126,7 +2361,7 @@ function assertListWritable(params) {
2126
2361
  const { dialect, model, field } = params;
2127
2362
  if (!field.isList || dialect.capabilities.scalarArrays)
2128
2363
  return;
2129
- throw unsupportedCapability3({
2364
+ throw unsupportedCapability4({
2130
2365
  dialect: dialect.name,
2131
2366
  feature: `writing the scalar list ${model.name}.${field.name}`,
2132
2367
  hint: "only postgres has native array columns \u2014 model the list as a related table instead"
@@ -2135,7 +2370,7 @@ function assertListWritable(params) {
2135
2370
  function compileStringPattern(params) {
2136
2371
  const { operator, operand, field, model } = params;
2137
2372
  if (typeof operand !== "string") {
2138
- throw validationError3({
2373
+ throw validationError4({
2139
2374
  message: `"${operator}" on ${model.name}.${field.name} expects a string`,
2140
2375
  meta: { model: model.name, field: field.name, operator }
2141
2376
  });
@@ -2162,7 +2397,7 @@ function compileListFieldFilter(params) {
2162
2397
  break;
2163
2398
  }
2164
2399
  if (!Array.isArray(operand)) {
2165
- throw validationError3({
2400
+ throw validationError4({
2166
2401
  message: `"equals" on the list ${model.name}.${field.name} expects an array or null`,
2167
2402
  meta: { model: model.name, field: field.name, operator }
2168
2403
  });
@@ -2186,7 +2421,7 @@ function compileListFieldFilter(params) {
2186
2421
  case "hasEvery":
2187
2422
  case "hasSome": {
2188
2423
  if (!Array.isArray(operand)) {
2189
- throw validationError3({
2424
+ throw validationError4({
2190
2425
  message: `"${operator}" on ${model.name}.${field.name} expects an array`,
2191
2426
  meta: { model: model.name, field: field.name, operator }
2192
2427
  });
@@ -2197,7 +2432,7 @@ function compileListFieldFilter(params) {
2197
2432
  }
2198
2433
  case "isEmpty": {
2199
2434
  if (typeof operand !== "boolean") {
2200
- throw validationError3({
2435
+ throw validationError4({
2201
2436
  message: `"isEmpty" on ${model.name}.${field.name} expects a boolean`,
2202
2437
  meta: { model: model.name, field: field.name, operator }
2203
2438
  });
@@ -2206,7 +2441,7 @@ function compileListFieldFilter(params) {
2206
2441
  break;
2207
2442
  }
2208
2443
  default:
2209
- throw validationError3({
2444
+ throw validationError4({
2210
2445
  message: `unknown list filter "${operator}" on ${model.name}.${field.name} \u2014 lists take equals, has, hasEvery, hasSome, isEmpty`,
2211
2446
  meta: { model: model.name, field: field.name, operator }
2212
2447
  });
@@ -2225,7 +2460,7 @@ function compileJsonFieldFilter(params) {
2225
2460
  const rawPath = filter.path;
2226
2461
  if (rawPath !== undefined) {
2227
2462
  if (!Array.isArray(rawPath) || rawPath.some((seg) => typeof seg !== "string" && typeof seg !== "number")) {
2228
- throw validationError3({
2463
+ throw validationError4({
2229
2464
  message: `"path" on ${model.name}.${field.name} must be an array of keys (strings) and indexes (numbers)`,
2230
2465
  meta: { model: model.name, field: field.name }
2231
2466
  });
@@ -2234,7 +2469,7 @@ function compileJsonFieldFilter(params) {
2234
2469
  const path = rawPath ?? [];
2235
2470
  const mode = filter.mode;
2236
2471
  if (mode !== undefined && mode !== "insensitive" && mode !== "default") {
2237
- throw validationError3({
2472
+ throw validationError4({
2238
2473
  message: `mode must be "insensitive" or "default" on ${model.name}.${field.name}`,
2239
2474
  meta: { model: model.name, field: field.name, mode }
2240
2475
  });
@@ -2243,7 +2478,7 @@ function compileJsonFieldFilter(params) {
2243
2478
  const requireOp = (op) => {
2244
2479
  if (dialect.supportedJsonOps.includes(op))
2245
2480
  return;
2246
- throw unsupportedCapability3({
2481
+ throw unsupportedCapability4({
2247
2482
  dialect: dialect.name,
2248
2483
  feature: `json filter "${op}" on ${model.name}.${field.name}`,
2249
2484
  hint: `this dialect can express: ${dialect.supportedJsonOps.join(", ")}`
@@ -2256,7 +2491,7 @@ function compileJsonFieldFilter(params) {
2256
2491
  if (operator === "equals" || operator === "not") {
2257
2492
  if (operand === null) {
2258
2493
  if (path.length > 0) {
2259
- throw validationError3({
2494
+ throw validationError4({
2260
2495
  message: `"${operator}: null" with a path on ${model.name}.${field.name} is ambiguous (SQL NULL vs JSON null) \u2014 omit the path to test column NULL`,
2261
2496
  meta: { model: model.name, field: field.name, operator }
2262
2497
  });
@@ -2277,7 +2512,7 @@ function compileJsonFieldFilter(params) {
2277
2512
  }
2278
2513
  if (JSON_STRING_OPS.includes(operator)) {
2279
2514
  if (typeof operand !== "string") {
2280
- throw validationError3({
2515
+ throw validationError4({
2281
2516
  message: `"${operator}" on ${model.name}.${field.name} expects a string`,
2282
2517
  meta: { model: model.name, field: field.name, operator }
2283
2518
  });
@@ -2300,13 +2535,13 @@ function compileJsonFieldFilter(params) {
2300
2535
  conditions.push({ kind: "jsonFilter", column, path, op, value: operand });
2301
2536
  continue;
2302
2537
  }
2303
- throw validationError3({
2538
+ throw validationError4({
2304
2539
  message: `unknown json filter "${operator}" on ${model.name}.${field.name} \u2014 supported: path, mode, equals, not, ${[...JSON_STRING_OPS, ...JSON_ARRAY_OPS].join(", ")}`,
2305
2540
  meta: { model: model.name, field: field.name, operator }
2306
2541
  });
2307
2542
  }
2308
2543
  if (conditions.length === 0) {
2309
- throw validationError3({
2544
+ throw validationError4({
2310
2545
  message: `json filter on ${model.name}.${field.name} needs at least one operator`,
2311
2546
  meta: { model: model.name, field: field.name }
2312
2547
  });
@@ -2329,14 +2564,14 @@ function compileOperatorFilter(params) {
2329
2564
  const mode = filter.mode;
2330
2565
  if (mode !== undefined) {
2331
2566
  if (mode !== "insensitive" && mode !== "strict" && mode !== "default") {
2332
- throw validationError3({
2567
+ throw validationError4({
2333
2568
  message: `mode must be "insensitive", "strict" or "default" on ${model.name}.${field.name}`,
2334
2569
  meta: { model: model.name, field: field.name, mode }
2335
2570
  });
2336
2571
  }
2337
2572
  const others = entries.filter(([operator]) => operator !== "mode");
2338
2573
  if (others.length === 0 || others.some(([operator]) => !modeCompanions.includes(operator))) {
2339
- throw validationError3({
2574
+ throw validationError4({
2340
2575
  message: `mode is only supported with ${modeCompanions.join("/")} on ${model.name}.${field.name}`,
2341
2576
  meta: { model: model.name, field: field.name }
2342
2577
  });
@@ -2347,7 +2582,7 @@ function compileOperatorFilter(params) {
2347
2582
  const ciCollated = (params2) => params2.dialect.name === "mysql" && params2.field.collation?.toLowerCase().endsWith("_ci") === true;
2348
2583
  const requireStringOperand = (operator, operand) => {
2349
2584
  if (typeof operand !== "string") {
2350
- throw validationError3({
2585
+ throw validationError4({
2351
2586
  message: `mode: "${String(mode)}" with ${operator} on ${model.name}.${field.name} expects a string`,
2352
2587
  meta: { model: model.name, field: field.name }
2353
2588
  });
@@ -2421,14 +2656,14 @@ function compileOperatorFilter(params) {
2421
2656
  case "in":
2422
2657
  case "notIn": {
2423
2658
  if (!Array.isArray(operand)) {
2424
- throw validationError3({
2659
+ throw validationError4({
2425
2660
  message: `"${operator}" on ${model.name}.${field.name} expects an array`,
2426
2661
  meta: { model: model.name, field: field.name, operator }
2427
2662
  });
2428
2663
  }
2429
2664
  for (const item of operand) {
2430
2665
  if (isDbNow(item)) {
2431
- throw validationError3({
2666
+ throw validationError4({
2432
2667
  message: `dbNow() cannot ride an "${operator}" list on ${model.name}.${field.name} \u2014 compare with lt/lte/gt/gte/equals instead`,
2433
2668
  meta: { model: model.name, field: field.name, operator }
2434
2669
  });
@@ -2486,7 +2721,7 @@ function compileOperatorFilter(params) {
2486
2721
  }));
2487
2722
  break;
2488
2723
  }
2489
- throw validationError3({
2724
+ throw validationError4({
2490
2725
  message: `unknown filter operator "${operator}" on ${model.name}.${field.name}`,
2491
2726
  meta: { model: model.name, field: field.name, operator }
2492
2727
  });
@@ -2512,7 +2747,7 @@ function compileFieldFilter(params) {
2512
2747
  }
2513
2748
  if (Array.isArray(value)) {
2514
2749
  if (!field.isList) {
2515
- throw validationError3({
2750
+ throw validationError4({
2516
2751
  message: `array filter value on ${model.name}.${field.name} \u2014 only list fields compare against arrays`,
2517
2752
  meta: { model: model.name, field: field.name }
2518
2753
  });
@@ -2523,7 +2758,7 @@ function compileFieldFilter(params) {
2523
2758
  if (isPlainObject3(value)) {
2524
2759
  return compileOperatorFilter({ ctx, model, field, filter: value, qualify });
2525
2760
  }
2526
- throw validationError3({
2761
+ throw validationError4({
2527
2762
  message: `unsupported filter value for ${model.name}.${field.name}`,
2528
2763
  meta: { model: model.name, field: field.name }
2529
2764
  });
@@ -2607,7 +2842,7 @@ function buildRelationExists(params) {
2607
2842
  function requirePredicateObject(params) {
2608
2843
  const { model, relation, operator, operand } = params;
2609
2844
  if (!isPlainObject3(operand)) {
2610
- throw validationError3({
2845
+ throw validationError4({
2611
2846
  message: `"${operator}" on relation ${model.name}.${relation.name} expects a where object`,
2612
2847
  meta: { model: model.name, relation: relation.name, operator }
2613
2848
  });
@@ -2620,7 +2855,7 @@ function compileRelationFilter(params) {
2620
2855
  const outer = qualify ?? model.tableName;
2621
2856
  if (relation.isList) {
2622
2857
  if (!isPlainObject3(value)) {
2623
- throw validationError3({
2858
+ throw validationError4({
2624
2859
  message: `filter on relation ${model.name}.${relation.name} must be { some | every | none: \u2026 }`,
2625
2860
  meta: { model: model.name, relation: relation.name }
2626
2861
  });
@@ -2658,14 +2893,14 @@ function compileRelationFilter(params) {
2658
2893
  }));
2659
2894
  break;
2660
2895
  default:
2661
- throw validationError3({
2896
+ throw validationError4({
2662
2897
  message: `unknown relation filter "${operator}" on ${model.name}.${relation.name} \u2014 to-many relations take some/every/none`,
2663
2898
  meta: { model: model.name, relation: relation.name, operator }
2664
2899
  });
2665
2900
  }
2666
2901
  }
2667
2902
  if (conditions2.length === 0) {
2668
- throw validationError3({
2903
+ throw validationError4({
2669
2904
  message: `filter on relation ${model.name}.${relation.name} must contain some, every or none`,
2670
2905
  meta: { model: model.name, relation: relation.name }
2671
2906
  });
@@ -2692,7 +2927,7 @@ function compileRelationFilter(params) {
2692
2927
  isNotArm = value[key];
2693
2928
  hasIsNot = true;
2694
2929
  } else {
2695
- throw validationError3({
2930
+ throw validationError4({
2696
2931
  message: `relation filter on ${model.name}.${relation.name} cannot mix "${key}" with is/isNot`,
2697
2932
  meta: { model: model.name, relation: relation.name, operator: key }
2698
2933
  });
@@ -2702,7 +2937,7 @@ function compileRelationFilter(params) {
2702
2937
  isArm = value;
2703
2938
  hasIs = true;
2704
2939
  } else {
2705
- throw validationError3({
2940
+ throw validationError4({
2706
2941
  message: `filter on relation ${model.name}.${relation.name} must be a where object, { is | isNot: \u2026 } or null`,
2707
2942
  meta: { model: model.name, relation: relation.name }
2708
2943
  });
@@ -2756,6 +2991,12 @@ function compileWhereInternal(params) {
2756
2991
  for (const [key, value] of Object.entries(where)) {
2757
2992
  if (value === undefined)
2758
2993
  continue;
2994
+ if (key === "$raw") {
2995
+ if (!isSqlFragment(value))
2996
+ throw validationError4({ message: "$raw requires a constructed SQL fragment" });
2997
+ operands.push({ kind: "composed", fragment: value });
2998
+ continue;
2999
+ }
2759
3000
  if (key === "AND") {
2760
3001
  operands.push({ kind: "and", operands: compileWhereGroup({ ctx, model, value, key, qualify }) });
2761
3002
  continue;
@@ -2797,20 +3038,20 @@ function compileOrderTerm(params) {
2797
3038
  if (isPlainObject3(spec)) {
2798
3039
  const { sort, nulls } = spec;
2799
3040
  if (sort !== "asc" && sort !== "desc") {
2800
- throw validationError3({
3041
+ throw validationError4({
2801
3042
  message: `orderBy sort must be "asc" or "desc" on ${model.name}.${field.name}`,
2802
3043
  meta: { model: model.name, field: field.name, sort }
2803
3044
  });
2804
3045
  }
2805
3046
  if (nulls !== undefined && nulls !== "first" && nulls !== "last") {
2806
- throw validationError3({
3047
+ throw validationError4({
2807
3048
  message: `orderBy nulls must be "first" or "last" on ${model.name}.${field.name}`,
2808
3049
  meta: { model: model.name, field: field.name, nulls }
2809
3050
  });
2810
3051
  }
2811
3052
  return nulls === undefined ? { column, direction: sort } : { column, direction: sort, nulls };
2812
3053
  }
2813
- throw validationError3({
3054
+ throw validationError4({
2814
3055
  message: `orderBy on ${model.name}.${field.name} must be "asc", "desc" or { sort, nulls }`,
2815
3056
  meta: { model: model.name, field: field.name }
2816
3057
  });
@@ -2846,7 +3087,7 @@ function compileRelationOrderTerms(params) {
2846
3087
  const requireDirection = (value, what) => {
2847
3088
  if (value === "asc" || value === "desc")
2848
3089
  return value;
2849
- throw validationError3({
3090
+ throw validationError4({
2850
3091
  message: `${what} on ${model.name}.${relation.name} must be "asc" or "desc" \u2014 nulls placement is not supported on relation orderBy`,
2851
3092
  meta: { model: model.name, relation: relation.name }
2852
3093
  });
@@ -2856,7 +3097,7 @@ function compileRelationOrderTerms(params) {
2856
3097
  continue;
2857
3098
  if (key === "_count") {
2858
3099
  if (!isList) {
2859
- throw validationError3({
3100
+ throw validationError4({
2860
3101
  message: `orderBy.${relation.name}._count on model "${model.name}" needs a list relation \u2014 "${relation.name}" is to-one`,
2861
3102
  meta: { model: model.name, relation: relation.name }
2862
3103
  });
@@ -2888,7 +3129,7 @@ function compileRelationOrderTerms(params) {
2888
3129
  continue;
2889
3130
  }
2890
3131
  if (isList) {
2891
- throw validationError3({
3132
+ throw validationError4({
2892
3133
  message: `orderBy.${relation.name}.${key} on model "${model.name}" \u2014 a to-many relation orders by _count only (a scalar across many rows is ambiguous)`,
2893
3134
  meta: { model: model.name, relation: relation.name, field: key }
2894
3135
  });
@@ -2946,7 +3187,7 @@ function compileSelect(params) {
2946
3187
  if (model.computed !== null && model.computed.has(name))
2947
3188
  continue;
2948
3189
  if (value !== true) {
2949
- throw validationError3({
3190
+ throw validationError4({
2950
3191
  message: `select.${name} must be true or false on model "${model.name}"`,
2951
3192
  meta: { model: model.name, field: name }
2952
3193
  });
@@ -2954,7 +3195,7 @@ function compileSelect(params) {
2954
3195
  names.push(getFieldMeta({ model, field: name, context: "select" }).name);
2955
3196
  }
2956
3197
  if (names.length === 0 && !allowEmpty) {
2957
- throw validationError3({
3198
+ throw validationError4({
2958
3199
  message: `select on model "${model.name}" must include at least one field`,
2959
3200
  meta: { model: model.name }
2960
3201
  });
@@ -2972,7 +3213,7 @@ function resolveComputedSelection(params) {
2972
3213
  if (!specs.has(name) || value === undefined || value === false)
2973
3214
  continue;
2974
3215
  if (value !== true) {
2975
- throw validationError3({
3216
+ throw validationError4({
2976
3217
  message: `select.${name} must be true or false on model "${model.name}"`,
2977
3218
  meta: { model: model.name, field: name }
2978
3219
  });
@@ -3040,7 +3281,7 @@ function expandCompoundWhere(params) {
3040
3281
  for (const field of fields) {
3041
3282
  const member = spec[field.name];
3042
3283
  if (member === undefined) {
3043
- throw validationError3({
3284
+ throw validationError4({
3044
3285
  message: `${what}.${key} on model "${model.name}" needs every member field (${fields.map((entry) => entry.name).join(", ")}) \u2014 "${field.name}" is missing`,
3045
3286
  meta: { model: model.name, key, field: field.name }
3046
3287
  });
@@ -3073,7 +3314,7 @@ function assertUniqueWhere(params) {
3073
3314
  ...model.singleIndexUniques.map((single) => single.fields[0].name),
3074
3315
  ...model.compoundUniques.keys()
3075
3316
  ];
3076
- throw validationError3({
3317
+ throw validationError4({
3077
3318
  message: `${model.clientName}.${method}() needs a unique where \u2014 one of: ${candidates.join(", ")}`,
3078
3319
  meta: { model: model.name, method, candidates }
3079
3320
  });
@@ -3203,7 +3444,7 @@ function compileCreateRow(params) {
3203
3444
  continue;
3204
3445
  }
3205
3446
  if (Array.isArray(value) && value.some((item) => isDbNow(item))) {
3206
- throw validationError3({
3447
+ throw validationError4({
3207
3448
  message: `dbNow() cannot be a list element (${model.name}.${entry.field.name})`,
3208
3449
  meta: { model: model.name, field: entry.field.name }
3209
3450
  });
@@ -3246,14 +3487,14 @@ function orderedColumns(params) {
3246
3487
  function compileAtomicOperation(params) {
3247
3488
  const { model, field, operator, operand } = params;
3248
3489
  if (field.scalarType === null || !NUMERIC_TYPES.includes(field.scalarType)) {
3249
- throw validationError3({
3490
+ throw validationError4({
3250
3491
  message: `"${operator}" needs a numeric field, but ${model.name}.${field.name} is ${String(field.enumName ?? field.scalarType)}`,
3251
3492
  meta: { model: model.name, field: field.name, operator }
3252
3493
  });
3253
3494
  }
3254
3495
  const numeric = typeof operand === "bigint" ? Number(operand) : operand;
3255
3496
  if (typeof numeric !== "number" || !Number.isFinite(numeric)) {
3256
- throw validationError3({
3497
+ throw validationError4({
3257
3498
  message: `"${operator}" on ${model.name}.${field.name} expects a number`,
3258
3499
  meta: { model: model.name, field: field.name, operator, operand }
3259
3500
  });
@@ -3284,7 +3525,7 @@ function compileUpdateSet(params) {
3284
3525
  continue;
3285
3526
  }
3286
3527
  if (Array.isArray(value) && value.some((item) => isDbNow(item))) {
3287
- throw validationError3({
3528
+ throw validationError4({
3288
3529
  message: `dbNow() cannot be a list element (${model.name}.${field.name})`,
3289
3530
  meta: { model: model.name, field: field.name }
3290
3531
  });
@@ -3292,7 +3533,7 @@ function compileUpdateSet(params) {
3292
3533
  if (isPlainObject3(value) && field.scalarType !== "Json") {
3293
3534
  const entries = Object.entries(value).filter(([, operand2]) => operand2 !== undefined);
3294
3535
  if (entries.length !== 1) {
3295
- throw validationError3({
3536
+ throw validationError4({
3296
3537
  message: `update of ${model.name}.${field.name} must use exactly one operation`,
3297
3538
  meta: { model: model.name, field: field.name, operations: entries.map(([key]) => key) }
3298
3539
  });
@@ -3308,7 +3549,7 @@ function compileUpdateSet(params) {
3308
3549
  }
3309
3550
  const atomic = ATOMIC_OPERATIONS[operator];
3310
3551
  if (atomic === undefined) {
3311
- throw validationError3({
3552
+ throw validationError4({
3312
3553
  message: `unknown update operation "${operator}" on ${model.name}.${field.name}`,
3313
3554
  meta: { model: model.name, field: field.name, operator }
3314
3555
  });
@@ -3326,7 +3567,7 @@ function compileUpdateSet(params) {
3326
3567
  }
3327
3568
  }
3328
3569
  if (Object.keys(set).length === 0) {
3329
- throw validationError3({
3570
+ throw validationError4({
3330
3571
  message: `update of model "${model.name}" must change at least one field`,
3331
3572
  meta: { model: model.name }
3332
3573
  });
@@ -3337,7 +3578,7 @@ function compileCreateRows(params) {
3337
3578
  const { dialect, model, data } = params;
3338
3579
  const items = Array.isArray(data) ? data : [data];
3339
3580
  if (items.length === 0) {
3340
- throw validationError3({
3581
+ throw validationError4({
3341
3582
  message: `${params.method ?? "createMany"} on model "${model.name}" needs at least one row`,
3342
3583
  meta: { model: model.name }
3343
3584
  });
@@ -3388,7 +3629,7 @@ function compileCreateRows(params) {
3388
3629
  function compileTake(params) {
3389
3630
  const { value, model } = params;
3390
3631
  if (typeof value !== "number" || !Number.isSafeInteger(value)) {
3391
- throw validationError3({
3632
+ throw validationError4({
3392
3633
  message: `take must be an integer on model "${model.name}", got ${String(value)}`,
3393
3634
  meta: { model: model.name, argument: "take", value }
3394
3635
  });
@@ -3404,7 +3645,7 @@ function parseCursor(params) {
3404
3645
  });
3405
3646
  const entries = Object.entries(spec).filter(([, value]) => value !== undefined);
3406
3647
  if (entries.length === 0) {
3407
- throw validationError3({
3648
+ throw validationError4({
3408
3649
  message: `cursor on model "${model.name}" needs a unique field value`,
3409
3650
  meta: { model: model.name }
3410
3651
  });
@@ -3414,7 +3655,7 @@ function parseCursor(params) {
3414
3655
  for (const [name, value] of entries) {
3415
3656
  const field = getFieldMeta({ model, field: name, context: "cursor" });
3416
3657
  if (!isDirectValue(value)) {
3417
- throw validationError3({
3658
+ throw validationError4({
3418
3659
  message: `cursor.${name} on model "${model.name}" must be a direct value`,
3419
3660
  meta: { model: model.name, field: name }
3420
3661
  });
@@ -3426,7 +3667,7 @@ function parseCursor(params) {
3426
3667
  const field = fields[0];
3427
3668
  const isSingleIndexUnique = model.singleIndexUniques.some((single) => single.fields[0].name === field.name);
3428
3669
  if (!field.isId && !field.isUnique && !isSingleIndexUnique) {
3429
- throw validationError3({
3670
+ throw validationError4({
3430
3671
  message: `cursor field ${model.name}.${field.name} must be @id or @unique \u2014 a non-unique cursor cannot identify a position`,
3431
3672
  meta: { model: model.name, field: field.name }
3432
3673
  });
@@ -3440,7 +3681,7 @@ function parseCursor(params) {
3440
3681
  model.primaryKey.map((field) => field.name).join(" + ") || "none",
3441
3682
  ...model.compoundUniques.keys()
3442
3683
  ];
3443
- throw validationError3({
3684
+ throw validationError4({
3444
3685
  message: `multi-field cursor on model "${model.name}" must be exactly the composite primary key or a composite @@unique group (${candidates.join(", ")})`,
3445
3686
  meta: { model: model.name, fields: [...names] }
3446
3687
  });
@@ -3451,7 +3692,7 @@ function parseCursor(params) {
3451
3692
  function keysetFieldOfTerm(params) {
3452
3693
  const { dialect, model, term } = params;
3453
3694
  if (!("column" in term)) {
3454
- throw unsupportedCapability3({
3695
+ throw unsupportedCapability4({
3455
3696
  dialect: dialect.name,
3456
3697
  feature: `keyset pagination over a relation or aggregate orderBy on model "${model.name}"`,
3457
3698
  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"
@@ -3459,7 +3700,7 @@ function keysetFieldOfTerm(params) {
3459
3700
  }
3460
3701
  const field = model.fieldByColumn.get(term.column.name);
3461
3702
  if (field === undefined) {
3462
- throw unsupportedCapability3({
3703
+ throw unsupportedCapability4({
3463
3704
  dialect: dialect.name,
3464
3705
  feature: `keyset pagination over a computed order key on model "${model.name}"`,
3465
3706
  hint: "order by scalar columns plus a unique tie-breaker"
@@ -3488,7 +3729,7 @@ function parseKeysetOptions(params) {
3488
3729
  const spec = requireObject({ value, what: "keyset", model });
3489
3730
  for (const key of Object.keys(spec)) {
3490
3731
  if (key !== "timestampPrecision") {
3491
- throw validationError3({
3732
+ throw validationError4({
3492
3733
  message: `unknown keyset option "${key}" on model "${model.name}" \u2014 only timestampPrecision is supported`,
3493
3734
  meta: { model: model.name, option: key, reason: "keyset-option-unknown" }
3494
3735
  });
@@ -3498,7 +3739,7 @@ function parseKeysetOptions(params) {
3498
3739
  if (precision === undefined)
3499
3740
  return {};
3500
3741
  if (precision !== "milliseconds") {
3501
- throw validationError3({
3742
+ throw validationError4({
3502
3743
  message: `keyset.timestampPrecision on model "${model.name}" must be "milliseconds"`,
3503
3744
  meta: { model: model.name, value: precision, reason: "keyset-option-invalid" }
3504
3745
  });
@@ -3508,25 +3749,25 @@ function parseKeysetOptions(params) {
3508
3749
  function buildKeysetBound(params) {
3509
3750
  const { dialect, model, args, userOrder, sqlOrder, colRefOf } = params;
3510
3751
  if (args.cursor !== undefined) {
3511
- throw validationError3({
3752
+ throw validationError4({
3512
3753
  message: `after and cursor cannot combine on model "${model.name}" \u2014 a keyset position and a unique-key cursor are two different pagination forms`,
3513
3754
  meta: { model: model.name, reason: "keyset-cursor-conflict" }
3514
3755
  });
3515
3756
  }
3516
3757
  if (args.distinct !== undefined) {
3517
- throw validationError3({
3758
+ throw validationError4({
3518
3759
  message: `after cannot combine with distinct on model "${model.name}" \u2014 deduplication reorders the result, so a position in the requested order does not address it`,
3519
3760
  meta: { model: model.name, reason: "keyset-distinct-unsupported" }
3520
3761
  });
3521
3762
  }
3522
3763
  if (args.skip !== undefined) {
3523
- throw validationError3({
3764
+ throw validationError4({
3524
3765
  message: `after cannot combine with skip on model "${model.name}" \u2014 keyset pagination has no offset, and an offset on top of it is not keyset pagination`,
3525
3766
  meta: { model: model.name, reason: "keyset-skip-unsupported" }
3526
3767
  });
3527
3768
  }
3528
3769
  if (userOrder.length === 0) {
3529
- throw validationError3({
3770
+ throw validationError4({
3530
3771
  message: `after on model "${model.name}" needs an orderBy \u2014 a position has no meaning without an order`,
3531
3772
  meta: { model: model.name, reason: "keyset-order-required" }
3532
3773
  });
@@ -3621,7 +3862,7 @@ function normalizeDistinct(params) {
3621
3862
  const { model, distinct } = params;
3622
3863
  const names = typeof distinct === "string" ? [distinct] : distinct;
3623
3864
  if (!Array.isArray(names) || names.length === 0 || names.some((name) => typeof name !== "string")) {
3624
- throw validationError3({
3865
+ throw validationError4({
3625
3866
  message: `distinct on model "${model.name}" must be a field name or a non-empty array of field names`,
3626
3867
  meta: { model: model.name }
3627
3868
  });
@@ -3679,7 +3920,7 @@ function buildFindShape(params) {
3679
3920
  baseOrder = model.primaryKey.map((field) => ({ column: colRefOf(field), direction: "asc" }));
3680
3921
  }
3681
3922
  if (backward && baseOrder.length === 0) {
3682
- throw validationError3({
3923
+ throw validationError4({
3683
3924
  message: `negative take on model "${model.name}" needs an orderBy (or a primary key) to reverse`,
3684
3925
  meta: { model: model.name }
3685
3926
  });
@@ -3693,13 +3934,13 @@ function buildFindShape(params) {
3693
3934
  let distinctOuterOrder = null;
3694
3935
  if (args.distinct !== undefined) {
3695
3936
  if (backward) {
3696
- throw validationError3({
3937
+ throw validationError4({
3697
3938
  message: `distinct cannot combine with a negative take on model "${model.name}"`,
3698
3939
  meta: { model: model.name }
3699
3940
  });
3700
3941
  }
3701
3942
  if (orderTerms.some((term) => ("select" in term))) {
3702
- throw validationError3({
3943
+ throw validationError4({
3703
3944
  message: `distinct cannot combine with a relation orderBy on model "${model.name}"`,
3704
3945
  meta: { model: model.name }
3705
3946
  });
@@ -3750,7 +3991,7 @@ function buildFindShape(params) {
3750
3991
  const leadingColumns = leading.filter((term) => ("column" in term));
3751
3992
  const coherent = leadingColumns.length === leading.length && leading.length === cursor.fields.length && new Set(leadingColumns.map((term) => term.column.name)).size === leading.length && leadingColumns.every((term) => cursorIndexByColumn.has(term.column.name));
3752
3993
  if (!coherent) {
3753
- throw validationError3({
3994
+ throw validationError4({
3754
3995
  message: `cursor pagination on model "${model.name}" needs orderBy to lead with the cursor field(s) ${cursor.fields.map((field) => `"${field.name}"`).join(", ")} \u2014 a cursor bound over a different ordering is not cursor pagination`,
3755
3996
  meta: { model: model.name, cursorFields: cursor.fields.map((field) => field.name) }
3756
3997
  });
@@ -3782,7 +4023,7 @@ function buildFindShape(params) {
3782
4023
  }
3783
4024
  if (args.after !== undefined) {
3784
4025
  if (typeof args.after !== "string") {
3785
- throw validationError3({
4026
+ throw validationError4({
3786
4027
  message: `after on model "${model.name}" must be a keyset token string`,
3787
4028
  meta: { model: model.name, reason: "keyset-token-malformed" }
3788
4029
  });
@@ -4060,13 +4301,13 @@ function compileOmit(params) {
4060
4301
  if (value === undefined || value === false)
4061
4302
  continue;
4062
4303
  if (value !== true) {
4063
- throw validationError3({
4304
+ throw validationError4({
4064
4305
  message: `omit.${name} must be true or false on model "${model.name}"`,
4065
4306
  meta: { model: model.name, field: name }
4066
4307
  });
4067
4308
  }
4068
4309
  if (model.relationByName.has(name)) {
4069
- throw validationError3({
4310
+ throw validationError4({
4070
4311
  message: `omit.${name} on model "${model.name}" \u2014 omit takes scalar fields; a relation is excluded by not including it`,
4071
4312
  meta: { model: model.name, field: name }
4072
4313
  });
@@ -4077,7 +4318,7 @@ function compileOmit(params) {
4077
4318
  }
4078
4319
  const kept = model.fields.filter((field) => !omitted.has(field.name)).map((field) => field.name);
4079
4320
  if (kept.length === 0) {
4080
- throw validationError3({
4321
+ throw validationError4({
4081
4322
  message: `omit on model "${model.name}" removes every field`,
4082
4323
  meta: { model: model.name }
4083
4324
  });
@@ -4092,14 +4333,14 @@ function validateAggregatedField(params) {
4092
4333
  if (key === "_count")
4093
4334
  return;
4094
4335
  if (field.isList) {
4095
- throw validationError3({
4336
+ throw validationError4({
4096
4337
  message: `${key} cannot aggregate the list field ${model.name}.${field.name}`,
4097
4338
  meta: { model: model.name, field: field.name, key }
4098
4339
  });
4099
4340
  }
4100
4341
  if (key === "_avg" || key === "_sum") {
4101
4342
  if (field.scalarType === null || !NUMERIC_TYPES.includes(field.scalarType)) {
4102
- throw validationError3({
4343
+ throw validationError4({
4103
4344
  message: `${key} needs a numeric field, but ${model.name}.${field.name} is ${String(field.enumName ?? field.scalarType)}`,
4104
4345
  meta: { model: model.name, field: field.name, key }
4105
4346
  });
@@ -4107,7 +4348,7 @@ function validateAggregatedField(params) {
4107
4348
  return;
4108
4349
  }
4109
4350
  if (field.scalarType === "Json" || field.scalarType === "Bytes" || field.scalarType === "Boolean") {
4110
- throw validationError3({
4351
+ throw validationError4({
4111
4352
  message: `${key} cannot aggregate ${model.name}.${field.name} (${field.scalarType} has no ordering)`,
4112
4353
  meta: { model: model.name, field: field.name, key }
4113
4354
  });
@@ -4135,14 +4376,14 @@ function compileAggregations(params) {
4135
4376
  if (flag === undefined || flag === false)
4136
4377
  continue;
4137
4378
  if (flag !== true) {
4138
- throw validationError3({
4379
+ throw validationError4({
4139
4380
  message: `${key}.${name} must be true or false on model "${model.name}"`,
4140
4381
  meta: { model: model.name, field: name, key }
4141
4382
  });
4142
4383
  }
4143
4384
  if (name === "_all") {
4144
4385
  if (key !== "_count") {
4145
- throw validationError3({
4386
+ throw validationError4({
4146
4387
  message: `${key}._all is not valid \u2014 _all only counts rows (_count)`,
4147
4388
  meta: { model: model.name, key }
4148
4389
  });
@@ -4169,7 +4410,7 @@ function compileAggregations(params) {
4169
4410
  }
4170
4411
  }
4171
4412
  if (selections.length === 0 && method === "aggregate") {
4172
- throw validationError3({
4413
+ throw validationError4({
4173
4414
  message: `${model.clientName}.aggregate() needs at least one of ${AGGREGATE_KEYS.join(", ")}`,
4174
4415
  meta: { model: model.name }
4175
4416
  });
@@ -4190,7 +4431,7 @@ function compileAggregatePredicate(params) {
4190
4431
  let field = null;
4191
4432
  if (selector === "_all") {
4192
4433
  if (key !== "_count") {
4193
- throw validationError3({
4434
+ throw validationError4({
4194
4435
  message: `having ${key}._all is not valid \u2014 _all only counts rows (_count)`,
4195
4436
  meta: { model: model.name, key }
4196
4437
  });
@@ -4209,7 +4450,7 @@ function compileAggregatePredicate(params) {
4209
4450
  continue;
4210
4451
  const op = HAVING_OPERATOR_MAP[operator];
4211
4452
  if (op === undefined) {
4212
- throw validationError3({
4453
+ throw validationError4({
4213
4454
  message: `unknown having operator "${operator}" on ${key} of ${model.name}.${selector} \u2014 supported: ${Object.keys(HAVING_OPERATOR_MAP).join(", ")}`,
4214
4455
  meta: { model: model.name, key, operator }
4215
4456
  });
@@ -4218,7 +4459,7 @@ function compileAggregatePredicate(params) {
4218
4459
  out.push({ kind: "aggCmp", op, agg, value });
4219
4460
  }
4220
4461
  if (out.length === 0) {
4221
- throw validationError3({
4462
+ throw validationError4({
4222
4463
  message: `having ${key}.${selector} on model "${model.name}" needs an operator`,
4223
4464
  meta: { model: model.name, key }
4224
4465
  });
@@ -4264,7 +4505,7 @@ function compileHaving(params) {
4264
4505
  const requireGrouped = () => {
4265
4506
  if (byNames.has(field.name))
4266
4507
  return;
4267
- throw validationError3({
4508
+ throw validationError4({
4268
4509
  message: `having on ${model.name}.${field.name} needs the field in \`by\` \u2014 only grouped columns and aggregates exist after grouping`,
4269
4510
  meta: { model: model.name, field: field.name }
4270
4511
  });
@@ -4317,7 +4558,7 @@ function compileGroupByOrder(params) {
4317
4558
  if (direction === undefined)
4318
4559
  continue;
4319
4560
  if (direction !== "asc" && direction !== "desc") {
4320
- throw validationError3({
4561
+ throw validationError4({
4321
4562
  message: `orderBy ${name}.${selector} must be "asc" or "desc" on model "${model.name}"`,
4322
4563
  meta: { model: model.name, key: name, selector }
4323
4564
  });
@@ -4325,7 +4566,7 @@ function compileGroupByOrder(params) {
4325
4566
  let agg;
4326
4567
  if (selector === "_all") {
4327
4568
  if (key !== "_count") {
4328
- throw validationError3({
4569
+ throw validationError4({
4329
4570
  message: `orderBy ${name}._all is not valid \u2014 _all only counts rows (_count)`,
4330
4571
  meta: { model: model.name, key: name }
4331
4572
  });
@@ -4343,7 +4584,7 @@ function compileGroupByOrder(params) {
4343
4584
  }
4344
4585
  const field = getFieldMeta({ model, field: name, context: "orderBy" });
4345
4586
  if (!byNames.has(field.name)) {
4346
- throw validationError3({
4587
+ throw validationError4({
4347
4588
  message: `orderBy on ${model.name}.${field.name} \u2014 groupBy can only order by fields in \`by\` or by aggregates`,
4348
4589
  meta: { model: model.name, field: field.name }
4349
4590
  });
@@ -4389,7 +4630,7 @@ function resolveStrategy(params) {
4389
4630
  const { dialect, model, method, args, hasRelations, defaultRelationStrategy } = params;
4390
4631
  const explicit = args.relationStrategy;
4391
4632
  if (explicit !== undefined && explicit !== "query" && explicit !== "join") {
4392
- throw validationError3({
4633
+ throw validationError4({
4393
4634
  message: `relationStrategy on ${model.clientName}.${method}() must be "query" or "join"`,
4394
4635
  meta: { model: model.name, method, relationStrategy: explicit }
4395
4636
  });
@@ -4399,7 +4640,7 @@ function resolveStrategy(params) {
4399
4640
  const strategy = explicit ?? defaultRelationStrategy ?? "query";
4400
4641
  if (strategy === "join" && !dialect.capabilities.lateralJoin) {
4401
4642
  if (explicit !== undefined || hasRelations) {
4402
- throw unsupportedCapability3({
4643
+ throw unsupportedCapability4({
4403
4644
  dialect: dialect.name,
4404
4645
  feature: 'relationStrategy "join"',
4405
4646
  hint: 'lateral joins are postgres-only \u2014 use the portable "query" strategy on this dialect'
@@ -4441,7 +4682,7 @@ function buildQuery(params) {
4441
4682
  if (args.select[key] === undefined || args.select[key] === false)
4442
4683
  continue;
4443
4684
  if (key === "_count" || model.relationByName.has(key)) {
4444
- throw validationError3({
4685
+ throw validationError4({
4445
4686
  message: `select.${key} on ${model.clientName}.${method}() \u2014 relations are not supported on this method`,
4446
4687
  meta: { model: model.name, method, field: key }
4447
4688
  });
@@ -4457,7 +4698,7 @@ function buildQuery(params) {
4457
4698
  defaultRelationStrategy: params.defaultRelationStrategy
4458
4699
  });
4459
4700
  if (args.select !== undefined && args.omit !== undefined) {
4460
- throw validationError3({
4701
+ throw validationError4({
4461
4702
  message: `select and omit cannot combine on ${model.clientName}.${method}() \u2014 select names fields to keep, omit fields to drop`,
4462
4703
  meta: { model: model.name, method }
4463
4704
  });
@@ -4523,7 +4764,7 @@ function buildQuery(params) {
4523
4764
  switch (method) {
4524
4765
  case "findMany":
4525
4766
  case "findFirst": {
4526
- const locked = compileLock({ args, model, method, hasRelations: relations !== null });
4767
+ const locked = compileLock({ dialect, args, model, method, hasRelations: relations !== null });
4527
4768
  const shape = buildFindShape({
4528
4769
  dialect,
4529
4770
  meta,
@@ -4536,13 +4777,13 @@ function buildQuery(params) {
4536
4777
  });
4537
4778
  return {
4538
4779
  ...base,
4539
- statement: locked ? { ...shape.statement, lockForUpdate: true } : shape.statement,
4780
+ statement: locked === null ? shape.statement : { ...shape.statement, rowLock: locked },
4540
4781
  reverseRows: shape.reverseRows,
4541
4782
  resortBy: shape.resortBy
4542
4783
  };
4543
4784
  }
4544
4785
  case "findUnique": {
4545
- const locked = compileLock({ args, model, method, hasRelations: relations !== null });
4786
+ const locked = compileLock({ dialect, args, model, method, hasRelations: relations !== null });
4546
4787
  const where = expandCompoundWhere({
4547
4788
  model,
4548
4789
  where: requireObject({ value: args.where, what: "where", model })
@@ -4559,7 +4800,7 @@ function buildQuery(params) {
4559
4800
  });
4560
4801
  return {
4561
4802
  ...base,
4562
- statement: locked ? { ...shape.statement, lockForUpdate: true } : shape.statement
4803
+ statement: locked === null ? shape.statement : { ...shape.statement, rowLock: locked }
4563
4804
  };
4564
4805
  }
4565
4806
  case "count": {
@@ -4583,7 +4824,7 @@ function buildQuery(params) {
4583
4824
  if (flag === undefined || flag === false)
4584
4825
  continue;
4585
4826
  if (flag !== true) {
4586
- throw validationError3({
4827
+ throw validationError4({
4587
4828
  message: `select.${name} must be true or false on ${model.clientName}.count()`,
4588
4829
  meta: { model: model.name, field: name }
4589
4830
  });
@@ -4604,7 +4845,7 @@ function buildQuery(params) {
4604
4845
  aggSelects.push({ agg: { fn: "count", column: { name: field.columnName } }, alias: field.name });
4605
4846
  }
4606
4847
  if (selections.length === 0) {
4607
- throw validationError3({
4848
+ throw validationError4({
4608
4849
  message: `count select on model "${model.name}" must include _all or at least one field`,
4609
4850
  meta: { model: model.name }
4610
4851
  });
@@ -4707,7 +4948,7 @@ function buildQuery(params) {
4707
4948
  const byRaw = args.by;
4708
4949
  const byList = typeof byRaw === "string" ? [byRaw] : byRaw;
4709
4950
  if (!Array.isArray(byList) || byList.length === 0 || byList.some((name) => typeof name !== "string")) {
4710
- throw validationError3({
4951
+ throw validationError4({
4711
4952
  message: `groupBy on model "${model.name}" needs \`by\`: a field name or a non-empty array of field names`,
4712
4953
  meta: { model: model.name }
4713
4954
  });
@@ -4715,7 +4956,7 @@ function buildQuery(params) {
4715
4956
  const byFields = byList.map((name) => {
4716
4957
  const field = getFieldMeta({ model, field: name, context: "by" });
4717
4958
  if (field.isList || field.scalarType === "Json") {
4718
- throw validationError3({
4959
+ throw validationError4({
4719
4960
  message: `groupBy cannot group by ${model.name}.${field.name} (${field.isList ? "list" : "Json"} fields have no grouping equality)`,
4720
4961
  meta: { model: model.name, field: field.name }
4721
4962
  });
@@ -4754,16 +4995,16 @@ function buildQuery(params) {
4754
4995
  if (args.onConflict !== undefined) {
4755
4996
  const spec = requireObject({ value: args.onConflict, what: "onConflict", model });
4756
4997
  for (const key of Object.keys(spec)) {
4757
- if (key !== "action" && key !== "target" && key !== "where") {
4758
- throw validationError3({
4759
- message: `unknown onConflict key "${key}" on ${model.clientName}.create() \u2014 expected action/target/where`,
4998
+ if (key !== "action" && key !== "target" && key !== "where" && key !== "update") {
4999
+ throw validationError4({
5000
+ message: `unknown onConflict key "${key}" on ${model.clientName}.create() \u2014 expected action/target/where/update`,
4760
5001
  meta: { model: model.name, key }
4761
5002
  });
4762
5003
  }
4763
5004
  }
4764
- if (spec.action !== "nothing") {
4765
- throw validationError3({
4766
- message: `onConflict.action on ${model.clientName}.create() must be "nothing" (conflicting rows are skipped \u2014 to update instead, use upsert())`,
5005
+ if (spec.action !== "nothing" && spec.action !== "update") {
5006
+ throw validationError4({
5007
+ message: `onConflict.action on ${model.clientName}.create() must be "nothing" or "update"`,
4767
5008
  meta: { model: model.name }
4768
5009
  });
4769
5010
  }
@@ -4775,17 +5016,39 @@ function buildQuery(params) {
4775
5016
  target: spec.target,
4776
5017
  where: spec.where
4777
5018
  });
4778
- conflictClause = {
4779
- doNothing: true,
4780
- ...compiled.columns === undefined ? {} : { columns: compiled.columns },
4781
- ...compiled.constraint === undefined ? {} : { constraint: compiled.constraint },
4782
- ...compiled.conflictWhere === undefined ? {} : { conflictWhere: compiled.conflictWhere }
4783
- };
5019
+ if (spec.action === "nothing" && spec.update !== undefined)
5020
+ throw validationError4({ message: "DO NOTHING cannot carry update expressions" });
5021
+ if (spec.action === "update") {
5022
+ if (!dialect.capabilities.returning)
5023
+ throw unsupportedCapability4({ dialect: dialect.name, feature: "create onConflict update returning", hint: "use upsertMany for a count-only conflict update" });
5024
+ if (compiled.columns === undefined && compiled.constraint === undefined)
5025
+ throw validationError4({ message: "A conflict update requires an explicit target" });
5026
+ const updates = requireObject({ value: spec.update, what: "onConflict.update", model });
5027
+ const fields = resolveUpsertUpdateFields({ model, update: Object.keys(updates), targetColumns: new Set(compiled.columns ?? []), skipUpdatedAt: true });
5028
+ const columns2 = Object.fromEntries(model.fields.map((field) => [field.name, field.columnName]));
5029
+ conflictClause = {
5030
+ columns: compiled.columns ?? [],
5031
+ ...compiled.constraint === undefined ? {} : { constraint: compiled.constraint },
5032
+ ...compiled.conflictWhere === undefined ? {} : { conflictWhere: compiled.conflictWhere },
5033
+ update: Object.fromEntries(fields.map((field) => {
5034
+ const fragment = updates[field.name];
5035
+ if (!isSqlFragment(fragment))
5036
+ throw validationError4({ message: "Conflict update expressions must be constructed SQL fragments" });
5037
+ return [field.columnName, mapConflictColumns({ fragment, columns: columns2 })];
5038
+ }))
5039
+ };
5040
+ } else
5041
+ conflictClause = {
5042
+ doNothing: true,
5043
+ ...compiled.columns === undefined ? {} : { columns: compiled.columns },
5044
+ ...compiled.constraint === undefined ? {} : { constraint: compiled.constraint },
5045
+ ...compiled.conflictWhere === undefined ? {} : { conflictWhere: compiled.conflictWhere }
5046
+ };
4784
5047
  }
4785
5048
  const row = compileCreateRow({ dialect, model, data: args.data });
4786
5049
  const columns = orderedColumns({ model, values: row.values });
4787
5050
  if (columns.length === 0) {
4788
- throw validationError3({
5051
+ throw validationError4({
4789
5052
  message: `create on model "${model.name}" needs at least one field value`,
4790
5053
  meta: { model: model.name }
4791
5054
  });
@@ -4806,7 +5069,7 @@ function buildQuery(params) {
4806
5069
  case "createMany":
4807
5070
  case "createManyAndReturn": {
4808
5071
  if (method === "createManyAndReturn" && !dialect.capabilities.createManyAndReturn) {
4809
- throw unsupportedCapability3({
5072
+ throw unsupportedCapability4({
4810
5073
  dialect: dialect.name,
4811
5074
  feature: "createManyAndReturn",
4812
5075
  hint: "this dialect cannot RETURNING multiple inserted rows \u2014 use createMany then findMany"
@@ -4820,7 +5083,7 @@ function buildQuery(params) {
4820
5083
  const spec = requireObject({ value: args.skipDuplicates, what: "skipDuplicates", model });
4821
5084
  for (const key of Object.keys(spec)) {
4822
5085
  if (key !== "target" && key !== "where") {
4823
- throw validationError3({
5086
+ throw validationError4({
4824
5087
  message: `unknown skipDuplicates key "${key}" on ${model.clientName}.${method}() \u2014 expected target/where`,
4825
5088
  meta: { model: model.name, key }
4826
5089
  });
@@ -4858,7 +5121,7 @@ function buildQuery(params) {
4858
5121
  case "upsertMany": {
4859
5122
  for (const flag of ["skipUpdatedAt", "allowAnyUniqueConflict"]) {
4860
5123
  if (args[flag] !== undefined && typeof args[flag] !== "boolean") {
4861
- throw validationError3({
5124
+ throw validationError4({
4862
5125
  message: `${flag} on ${model.clientName}.upsertMany() must be a boolean, got ${String(args[flag])}`,
4863
5126
  meta: { model: model.name, argument: flag }
4864
5127
  });
@@ -4871,9 +5134,10 @@ function buildQuery(params) {
4871
5134
  });
4872
5135
  const conflictTarget = resolveUpsertConflictTarget({ model, target: args.conflictTarget });
4873
5136
  const targetColumns = conflictTarget.kind === "columns" ? conflictTarget.fields.map((field) => field.columnName) : [];
5137
+ const expressionUpdate = isPlainObject3(args.update) ? args.update : undefined;
4874
5138
  const updateFields = resolveUpsertUpdateFields({
4875
5139
  model,
4876
- update: args.update,
5140
+ update: expressionUpdate === undefined ? args.update : Object.keys(expressionUpdate),
4877
5141
  targetColumns: new Set(targetColumns),
4878
5142
  skipUpdatedAt: args.skipUpdatedAt === true
4879
5143
  });
@@ -4917,7 +5181,14 @@ function buildQuery(params) {
4917
5181
  columns: targetColumns,
4918
5182
  ...namedConstraint === undefined ? {} : { constraint: namedConstraint },
4919
5183
  ...partialArbiter === undefined ? {} : { conflictWhere: partialArbiter },
4920
- update: Object.fromEntries(updateFields.map((field) => [field.columnName, excludedRef({ column: field.columnName })]))
5184
+ update: Object.fromEntries(updateFields.map((field) => {
5185
+ if (expressionUpdate === undefined || !Object.hasOwn(expressionUpdate, field.name))
5186
+ return [field.columnName, excludedRef({ column: field.columnName })];
5187
+ const fragment = expressionUpdate[field.name];
5188
+ if (!isSqlFragment(fragment))
5189
+ throw validationError4({ message: "Conflict update expressions must be constructed SQL fragments" });
5190
+ return [field.columnName, mapConflictColumns({ fragment, columns: Object.fromEntries(model.fields.map((entry) => [entry.name, entry.columnName])) })];
5191
+ }))
4921
5192
  };
4922
5193
  const upsertStatements = upsertGroups.map((group) => ({
4923
5194
  kind: "insert",
@@ -4938,14 +5209,14 @@ function buildQuery(params) {
4938
5209
  case "updateManyAndReturn": {
4939
5210
  const isUnique = method === "update";
4940
5211
  if (method === "updateManyAndReturn" && !dialect.capabilities.returning) {
4941
- throw unsupportedCapability3({
5212
+ throw unsupportedCapability4({
4942
5213
  dialect: dialect.name,
4943
5214
  feature: "updateManyAndReturn",
4944
5215
  hint: "this dialect has no RETURNING \u2014 use updateMany then findMany, accepting that the read is a separate, unprotected statement"
4945
5216
  });
4946
5217
  }
4947
5218
  if (args.skipUpdatedAt !== undefined && typeof args.skipUpdatedAt !== "boolean") {
4948
- throw validationError3({
5219
+ throw validationError4({
4949
5220
  message: `skipUpdatedAt on ${model.clientName}.${method}() must be a boolean, got ${String(args.skipUpdatedAt)}`,
4950
5221
  meta: { model: model.name, method }
4951
5222
  });
@@ -4977,13 +5248,13 @@ function buildQuery(params) {
4977
5248
  if (args.onConflict !== undefined) {
4978
5249
  const spec = requireObject({ value: args.onConflict, what: "onConflict", model });
4979
5250
  if (typeof spec.constraint !== "string" || spec.constraint.length === 0 || Object.keys(spec).length !== 1) {
4980
- throw validationError3({
5251
+ throw validationError4({
4981
5252
  message: `onConflict on ${model.clientName}.upsert() must be { constraint: string } \u2014 the named unique constraint that arbitrates the conflict`,
4982
5253
  meta: { model: model.name }
4983
5254
  });
4984
5255
  }
4985
5256
  if (!dialect.capabilities.conflictTargetConstraint) {
4986
- throw unsupportedCapability3({
5257
+ throw unsupportedCapability4({
4987
5258
  dialect: dialect.name,
4988
5259
  feature: "a named-constraint conflict target on upsert",
4989
5260
  hint: "only postgres supports ON CONFLICT ON CONSTRAINT \u2014 rely on the where-derived column target instead"
@@ -5001,7 +5272,7 @@ function buildQuery(params) {
5001
5272
  const targetFields = conflictTargetGroup({ model, where }).fields;
5002
5273
  for (const field of targetFields) {
5003
5274
  if (!(field.columnName in row.values)) {
5004
- throw new VibeError8({
5275
+ throw new VibeError10({
5005
5276
  code: "VIBE_INTERNAL",
5006
5277
  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`,
5007
5278
  meta: { model: model.name, column: field.columnName }
@@ -5012,7 +5283,7 @@ function buildQuery(params) {
5012
5283
  for (const [key, value] of Object.entries(where)) {
5013
5284
  if (value === undefined || nativeTargetNames.has(key))
5014
5285
  continue;
5015
- throw new VibeError8({
5286
+ throw new VibeError10({
5016
5287
  code: "VIBE_INTERNAL",
5017
5288
  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`,
5018
5289
  meta: { model: model.name, field: key }
@@ -5065,7 +5336,7 @@ function buildQuery(params) {
5065
5336
  import { getSqlDialect, renderStatement } from "@vibeorm/sql";
5066
5337
 
5067
5338
  // packages/runtime/src/relation-key.ts
5068
- import { VibeError as VibeError9 } from "@vibeorm/schema";
5339
+ import { VibeError as VibeError11 } from "@vibeorm/schema";
5069
5340
  function relationValueKey(params) {
5070
5341
  const { value } = params;
5071
5342
  if (value === null || value === undefined)
@@ -5098,7 +5369,7 @@ function relationTupleKey(params) {
5098
5369
  if (value === null || value === undefined)
5099
5370
  return null;
5100
5371
  if (typeof value === "object" && !(value instanceof Date) && !(value instanceof Uint8Array)) {
5101
- throw new VibeError9({
5372
+ throw new VibeError11({
5102
5373
  code: "VIBE_UNSUPPORTED_CAPABILITY",
5103
5374
  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",
5104
5375
  meta: { received: Object.prototype.toString.call(value) }
@@ -5124,7 +5395,7 @@ function assertWriteScope(params) {
5124
5395
  }
5125
5396
 
5126
5397
  // packages/runtime/src/nested-writes.ts
5127
- import { VibeError as VibeError10 } from "@vibeorm/schema";
5398
+ import { VibeError as VibeError12 } from "@vibeorm/schema";
5128
5399
  function parentTupleOf(params) {
5129
5400
  const { link, row } = params;
5130
5401
  return link.parentKeys.map((field) => row[field.name]);
@@ -5160,7 +5431,7 @@ function detachAssignment(params) {
5160
5431
  const { fields, op, verb, target } = params;
5161
5432
  const optional = fields.filter((field) => field.isOptional);
5162
5433
  if (optional.length === 0) {
5163
- throw validationError4({
5434
+ throw validationError5({
5164
5435
  message: `nested "${verb}" on ${op.link.relation.model}.${op.name} needs an optional foreign key on ${target}.${fields.map((field) => field.name).join(", ")} \u2014 a required relation cannot be detached`,
5165
5436
  meta: { relation: op.name, verb }
5166
5437
  });
@@ -5170,11 +5441,11 @@ function detachAssignment(params) {
5170
5441
  out[field.name] = null;
5171
5442
  return out;
5172
5443
  }
5173
- function validationError4(params) {
5174
- return new VibeError10({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
5444
+ function validationError5(params) {
5445
+ return new VibeError12({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
5175
5446
  }
5176
5447
  function notFound(params) {
5177
- return new VibeError10({ code: "VIBE_NOT_FOUND", message: params.message, meta: params.meta });
5448
+ return new VibeError12({ code: "VIBE_NOT_FOUND", message: params.message, meta: params.meta });
5178
5449
  }
5179
5450
  function isPlainObject4(value) {
5180
5451
  return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
@@ -5225,7 +5496,7 @@ function partitionData(params) {
5225
5496
  continue;
5226
5497
  }
5227
5498
  if (!isPlainObject4(value)) {
5228
- throw validationError4({
5499
+ throw validationError5({
5229
5500
  message: `relation ${model.name}.${key} in data must be an object of nested-write operations`,
5230
5501
  meta: { model: model.name, relation: key }
5231
5502
  });
@@ -5239,7 +5510,7 @@ function partitionData(params) {
5239
5510
  continue;
5240
5511
  }
5241
5512
  if (!allowed.includes(verb)) {
5242
- throw validationError4({
5513
+ throw validationError5({
5243
5514
  message: `unknown nested-write operation "${verb}" on ${model.name}.${key} (${phase}) \u2014 supported: ${allowed.join(", ")}`,
5244
5515
  meta: { model: model.name, relation: key, verb, phase }
5245
5516
  });
@@ -5247,7 +5518,7 @@ function partitionData(params) {
5247
5518
  verbs[verb] = operand;
5248
5519
  }
5249
5520
  if (Object.keys(verbs).length === 0) {
5250
- throw validationError4({
5521
+ throw validationError5({
5251
5522
  message: `relation ${model.name}.${key} in data carries no nested-write operation`,
5252
5523
  meta: { model: model.name, relation: key }
5253
5524
  });
@@ -5260,14 +5531,14 @@ function toItems(params) {
5260
5531
  const { op, verb, operand } = params;
5261
5532
  const list = Array.isArray(operand) ? operand : [operand];
5262
5533
  if (!op.link.relation.isList && Array.isArray(operand)) {
5263
- throw validationError4({
5534
+ throw validationError5({
5264
5535
  message: `nested "${verb}" on to-one relation ${op.link.relation.model}.${op.name} cannot take an array`,
5265
5536
  meta: { relation: op.name, verb }
5266
5537
  });
5267
5538
  }
5268
5539
  return list.map((item) => {
5269
5540
  if (!isPlainObject4(item)) {
5270
- throw validationError4({
5541
+ throw validationError5({
5271
5542
  message: `nested "${verb}" on ${op.link.relation.model}.${op.name} expects object items`,
5272
5543
  meta: { relation: op.name, verb }
5273
5544
  });
@@ -5279,7 +5550,7 @@ function requireShape(params) {
5279
5550
  const { op, verb, item, keys } = params;
5280
5551
  for (const key of keys) {
5281
5552
  if (!isPlainObject4(item[key])) {
5282
- throw validationError4({
5553
+ throw validationError5({
5283
5554
  message: `nested "${verb}" on ${op.link.relation.model}.${op.name} needs an object "${key}"`,
5284
5555
  meta: { relation: op.name, verb, missing: key }
5285
5556
  });
@@ -5399,7 +5670,7 @@ function singleVerb(params) {
5399
5670
  const { op } = params;
5400
5671
  const entries = Object.entries(op.verbs);
5401
5672
  if (entries.length !== 1) {
5402
- throw validationError4({
5673
+ throw validationError5({
5403
5674
  message: `to-one relation ${op.link.relation.model}.${op.name} takes exactly one nested-write operation, got: ${entries.map(([verb]) => verb).join(", ")}`,
5404
5675
  meta: { relation: op.name, verbs: entries.map(([verb]) => verb) }
5405
5676
  });
@@ -5553,7 +5824,7 @@ function updateDataOf(params) {
5553
5824
  if ("data" in operand && isPlainObject4(operand.data))
5554
5825
  return operand.data;
5555
5826
  if ("data" in operand) {
5556
- throw validationError4({
5827
+ throw validationError5({
5557
5828
  message: `nested "update" on ${op.link.relation.model}.${op.name} needs an object "data"`,
5558
5829
  meta: { relation: op.name }
5559
5830
  });
@@ -5610,7 +5881,7 @@ async function resolveOwnerUpdateVerb(params) {
5610
5881
  if (operand === false)
5611
5882
  return;
5612
5883
  if (!link.relation.isOptional) {
5613
- throw validationError4({
5884
+ throw validationError5({
5614
5885
  message: `nested "disconnect" on required relation ${link.relation.model}.${op.name} \u2014 connect a replacement instead`,
5615
5886
  meta: { relation: op.name }
5616
5887
  });
@@ -5622,7 +5893,7 @@ async function resolveOwnerUpdateVerb(params) {
5622
5893
  if (operand === false)
5623
5894
  return;
5624
5895
  if (!link.relation.isOptional) {
5625
- throw validationError4({
5896
+ throw validationError5({
5626
5897
  message: `nested "delete" on required relation ${link.relation.model}.${op.name} \u2014 the parent cannot exist without it`,
5627
5898
  meta: { relation: op.name }
5628
5899
  });
@@ -5642,7 +5913,7 @@ async function resolveOwnerUpdateVerb(params) {
5642
5913
  }
5643
5914
  case "update": {
5644
5915
  if (!isPlainObject4(operand)) {
5645
- throw validationError4({
5916
+ throw validationError5({
5646
5917
  message: `nested "update" on ${link.relation.model}.${op.name} expects an object`,
5647
5918
  meta: { relation: op.name }
5648
5919
  });
@@ -5665,7 +5936,7 @@ async function resolveOwnerUpdateVerb(params) {
5665
5936
  }
5666
5937
  default: {
5667
5938
  if (!isPlainObject4(operand)) {
5668
- throw validationError4({
5939
+ throw validationError5({
5669
5940
  message: `nested "upsert" on ${link.relation.model}.${op.name} expects an object`,
5670
5941
  meta: { relation: op.name }
5671
5942
  });
@@ -5806,7 +6077,7 @@ async function applyChildUpdateOps(params) {
5806
6077
  case "update": {
5807
6078
  if (link.kind === "toOneInverse") {
5808
6079
  if (!isPlainObject4(operand)) {
5809
- throw validationError4({
6080
+ throw validationError5({
5810
6081
  message: `nested "update" on ${link.relation.model}.${op.name} expects an object`,
5811
6082
  meta: { relation: op.name }
5812
6083
  });
@@ -5869,7 +6140,7 @@ async function applyChildUpdateOps(params) {
5869
6140
  for (const item of items) {
5870
6141
  const data = item.data;
5871
6142
  if (!isPlainObject4(data)) {
5872
- throw validationError4({
6143
+ throw validationError5({
5873
6144
  message: `nested "updateMany" on ${link.relation.model}.${op.name} needs an object "data"`,
5874
6145
  meta: { relation: op.name }
5875
6146
  });
@@ -5940,7 +6211,7 @@ async function applyChildUpdateOps(params) {
5940
6211
  default: {
5941
6212
  if (link.kind === "toOneInverse") {
5942
6213
  if (!isPlainObject4(operand)) {
5943
- throw validationError4({
6214
+ throw validationError5({
5944
6215
  message: `nested "upsert" on ${link.relation.model}.${op.name} expects an object`,
5945
6216
  meta: { relation: op.name }
5946
6217
  });
@@ -6278,7 +6549,7 @@ function previewOperation(params) {
6278
6549
  };
6279
6550
  }
6280
6551
  // packages/runtime/src/diagnostics/plan.ts
6281
- import { VibeError as VibeError11, unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
6552
+ import { VibeError as VibeError13, unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
6282
6553
  import { NATIVE_RLS, getSqlDialect as getSqlDialect2, renderPostgresExplain, renderPostgresWorkload, renderStatement as renderStatement2 } from "@vibeorm/sql";
6283
6554
  var PLAN_DEFAULTS = Object.freeze({ maxNodes: 200, maxConditionLength: 2000 });
6284
6555
  var CONDITION_KEYS = [
@@ -6297,7 +6568,7 @@ var CONDITION_KEYS = [
6297
6568
  "Output"
6298
6569
  ];
6299
6570
  function planError(params) {
6300
- return new VibeError11({ code: "VIBE_ADAPTER", message: params.message, meta: {} });
6571
+ return new VibeError13({ code: "VIBE_ADAPTER", message: params.message, meta: {} });
6301
6572
  }
6302
6573
  function asRecord(value) {
6303
6574
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
@@ -6330,14 +6601,14 @@ function parsePostgresPlan(params) {
6330
6601
  const maxNodes = params.maxNodes ?? PLAN_DEFAULTS.maxNodes;
6331
6602
  const includeLiterals = params.includeLiterals === true;
6332
6603
  if (!Number.isInteger(maxNodes) || maxNodes < 1) {
6333
- throw new VibeError11({ code: "VIBE_CONFIG", message: `maxNodes must be a positive integer, got ${String(maxNodes)}`, meta: { maxNodes } });
6604
+ throw new VibeError13({ code: "VIBE_CONFIG", message: `maxNodes must be a positive integer, got ${String(maxNodes)}`, meta: { maxNodes } });
6334
6605
  }
6335
6606
  let parsed = payload;
6336
6607
  if (typeof payload === "string") {
6337
6608
  try {
6338
6609
  parsed = JSON.parse(payload);
6339
6610
  } catch {
6340
- throw new VibeError11({
6611
+ throw new VibeError13({
6341
6612
  code: "VIBE_ADAPTER",
6342
6613
  message: "EXPLAIN returned malformed JSON",
6343
6614
  meta: { reason: "malformed-plan-json", mode }
@@ -6404,7 +6675,7 @@ function parsePostgresPlan(params) {
6404
6675
  }
6405
6676
  var WRITE_LEADERS = ["INSERT", "UPDATE", "DELETE", "MERGE", "TRUNCATE", "CREATE", "DROP", "ALTER", "GRANT", "REVOKE", "COPY", "CALL", "DO"];
6406
6677
  function refuse(params) {
6407
- return new VibeError11({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta ?? {} });
6678
+ return new VibeError13({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta ?? {} });
6408
6679
  }
6409
6680
  function assertExecutableRead(params) {
6410
6681
  const normalized = normalizeSql({ text: params.text });
@@ -6443,7 +6714,7 @@ async function readPlanContext(params) {
6443
6714
  }
6444
6715
  function requirePostgres(params) {
6445
6716
  if (params.dialect !== "postgres") {
6446
- throw unsupportedCapability4({
6717
+ throw unsupportedCapability5({
6447
6718
  dialect: params.dialect,
6448
6719
  feature: "query plan inspection",
6449
6720
  hint: "plan inspection is PostgreSQL-shaped; operation tracing (telemetry) works on every dialect"
@@ -6452,7 +6723,7 @@ function requirePostgres(params) {
6452
6723
  return getSqlDialect2({ dialect: params.dialect });
6453
6724
  }
6454
6725
  async function explainSql(params) {
6455
- const { executor, sql, rlsContext } = params;
6726
+ const { executor, sql: sql2, rlsContext } = params;
6456
6727
  const mode = params.mode ?? "estimate";
6457
6728
  const dialect = requirePostgres({ dialect: params.dialect });
6458
6729
  const warnings = [];
@@ -6469,7 +6740,7 @@ async function explainSql(params) {
6469
6740
  meta: { mode }
6470
6741
  });
6471
6742
  }
6472
- assertExecutableRead({ text: sql.text });
6743
+ assertExecutableRead({ text: sql2.text });
6473
6744
  warnings.push("this statement was EXECUTED to produce observed rows and milliseconds; a read-only transaction and a keyword check are not a proof of safety, because a SELECT can call a function with side effects");
6474
6745
  }
6475
6746
  const contextKeys = rlsContext === undefined ? [] : Object.keys(rlsContext);
@@ -6495,7 +6766,7 @@ async function explainSql(params) {
6495
6766
  const context = await readPlanContext({ executor, dialect, rlsContextKeys: contextKeys });
6496
6767
  if (!context.representative) {
6497
6768
  if (params.requireRepresentativeContext === true) {
6498
- throw new VibeError11({
6769
+ throw new VibeError13({
6499
6770
  code: "VIBE_RLS_CONTEXT",
6500
6771
  message: `refusing to report a plan taken under a non-representative identity: ${context.reason ?? "role bypasses row policies"}`,
6501
6772
  meta: { currentUser: context.currentUser, bypassRls: context.bypassRls, superuser: context.superuser }
@@ -6505,7 +6776,7 @@ async function explainSql(params) {
6505
6776
  }
6506
6777
  const explain = renderPostgresExplain({
6507
6778
  dialect,
6508
- query: { text: sql.text, values: [...sql.values], returning: "none" },
6779
+ query: { text: sql2.text, values: [...sql2.values], returning: "none" },
6509
6780
  options: mode === "analyze" ? { analyze: true, buffers: true } : {}
6510
6781
  });
6511
6782
  const rows = await run(explain);
@@ -6577,7 +6848,7 @@ async function explainOperation(params) {
6577
6848
  });
6578
6849
  }
6579
6850
  // packages/runtime/src/diagnostics/workload.ts
6580
- import { unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
6851
+ import { unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
6581
6852
  import { getSqlDialect as getSqlDialect3, renderPostgresWorkload as renderPostgresWorkload2 } from "@vibeorm/sql";
6582
6853
  var STATEMENTS_INFO_MIN_VERSION = 140000;
6583
6854
  var STATEMENTS_MIN_VERSION = 130000;
@@ -6610,7 +6881,7 @@ async function readAvailability(params) {
6610
6881
  async function readWorkloadStatistics(params) {
6611
6882
  const { executor } = params;
6612
6883
  if (params.dialect !== "postgres") {
6613
- throw unsupportedCapability5({
6884
+ throw unsupportedCapability6({
6614
6885
  dialect: params.dialect,
6615
6886
  feature: "workload statistics (pg_stat_statements)",
6616
6887
  hint: "server workload statistics are PostgreSQL-only; operation telemetry works on every dialect"
@@ -6688,7 +6959,7 @@ async function readWorkloadStatistics(params) {
6688
6959
  };
6689
6960
  }
6690
6961
  // packages/runtime/src/diagnostics/insight.ts
6691
- import { VibeError as VibeError12 } from "@vibeorm/schema";
6962
+ import { VibeError as VibeError14 } from "@vibeorm/schema";
6692
6963
  var DIAGNOSTIC_THRESHOLDS = Object.freeze({
6693
6964
  repeatThreshold: 5,
6694
6965
  minRepeatThreshold: 2,
@@ -6706,7 +6977,7 @@ function bounded(params) {
6706
6977
  if (value === undefined)
6707
6978
  return fallback;
6708
6979
  if (!Number.isInteger(value) || value < min || value > max) {
6709
- throw new VibeError12({
6980
+ throw new VibeError14({
6710
6981
  code: "VIBE_CONFIG",
6711
6982
  message: `${name} must be an integer between ${min} and ${max}, got ${String(value)}`,
6712
6983
  meta: { [name]: value }
@@ -6929,11 +7200,11 @@ function recommendFromPlan(params) {
6929
7200
  return findings.slice(0, maxFindings);
6930
7201
  }
6931
7202
  // packages/runtime/src/relation-loader.ts
6932
- import { VibeError as VibeError14 } from "@vibeorm/schema";
7203
+ import { VibeError as VibeError16 } from "@vibeorm/schema";
6933
7204
  import { renderStatement as renderStatement3 } from "@vibeorm/sql";
6934
7205
 
6935
7206
  // packages/runtime/src/computed.ts
6936
- import { VibeError as VibeError13 } from "@vibeorm/schema";
7207
+ import { VibeError as VibeError15 } from "@vibeorm/schema";
6937
7208
  function sameNeeds(a, b) {
6938
7209
  if (a.length !== b.length)
6939
7210
  return false;
@@ -6969,7 +7240,7 @@ function assertComputedManifest(params) {
6969
7240
  problems.push(`"${key}" is configured but missing from the generated client`);
6970
7241
  }
6971
7242
  if (problems.length > 0) {
6972
- throw new VibeError13({
7243
+ throw new VibeError15({
6973
7244
  code: "VIBE_STALE_CLIENT",
6974
7245
  message: `computed-field config changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
6975
7246
  meta: { problems }
@@ -7101,7 +7372,7 @@ function chunkKeys(params) {
7101
7372
  const rendered = renderStatement3({ dialect: ctx.dialect, statement: build(keys.slice(0, 1)) });
7102
7373
  fixed = rendered.values.length - (expands ? arity : 1);
7103
7374
  if (fixed + arity > budget) {
7104
- throw new VibeError14({
7375
+ throw new VibeError16({
7105
7376
  code: "VIBE_UNSUPPORTED_CAPABILITY",
7106
7377
  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`,
7107
7378
  meta: { relation, dialect: ctx.dialect.name, required: fixed + arity, budget }
@@ -7231,7 +7502,7 @@ async function loadManyToMany(params) {
7231
7502
  const link = node.link;
7232
7503
  const join = link.join;
7233
7504
  if (join === null) {
7234
- throw new VibeError14({
7505
+ throw new VibeError16({
7235
7506
  code: "VIBE_INTERNAL",
7236
7507
  message: `manyToMany relation "${node.name}" resolved without join-table wiring`
7237
7508
  });
@@ -7510,7 +7781,7 @@ async function attachLateralRelations(params) {
7510
7781
  try {
7511
7782
  parsed = JSON.parse(raw);
7512
7783
  } catch (cause) {
7513
- throw new VibeError14({
7784
+ throw new VibeError16({
7514
7785
  code: "VIBE_INTERNAL",
7515
7786
  message: `relation column "${column}" returned text that is not valid JSON`,
7516
7787
  meta: { relation: node.name },
@@ -7520,7 +7791,7 @@ async function attachLateralRelations(params) {
7520
7791
  }
7521
7792
  if (isList) {
7522
7793
  if (!Array.isArray(parsed)) {
7523
- throw new VibeError14({ code: "VIBE_INTERNAL", message: "to-many relation payload must be an array", meta: { relation: node.name } });
7794
+ throw new VibeError16({ code: "VIBE_INTERNAL", message: "to-many relation payload must be an array", meta: { relation: node.name } });
7524
7795
  }
7525
7796
  const children = parsed.map(decodePosition);
7526
7797
  for (const child of children) {
@@ -7552,7 +7823,7 @@ async function attachLateralRelations(params) {
7552
7823
  const raw = row[column];
7553
7824
  const count = typeof raw === "number" || typeof raw === "bigint" || typeof raw === "string" && /^\d+$/.test(raw) ? Number(raw) : NaN;
7554
7825
  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 } });
7826
+ throw new VibeError16({ code: "VIBE_INTERNAL", message: "relation count payload must be a non-negative safe integer", meta: { relation: selection.counts[index]?.name } });
7556
7827
  }
7557
7828
  counts[selection.counts[index].name] = count;
7558
7829
  delete row[column];
@@ -7561,7 +7832,7 @@ async function attachLateralRelations(params) {
7561
7832
  }
7562
7833
  }
7563
7834
  // packages/runtime/src/extensions.ts
7564
- import { VibeError as VibeError15, extensionManifestEntry } from "@vibeorm/schema";
7835
+ import { VibeError as VibeError17, extensionManifestEntry } from "@vibeorm/schema";
7565
7836
  function assertExtensionManifest(params) {
7566
7837
  const { manifest } = params;
7567
7838
  const instances = params.instances ?? [];
@@ -7584,7 +7855,7 @@ function assertExtensionManifest(params) {
7584
7855
  problems.push(`"${name}" is configured but missing from the generated client`);
7585
7856
  }
7586
7857
  if (problems.length > 0) {
7587
- throw new VibeError15({
7858
+ throw new VibeError17({
7588
7859
  code: "VIBE_STALE_CLIENT",
7589
7860
  message: `extension list changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
7590
7861
  meta: { manifest, problems }
@@ -7599,7 +7870,7 @@ function buildExtensionOperatorMap(params) {
7599
7870
  for (const operator of extension.sql?.whereOperators ?? []) {
7600
7871
  const existing = owner.get(operator.name);
7601
7872
  if (existing !== undefined) {
7602
- throw new VibeError15({
7873
+ throw new VibeError17({
7603
7874
  code: "VIBE_CONFIG",
7604
7875
  message: `where operator "${operator.name}" is claimed by both "${existing}" and "${extension.name}"`,
7605
7876
  meta: { operator: operator.name }
@@ -7615,7 +7886,7 @@ function buildExtensionOperatorMap(params) {
7615
7886
  options: extension.options
7616
7887
  });
7617
7888
  if ("error" in compiled) {
7618
- throw new VibeError15({
7889
+ throw new VibeError17({
7619
7890
  code: "VIBE_VALIDATION",
7620
7891
  message: `"${operator.name}" on ${model}.${field}: ${compiled.error}`,
7621
7892
  meta: { model, field, operator: operator.name, extension: extension.name }
@@ -7647,9 +7918,53 @@ function buildConnectHooks(params) {
7647
7918
  return hooks;
7648
7919
  }
7649
7920
  // packages/runtime/src/views.ts
7650
- import { VibeError as VibeError16 } from "@vibeorm/schema";
7651
- function validationError5(params) {
7652
- return new VibeError16({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
7921
+ import { VibeError as VibeError19 } from "@vibeorm/schema";
7922
+
7923
+ // packages/runtime/src/sql-access.ts
7924
+ import { VibeError as VibeError18 } from "@vibeorm/schema";
7925
+ import { isSqlFragment as isSqlFragment2 } from "@vibeorm/sql";
7926
+ function containsSqlFragment(value, seen = new Set) {
7927
+ if (isSqlFragment2(value))
7928
+ return true;
7929
+ if (typeof value !== "object" || value === null || seen.has(value))
7930
+ return false;
7931
+ seen.add(value);
7932
+ if (Array.isArray(value))
7933
+ return value.some((entry) => containsSqlFragment(entry, seen));
7934
+ return Object.values(value).some((entry) => containsSqlFragment(entry, seen));
7935
+ }
7936
+ function restrictedSqlArgs(params) {
7937
+ const seen = new Map;
7938
+ const copy = (value) => {
7939
+ if (isSqlFragment2(value))
7940
+ throw new VibeError18({ code: "VIBE_VALIDATION", message: `SQL fragments are unavailable through ${params.path}; use the unrestricted client`, meta: { path: params.path } });
7941
+ if (typeof value !== "object" || value === null)
7942
+ return value;
7943
+ if (seen.has(value))
7944
+ return seen.get(value);
7945
+ if (Array.isArray(value)) {
7946
+ const out2 = [];
7947
+ seen.set(value, out2);
7948
+ for (const item of value)
7949
+ out2.push(copy(item));
7950
+ return out2;
7951
+ }
7952
+ if (value instanceof Date)
7953
+ return new Date(value.getTime());
7954
+ if (value instanceof Uint8Array)
7955
+ return new Uint8Array(value);
7956
+ const out = Object.create(null);
7957
+ seen.set(value, out);
7958
+ for (const [key, child] of Object.entries(value))
7959
+ out[key] = copy(child);
7960
+ return out;
7961
+ };
7962
+ return copy(params.value);
7963
+ }
7964
+
7965
+ // packages/runtime/src/views.ts
7966
+ function validationError6(params) {
7967
+ return new VibeError19({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
7653
7968
  }
7654
7969
 
7655
7970
  class ViewResult {
@@ -7704,7 +8019,7 @@ function isPlainObject5(value) {
7704
8019
  function resolveDefinition(params) {
7705
8020
  const { meta, name, definition } = params;
7706
8021
  if (!isPlainObject5(definition)) {
7707
- throw validationError5({
8022
+ throw validationError6({
7708
8023
  message: `view "${name}": definition must be an object of { modelKey: { field: true } }`,
7709
8024
  meta: { view: name }
7710
8025
  });
@@ -7715,13 +8030,13 @@ function resolveDefinition(params) {
7715
8030
  continue;
7716
8031
  const model = meta.modelByName.get(key);
7717
8032
  if (model === undefined) {
7718
- throw validationError5({
8033
+ throw validationError6({
7719
8034
  message: `view "${name}": unknown model "${key}" in the definition`,
7720
8035
  meta: { view: name, model: key }
7721
8036
  });
7722
8037
  }
7723
8038
  if (!isPlainObject5(fieldSpec)) {
7724
- throw validationError5({
8039
+ throw validationError6({
7725
8040
  message: `view "${name}": definition.${key} must be an object of { field: true }`,
7726
8041
  meta: { view: name, model: model.name }
7727
8042
  });
@@ -7731,14 +8046,14 @@ function resolveDefinition(params) {
7731
8046
  if (flag === undefined || flag === false)
7732
8047
  continue;
7733
8048
  if (flag !== true) {
7734
- throw validationError5({
8049
+ throw validationError6({
7735
8050
  message: `view "${name}": definition.${key}.${field} must be true`,
7736
8051
  meta: { view: name, model: model.name, field }
7737
8052
  });
7738
8053
  }
7739
8054
  if (!model.fieldByName.has(field)) {
7740
8055
  const isRelation = model.relationByName.has(field);
7741
- throw validationError5({
8056
+ throw validationError6({
7742
8057
  message: isRelation ? `view "${name}": "${field}" on model "${model.name}" is a relation \u2014 a view definition lists scalar fields; relations are restricted through the related model's own entry` : `view "${name}": unknown field "${field}" on model "${model.name}"`,
7743
8058
  meta: { view: name, model: model.name, field }
7744
8059
  });
@@ -7746,7 +8061,7 @@ function resolveDefinition(params) {
7746
8061
  fields.add(field);
7747
8062
  }
7748
8063
  if (fields.size === 0) {
7749
- throw validationError5({
8064
+ throw validationError6({
7750
8065
  message: `view "${name}": definition.${key} exposes no fields`,
7751
8066
  meta: { view: name, model: model.name }
7752
8067
  });
@@ -7754,7 +8069,7 @@ function resolveDefinition(params) {
7754
8069
  resolved.set(model.clientName, { model, fields });
7755
8070
  }
7756
8071
  if (resolved.size === 0) {
7757
- throw validationError5({
8072
+ throw validationError6({
7758
8073
  message: `view "${name}": the definition names no models`,
7759
8074
  meta: { view: name }
7760
8075
  });
@@ -7776,7 +8091,7 @@ function revealSet(params) {
7776
8091
  if (reveal === undefined)
7777
8092
  return revealed;
7778
8093
  if (!isPlainObject5(reveal)) {
7779
- throw validationError5({
8094
+ throw validationError6({
7780
8095
  message: `view "${name}": reveal must be an object of { field: true }`,
7781
8096
  meta: { view: name, model: mask.model.name }
7782
8097
  });
@@ -7785,13 +8100,13 @@ function revealSet(params) {
7785
8100
  if (flag === undefined || flag === false)
7786
8101
  continue;
7787
8102
  if (flag !== true) {
7788
- throw validationError5({
8103
+ throw validationError6({
7789
8104
  message: `view "${name}": reveal.${field} must be true or false`,
7790
8105
  meta: { view: name, model: mask.model.name, field }
7791
8106
  });
7792
8107
  }
7793
8108
  if (!mask.model.fieldByName.has(field)) {
7794
- throw validationError5({
8109
+ throw validationError6({
7795
8110
  message: `view "${name}": cannot reveal "${field}" \u2014 not a scalar field of model "${mask.model.name}"`,
7796
8111
  meta: { view: name, model: mask.model.name, field }
7797
8112
  });
@@ -7805,7 +8120,7 @@ function mergeLevel(params) {
7805
8120
  const { meta, name, definition, mask, args } = params;
7806
8121
  const model = mask.model;
7807
8122
  if (args.omit !== undefined) {
7808
- throw validationError5({
8123
+ throw validationError6({
7809
8124
  message: `view "${name}": omit is not available on a view query \u2014 the definition already restricts ${model.name}'s fields`,
7810
8125
  meta: { view: name, model: model.name }
7811
8126
  });
@@ -7814,7 +8129,7 @@ function mergeLevel(params) {
7814
8129
  let scalars;
7815
8130
  const userSelect = args.select;
7816
8131
  if (userSelect !== undefined && !isPlainObject5(userSelect)) {
7817
- throw validationError5({
8132
+ throw validationError6({
7818
8133
  message: `view "${name}": select must be an object`,
7819
8134
  meta: { view: name, model: model.name }
7820
8135
  });
@@ -7838,7 +8153,7 @@ function mergeLevel(params) {
7838
8153
  if (source === undefined)
7839
8154
  return;
7840
8155
  if (!isPlainObject5(source)) {
7841
- throw validationError5({
8156
+ throw validationError6({
7842
8157
  message: `view "${name}": ${argName} must be an object`,
7843
8158
  meta: { view: name, model: model.name }
7844
8159
  });
@@ -7853,7 +8168,7 @@ function mergeLevel(params) {
7853
8168
  if (model.relationByName.has(key))
7854
8169
  relationSpecs.set(key, spec);
7855
8170
  else if (argName === "include") {
7856
- throw validationError5({
8171
+ throw validationError6({
7857
8172
  message: `view "${name}": unknown relation "${key}" on model "${model.name}" in include`,
7858
8173
  meta: { view: name, model: model.name, relation: key }
7859
8174
  });
@@ -7886,7 +8201,7 @@ function mergeLevel(params) {
7886
8201
  continue;
7887
8202
  }
7888
8203
  if (!isPlainObject5(spec)) {
7889
- throw validationError5({
8204
+ throw validationError6({
7890
8205
  message: `view "${name}": include.${relationName} must be true or an object`,
7891
8206
  meta: { view: name, model: model.name, relation: relationName }
7892
8207
  });
@@ -7947,9 +8262,9 @@ var VIEW_WRITE_METHODS = [
7947
8262
  function createViewDelegate(params) {
7948
8263
  const { meta, name, definition, mask, delegate } = params;
7949
8264
  const merge = (args) => {
7950
- const raw = args ?? {};
8265
+ const raw = restrictedSqlArgs({ value: args ?? {}, path: "$defineView" });
7951
8266
  if (!isPlainObject5(raw)) {
7952
- throw validationError5({
8267
+ throw validationError6({
7953
8268
  message: `view "${name}": query args must be an object`,
7954
8269
  meta: { view: name, model: mask.model.name }
7955
8270
  });
@@ -7982,13 +8297,13 @@ function createViewDelegate(params) {
7982
8297
  const { merged, plan } = merge(args);
7983
8298
  return wrapRows({ rows: [await delegate.findUniqueOrThrow(merged)], plan })[0];
7984
8299
  },
7985
- count: (args) => delegate.count(args),
7986
- aggregate: (args) => delegate.aggregate(args),
7987
- groupBy: (args) => delegate.groupBy(args)
8300
+ count: (args) => delegate.count(restrictedSqlArgs({ value: args, path: "$defineView" })),
8301
+ aggregate: (args) => delegate.aggregate(restrictedSqlArgs({ value: args, path: "$defineView" })),
8302
+ groupBy: (args) => delegate.groupBy(restrictedSqlArgs({ value: args, path: "$defineView" }))
7988
8303
  };
7989
8304
  for (const method of VIEW_WRITE_METHODS) {
7990
8305
  view[method] = () => {
7991
- throw validationError5({
8306
+ throw validationError6({
7992
8307
  message: `view "${name}" is read-only \u2014 ${method}() is not available on a field-masking view; write through the base client instead`,
7993
8308
  meta: { view: name, model: mask.model.name, method }
7994
8309
  });
@@ -8003,7 +8318,7 @@ function createView(params) {
8003
8318
  for (const [clientName, mask] of resolved) {
8004
8319
  const delegate = models[clientName];
8005
8320
  if (delegate === undefined) {
8006
- throw new VibeError16({
8321
+ throw new VibeError19({
8007
8322
  code: "VIBE_INTERNAL",
8008
8323
  message: `view "${name}": no delegate for model "${mask.model.name}"`,
8009
8324
  meta: { view: name, model: mask.model.name }
@@ -8019,9 +8334,9 @@ function attachDefineView(params) {
8019
8334
  Object.defineProperty(client, "$defineView", { value, enumerable: true });
8020
8335
  }
8021
8336
  // packages/runtime/src/validators.ts
8022
- import { VibeError as VibeError17 } from "@vibeorm/schema";
8337
+ import { VibeError as VibeError20 } from "@vibeorm/schema";
8023
8338
  function validationFailed(params) {
8024
- return new VibeError17({
8339
+ return new VibeError20({
8025
8340
  code: "VIBE_VALIDATION",
8026
8341
  message: `validation failed for ${params.model}.${params.field}: ${params.issues[0]?.message ?? "invalid value"}`,
8027
8342
  meta: { model: params.model, field: params.field, issues: params.issues }
@@ -8030,7 +8345,7 @@ function validationFailed(params) {
8030
8345
  function validateValue(params) {
8031
8346
  const result = params.schema["~standard"].validate(params.value);
8032
8347
  if (result instanceof Promise) {
8033
- throw new VibeError17({
8348
+ throw new VibeError20({
8034
8349
  code: "VIBE_VALIDATION",
8035
8350
  message: `async validator on ${params.model}.${params.field} \u2014 write validators must be synchronous (validate before calling the client for async rules)`,
8036
8351
  meta: { model: params.model, field: params.field }
@@ -8153,9 +8468,270 @@ function deferPolicyOperation(params) {
8153
8468
  }
8154
8469
 
8155
8470
  // packages/runtime/src/policy.ts
8156
- import { VibeError as VibeError18 } from "@vibeorm/schema";
8471
+ import { VibeError as VibeError21 } from "@vibeorm/schema";
8472
+
8473
+ // packages/runtime/src/read-only.ts
8474
+ import { ReadOnlyClientError } from "@vibeorm/schema";
8475
+ var READ_DELEGATE_METHODS = Object.freeze([
8476
+ "findMany",
8477
+ "findFirst",
8478
+ "findFirstOrThrow",
8479
+ "findUnique",
8480
+ "findUniqueOrThrow",
8481
+ "count",
8482
+ "aggregate",
8483
+ "groupBy"
8484
+ ]);
8485
+ var WRITE_DELEGATE_METHODS = Object.freeze([
8486
+ "create",
8487
+ "createMany",
8488
+ "createManyAndReturn",
8489
+ "update",
8490
+ "updateMany",
8491
+ "updateManyAndReturn",
8492
+ "upsert",
8493
+ "upsertMany",
8494
+ "delete",
8495
+ "deleteMany"
8496
+ ]);
8497
+ var DELEGATE_METHOD_ACCESS = Object.freeze(Object.fromEntries([
8498
+ ...READ_DELEGATE_METHODS.map((method) => [method, "read"]),
8499
+ ...WRITE_DELEGATE_METHODS.map((method) => [method, "write"])
8500
+ ]));
8501
+ function delegateMethodAccess(params) {
8502
+ return Object.hasOwn(DELEGATE_METHOD_ACCESS, params.method) ? DELEGATE_METHOD_ACCESS[params.method] : undefined;
8503
+ }
8504
+ var CLIENT_MEMBER_ACCESS = Object.freeze({
8505
+ $models: "rebuilt",
8506
+ $on: "forwarded",
8507
+ $transaction: "rebuilt",
8508
+ $defineView: "rebuilt",
8509
+ $scoped: "rebuilt",
8510
+ $withPolicy: "rebuilt",
8511
+ $bypassPolicy: "rebuilt",
8512
+ $readOnly: "rebuilt",
8513
+ $querySql: "raw",
8514
+ $executeSql: "raw",
8515
+ $queryRaw: "raw",
8516
+ $executeRaw: "raw",
8517
+ $queryRawUnsafe: "raw",
8518
+ $executeRawUnsafe: "raw",
8519
+ $connect: "control",
8520
+ $disconnect: "control",
8521
+ $advisoryLock: "control",
8522
+ $tryAdvisoryLock: "control",
8523
+ $withContext: "control"
8524
+ });
8525
+ function clientMemberAccess(params) {
8526
+ return Object.hasOwn(CLIENT_MEMBER_ACCESS, params.member) ? CLIENT_MEMBER_ACCESS[params.member] : undefined;
8527
+ }
8528
+ var REASON_HINTS = Object.freeze({
8529
+ write: "it writes rows \u2014 run it on the writable client",
8530
+ raw: "raw SQL is not available on a read-only client (a SELECT-shaped statement is not proof of a read); run it on the writable client, or use a database read-only role when the database itself must enforce it",
8531
+ control: "it controls the connection or the transaction rather than reading; run it on the writable client",
8532
+ unclassified: "it is not classified as a read \u2014 extensions opt in per surface, and anything unclassified stays denied",
8533
+ foreign_operation: "$transaction([...]) on a read-only client takes read operations only; this one would write"
8534
+ });
8535
+ function readOnlyRefusal(params) {
8536
+ const { path, reason } = params;
8537
+ return new ReadOnlyClientError({
8538
+ path,
8539
+ reason,
8540
+ message: `$readOnly: ${path}${reason === "foreign_operation" ? "" : "()"} is not available on a read-only client \u2014 ${REASON_HINTS[reason]}`,
8541
+ ...params.meta === undefined ? {} : { meta: params.meta }
8542
+ });
8543
+ }
8544
+ var contexts = new WeakMap;
8545
+ var facades = new WeakMap;
8546
+ function bound(params) {
8547
+ const { value, receiver } = params;
8548
+ return typeof value === "function" ? value.bind(receiver) : undefined;
8549
+ }
8550
+ function classifiedMount(params) {
8551
+ const { value, readOnly } = params;
8552
+ if (readOnly === undefined)
8553
+ return;
8554
+ if (readOnly === true)
8555
+ return typeof value === "function" ? (...args) => {
8556
+ if (containsSqlFragment(args))
8557
+ return Promise.reject(readOnlyRefusal({ path: "extension", reason: "raw" }));
8558
+ return value(...restrictedSqlArgs({ value: args, path: "$readOnly extension" }));
8559
+ } : undefined;
8560
+ if (typeof value !== "object" || value === null)
8561
+ return;
8562
+ const source = value;
8563
+ const namespace = {};
8564
+ for (const member of readOnly) {
8565
+ const fn = bound({ value: source[member], receiver: namespace });
8566
+ if (fn !== undefined)
8567
+ namespace[member] = (...args) => {
8568
+ if (containsSqlFragment(args))
8569
+ return Promise.reject(readOnlyRefusal({ path: member, reason: "raw" }));
8570
+ return fn(...restrictedSqlArgs({ value: args, path: "$readOnly extension" }));
8571
+ };
8572
+ }
8573
+ return Object.freeze(namespace);
8574
+ }
8575
+ function readOnlyDelegate(params) {
8576
+ const { model, mounts } = params;
8577
+ const base = params.base;
8578
+ const delegate = {};
8579
+ for (const method of READ_DELEGATE_METHODS) {
8580
+ const fn = bound({ value: base[method], receiver: base });
8581
+ if (fn !== undefined)
8582
+ delegate[method] = (...args) => {
8583
+ if (containsSqlFragment(args))
8584
+ return Promise.reject(readOnlyRefusal({ path: `${model.clientName}.${method}`, reason: "raw" }));
8585
+ const operation = fn(...args);
8586
+ params.operations.restrictSql?.({ value: operation });
8587
+ return operation;
8588
+ };
8589
+ }
8590
+ for (const method of WRITE_DELEGATE_METHODS) {
8591
+ const refusal = () => Promise.reject(readOnlyRefusal({
8592
+ path: `${model.clientName}.${method}`,
8593
+ reason: "write",
8594
+ meta: { model: model.name, method }
8595
+ }));
8596
+ delegate[method] = refusal;
8597
+ }
8598
+ for (const mount of mounts) {
8599
+ if (mount.scope !== "model" || mount.model !== model.name)
8600
+ continue;
8601
+ const classified = classifiedMount({ value: base[mount.property], readOnly: mount.readOnly });
8602
+ delegate[mount.property] = classified ?? (() => Promise.reject(readOnlyRefusal({
8603
+ path: `${model.clientName}.${mount.property}`,
8604
+ reason: "unclassified",
8605
+ meta: { model: model.name, method: mount.property, extension: mount.extension }
8606
+ })));
8607
+ }
8608
+ return Object.freeze(delegate);
8609
+ }
8610
+ function refuseBatch(params) {
8611
+ for (const operation of params.operations) {
8612
+ params.bridge.refuse({ value: operation, reason: params.reason });
8613
+ }
8614
+ return Promise.reject(params.reason);
8615
+ }
8616
+ function buildReadOnly(params) {
8617
+ const { client, context } = params;
8618
+ const { meta, mounts, operations } = context;
8619
+ const models = {};
8620
+ const facade = {};
8621
+ for (const model of meta.models) {
8622
+ const base = client.$models[model.name];
8623
+ if (base === undefined)
8624
+ continue;
8625
+ const delegate = readOnlyDelegate({ base, model, mounts, operations });
8626
+ models[model.name] = delegate;
8627
+ models[model.clientName] = delegate;
8628
+ }
8629
+ facade.$models = Object.freeze(models);
8630
+ for (const model of meta.models) {
8631
+ const delegate = models[model.clientName];
8632
+ if (delegate !== undefined) {
8633
+ Object.defineProperty(facade, model.clientName, { value: delegate, enumerable: true });
8634
+ }
8635
+ }
8636
+ facade.$on = client.$on.bind(client);
8637
+ facade.$transaction = (arg, options) => {
8638
+ if (typeof arg === "function") {
8639
+ const run = arg;
8640
+ return client.$transaction((tx) => run(readOnlyOver({ client: tx, context })), options);
8641
+ }
8642
+ if (Array.isArray(arg)) {
8643
+ for (const operation of arg) {
8644
+ const method = operations.methodOf({ value: operation });
8645
+ if (method === undefined)
8646
+ continue;
8647
+ if (delegateMethodAccess({ method }) === "read") {
8648
+ operations.restrictSql?.({ value: operation });
8649
+ continue;
8650
+ }
8651
+ return refuseBatch({
8652
+ operations: arg,
8653
+ reason: readOnlyRefusal({
8654
+ path: `$transaction([\u2026 ${method}() \u2026])`,
8655
+ reason: "foreign_operation",
8656
+ meta: { method }
8657
+ }),
8658
+ bridge: operations
8659
+ });
8660
+ }
8661
+ return client.$transaction(arg, options);
8662
+ }
8663
+ return client.$transaction(arg, options);
8664
+ };
8665
+ attachDefineView({ client: facade, meta, models });
8666
+ const derive = (member) => {
8667
+ const source = client[member];
8668
+ if (source === undefined)
8669
+ return;
8670
+ facade[member] = (arg) => {
8671
+ const derived = source.call(client, arg);
8672
+ return readOnlyOver({ client: derived, context });
8673
+ };
8674
+ };
8675
+ derive("$scoped");
8676
+ derive("$withPolicy");
8677
+ derive("$bypassPolicy");
8678
+ facade.$readOnly = () => facade;
8679
+ for (const [member, access] of Object.entries(CLIENT_MEMBER_ACCESS)) {
8680
+ if (access !== "raw" && access !== "control")
8681
+ continue;
8682
+ const reason = access === "raw" ? "raw" : "control";
8683
+ facade[member] = member === "$withContext" ? () => {
8684
+ throw readOnlyRefusal({ path: member, reason, meta: { method: member } });
8685
+ } : () => Promise.reject(readOnlyRefusal({ path: member, reason, meta: { method: member } }));
8686
+ }
8687
+ for (const mount of mounts) {
8688
+ if (mount.scope !== "client")
8689
+ continue;
8690
+ const classified = classifiedMount({ value: client[mount.property], readOnly: mount.readOnly });
8691
+ facade[mount.property] = classified ?? (() => Promise.reject(readOnlyRefusal({
8692
+ path: mount.property,
8693
+ reason: "unclassified",
8694
+ meta: { method: mount.property, extension: mount.extension }
8695
+ })));
8696
+ }
8697
+ return Object.freeze(facade);
8698
+ }
8699
+ function readOnlyOver(params) {
8700
+ const existing = facades.get(params.client);
8701
+ if (existing !== undefined)
8702
+ return existing;
8703
+ const built = buildReadOnly(params);
8704
+ facades.set(params.client, built);
8705
+ return built;
8706
+ }
8707
+ function attachReadOnly(params) {
8708
+ const context = {
8709
+ meta: params.meta,
8710
+ mounts: params.mounts ?? [],
8711
+ operations: params.operations
8712
+ };
8713
+ contexts.set(params.client, context);
8714
+ Object.defineProperty(params.client, "$readOnly", {
8715
+ value: () => readOnlyOver({ client: params.client, context }),
8716
+ enumerable: true,
8717
+ configurable: true
8718
+ });
8719
+ }
8720
+ function inheritReadOnly(params) {
8721
+ const context = contexts.get(params.from);
8722
+ if (context === undefined)
8723
+ return;
8724
+ contexts.set(params.to, context);
8725
+ Object.defineProperty(params.to, "$readOnly", {
8726
+ value: () => readOnlyOver({ client: params.to, context }),
8727
+ enumerable: true,
8728
+ configurable: true
8729
+ });
8730
+ }
8731
+
8732
+ // packages/runtime/src/policy.ts
8157
8733
  function policyError(message, meta) {
8158
- return new VibeError18({ code: "VIBE_VALIDATION", message, meta: meta ?? {} });
8734
+ return new VibeError21({ code: "VIBE_VALIDATION", message, meta: meta ?? {} });
8159
8735
  }
8160
8736
  function policiedModels(meta) {
8161
8737
  return meta.models.filter((model) => model.policy !== undefined);
@@ -8364,13 +8940,15 @@ function scopeSelection(params) {
8364
8940
  return out;
8365
8941
  }
8366
8942
  function scopeArgs(params) {
8367
- const { meta, model, args, context } = params;
8943
+ const { meta, model, context } = params;
8944
+ const args = restrictedSqlArgs({ value: params.args, path: "$withPolicy" });
8368
8945
  const scoped = scopeSelection({ meta, model, args, context });
8369
8946
  return scoped.where === undefined ? scoped : { ...scoped, where: scopeWhere({ meta, model, where: scoped.where, context }) };
8370
8947
  }
8371
8948
  var REFERENCE_VERBS = ["connect", "connectOrCreate", "set", "disconnect", "update", "updateMany", "delete", "deleteMany", "upsert"];
8372
8949
  function forceWriteData(params) {
8373
- const { meta, model, data, context, phase } = params;
8950
+ const { meta, model, context, phase } = params;
8951
+ const data = restrictedSqlArgs({ value: params.data, path: "$withPolicy" });
8374
8952
  let out = { ...data };
8375
8953
  if (model.policy !== undefined) {
8376
8954
  const value = predicateFor({ model, context })[model.policy.field];
@@ -8499,7 +9077,7 @@ function scopedDelegate(params) {
8499
9077
  const pkWhere = pk.length === 1 ? { [pk[0]]: target[pk[0]] } : { [pkClientKey()]: Object.fromEntries(pk.map((name) => [name, target[name]])) };
8500
9078
  return write(raw, pkWhere);
8501
9079
  };
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 } });
9080
+ const notFound2 = (method) => new VibeError21({ 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 } });
8503
9081
  return {
8504
9082
  findMany: (args) => base.findMany(scopeFind(args)),
8505
9083
  findFirst: (args) => base.findFirst(scopeFind(args)),
@@ -8533,7 +9111,7 @@ function scopedDelegate(params) {
8533
9111
  })
8534
9112
  })),
8535
9113
  upsertMany: () => {
8536
- throw new VibeError18({
9114
+ throw new VibeError21({
8537
9115
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8538
9116
  message: "upsertMany is not available on a policy-bound client: its conflict target is not policy-checked yet",
8539
9117
  meta: { model: model.name }
@@ -8542,7 +9120,7 @@ function scopedDelegate(params) {
8542
9120
  updateManyAndReturn: (args) => deferred(() => {
8543
9121
  const run = base.updateManyAndReturn;
8544
9122
  if (run === undefined) {
8545
- throw new VibeError18({
9123
+ throw new VibeError21({
8546
9124
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8547
9125
  message: "updateManyAndReturn is not available on this delegate",
8548
9126
  meta: { model: model.name }
@@ -8613,7 +9191,7 @@ function globalDelegate(params) {
8613
9191
  updateManyAndReturn: (args) => deferred(() => {
8614
9192
  const run = base.updateManyAndReturn;
8615
9193
  if (run === undefined) {
8616
- throw new VibeError18({
9194
+ throw new VibeError21({
8617
9195
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8618
9196
  message: "updateManyAndReturn is not available on this delegate"
8619
9197
  });
@@ -8624,7 +9202,7 @@ function globalDelegate(params) {
8624
9202
  upsertMany: (args) => deferred(() => {
8625
9203
  if (params.policyBound === false)
8626
9204
  return base.upsertMany(args);
8627
- throw new VibeError18({
9205
+ throw new VibeError21({
8628
9206
  code: "VIBE_UNSUPPORTED_CAPABILITY",
8629
9207
  message: "upsertMany is not available on a policy-bound client: its conflict target is not policy-checked yet"
8630
9208
  });
@@ -8722,6 +9300,7 @@ function attachPolicy(params) {
8722
9300
  }
8723
9301
  Object.defineProperty(scoped, "$models", { value: scopedModels, enumerable: true });
8724
9302
  attachDefineView({ client: scoped, meta, models: scopedModels });
9303
+ inheritReadOnly({ from: client, to: scoped });
8725
9304
  Object.defineProperty(scoped, "$transaction", {
8726
9305
  value: (arg, options) => typeof arg === "function" ? client.$transaction((tx) => arg(buildScopedOver(tx, context)), options) : client.$transaction(arg, options),
8727
9306
  enumerable: true
@@ -8753,6 +9332,7 @@ function attachPolicy(params) {
8753
9332
  }
8754
9333
  Object.defineProperty(inner, "$models", { value: scopedModels, enumerable: true });
8755
9334
  attachDefineView({ client: inner, meta, models: scopedModels });
9335
+ inheritReadOnly({ from: tx, to: inner });
8756
9336
  Object.defineProperty(inner, "$transaction", {
8757
9337
  value: (arg, options) => typeof arg === "function" ? tx.$transaction((nested) => arg(buildScopedOver(nested, context)), options) : tx.$transaction(arg, options),
8758
9338
  enumerable: true,
@@ -8773,13 +9353,14 @@ function attachPolicy(params) {
8773
9353
  }
8774
9354
  Object.defineProperty(bypass, "$models", { value: models, enumerable: true });
8775
9355
  attachDefineView({ client: bypass, meta, models });
9356
+ inheritReadOnly({ from: client, to: bypass });
8776
9357
  return bypass;
8777
9358
  },
8778
9359
  enumerable: true
8779
9360
  });
8780
9361
  }
8781
9362
  // packages/runtime/src/scoped.ts
8782
- import { ScopedQueryError, VibeError as VibeError19 } from "@vibeorm/schema";
9363
+ import { ScopedQueryError, VibeError as VibeError22 } from "@vibeorm/schema";
8783
9364
  function refusal(params) {
8784
9365
  return new ScopedQueryError(params);
8785
9366
  }
@@ -9511,7 +10092,7 @@ function rewriteScopedCall(params) {
9511
10092
  const entry = entryOf(ctx, model);
9512
10093
  const scoped = entry.kind !== "none";
9513
10094
  const root = `${model.clientName}.${method}`;
9514
- const args = { ...params.args ?? {} };
10095
+ const args = restrictedSqlArgs({ value: params.args ?? {}, path: "$scoped" });
9515
10096
  const ownPred = () => requirePred({ ctx, model, path: root });
9516
10097
  const walkOwnWhere = (where, path) => isPlainObject8(where) ? walkWhere({ ctx, model, where, path }) : where;
9517
10098
  if (FIND_LIKE.includes(method)) {
@@ -9689,7 +10270,7 @@ function forbidden(params) {
9689
10270
  message: `$scoped: ${params.method} is not available on a scoped handle \u2014 raw SQL cannot be tenant-verified; run it on the BASE client (the deliberate unscoped escape)`
9690
10271
  });
9691
10272
  }
9692
- var RAW_METHODS = ["$queryRaw", "$executeRaw", "$queryRawUnsafe", "$executeRawUnsafe"];
10273
+ var RAW_METHODS = ["$querySql", "$executeSql", "$queryRaw", "$executeRaw", "$queryRawUnsafe", "$executeRawUnsafe"];
9693
10274
  function buildScopedOver(params) {
9694
10275
  const { ctx, client } = params;
9695
10276
  const scoped = Object.create(client);
@@ -9705,6 +10286,7 @@ function buildScopedOver(params) {
9705
10286
  }
9706
10287
  Object.defineProperty(scoped, "$models", { value: scopedModels, enumerable: true });
9707
10288
  attachDefineView({ client: scoped, meta: ctx.meta, models: scopedModels });
10289
+ inheritReadOnly({ from: client, to: scoped });
9708
10290
  Object.defineProperty(scoped, "$transaction", {
9709
10291
  value: (arg, options) => typeof arg === "function" ? client.$transaction((tx) => arg(buildScopedOver({ ctx, client: tx })), options) : client.$transaction(arg, options),
9710
10292
  enumerable: true
@@ -9733,7 +10315,7 @@ function attachScoped(params) {
9733
10315
  Object.defineProperty(client, "$scoped", {
9734
10316
  value: (config) => {
9735
10317
  if (!isPlainObject8(config) || !isPlainObject8(config.models)) {
9736
- throw new VibeError19({
10318
+ throw new VibeError22({
9737
10319
  code: "VIBE_VALIDATION",
9738
10320
  message: "$scoped takes { scope: string | number | bigint | null, models: { <Model>: \u2026 } }"
9739
10321
  });
@@ -9749,7 +10331,7 @@ function attachScoped(params) {
9749
10331
  }
9750
10332
  const scope = config.scope;
9751
10333
  if (scope !== null && typeof scope !== "string" && typeof scope !== "number" && typeof scope !== "bigint") {
9752
- throw new VibeError19({
10334
+ throw new VibeError22({
9753
10335
  code: "VIBE_VALIDATION",
9754
10336
  message: `$scoped: scope must be a string, number, bigint or null \u2014 got ${typeof scope}`
9755
10337
  });
@@ -9765,7 +10347,7 @@ function attachScoped(params) {
9765
10347
  });
9766
10348
  }
9767
10349
  // packages/runtime/src/rls-readiness.ts
9768
- import { VibeError as VibeError20, assertValidSchemaIR, unsupportedCapability as unsupportedCapability6 } from "@vibeorm/schema";
10350
+ import { VibeError as VibeError23, assertValidSchemaIR, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
9769
10351
  import { NATIVE_RLS as NATIVE_RLS2, getSqlDialect as getSqlDialect4, renderRlsPolicy, renderRlsRole, NATIVE_RLS_PROBE_POLICY, renderRlsReadinessQuery, renderRlsProbeTable } from "@vibeorm/sql";
9770
10352
  import { RLS_READINESS_SQL, NATIVE_RLS_PROBE_POLICY as NATIVE_RLS_PROBE_POLICY2 } from "@vibeorm/sql";
9771
10353
  function textOf(params) {
@@ -9816,10 +10398,10 @@ function unprotectedTablesOf(params) {
9816
10398
  function requireNativeRls(params) {
9817
10399
  const dialect = getSqlDialect4({ dialect: params.adapter.dialect });
9818
10400
  if (!dialect.capabilities.nativeRowLevelSecurity) {
9819
- throw unsupportedCapability6({ dialect: dialect.name, feature: "nativeRowLevelSecurity" });
10401
+ throw unsupportedCapability7({ dialect: dialect.name, feature: "nativeRowLevelSecurity" });
9820
10402
  }
9821
10403
  if (params.schema.rls === undefined) {
9822
- throw new VibeError20({
10404
+ throw new VibeError23({
9823
10405
  code: "VIBE_CONFIG",
9824
10406
  message: "Native row-level security readiness requires a schema that declares native RLS"
9825
10407
  });
@@ -10231,7 +10813,7 @@ async function inspectNativeRls(params) {
10231
10813
  function requireEmbeddedRole(params) {
10232
10814
  const { role } = params;
10233
10815
  if (typeof role !== "string" || role.length === 0 || role.includes("\x00")) {
10234
- throw new VibeError20({
10816
+ throw new VibeError23({
10235
10817
  code: "VIBE_CONFIG",
10236
10818
  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",
10237
10819
  meta: { provider: "pglite" }
@@ -10243,7 +10825,7 @@ function assertNativeRlsReady(params) {
10243
10825
  const { report } = params;
10244
10826
  if (report.ready)
10245
10827
  return;
10246
- throw new VibeError20({
10828
+ throw new VibeError23({
10247
10829
  code: "VIBE_CONFIG",
10248
10830
  message: `Native row-level security is not ready on this database: ${report.problems.map((problem) => `${problem.name} (${problem.status})`).join(", ")}`,
10249
10831
  meta: {
@@ -10268,11 +10850,13 @@ function formatNativeRlsReport(params) {
10268
10850
  return lines;
10269
10851
  }
10270
10852
  // packages/runtime/src/client.ts
10271
- import { RlsContextError as RlsContextError2, VibeError as VibeError24, assertValidSchemaIR as assertValidSchemaIR2, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
10272
- import { ParamCollector, getSqlDialect as getSqlDialect5, renderRlsContext, renderRlsPolicy as renderRlsPolicy2, renderRlsRole as renderRlsRole2 } from "@vibeorm/sql";
10853
+ import { compileSql } from "@vibeorm/sql";
10854
+ import { RlsContextError as RlsContextError2, VibeError as VibeError27, assertValidSchemaIR as assertValidSchemaIR2, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
10855
+ import { ADVISORY_LOCK_RESULT_COLUMN, ParamCollector, getSqlDialect as getSqlDialect5, renderRlsContext, renderRlsPolicy as renderRlsPolicy2, renderRlsRole as renderRlsRole2, renderStatement as renderStatement5 } from "@vibeorm/sql";
10273
10856
 
10274
10857
  // packages/runtime/src/render-cache.ts
10275
10858
  import {
10859
+ isSqlFragment as isSqlFragment3,
10276
10860
  isDbNowMarker,
10277
10861
  isJsonParamCell,
10278
10862
  isListSetOperation,
@@ -10490,6 +11074,7 @@ function walkExpr(walk2, dialect, expr) {
10490
11074
  return true;
10491
11075
  case "jsonFilter":
10492
11076
  return false;
11077
+ case "composed":
10493
11078
  case "fragment":
10494
11079
  return false;
10495
11080
  case "raw":
@@ -10614,8 +11199,9 @@ function walkSelect(walk2, dialect, statement) {
10614
11199
  walk2.key += `t${statement.limit}`;
10615
11200
  if (statement.offset !== undefined)
10616
11201
  walk2.key += `s${statement.offset}`;
10617
- if (statement.lockForUpdate === true)
10618
- walk2.key += "u";
11202
+ if (statement.rowLock !== undefined) {
11203
+ walk2.key += `u:${statement.rowLock.strength}/${statement.rowLock.wait}`;
11204
+ }
10619
11205
  walk2.key += "}";
10620
11206
  return true;
10621
11207
  }
@@ -10664,6 +11250,8 @@ function walkInsert(walk2, dialect, statement) {
10664
11250
  }
10665
11251
  walk2.key += ")";
10666
11252
  } else {
11253
+ if (Object.values(onConflict.update).some(isSqlFragment3))
11254
+ return false;
10667
11255
  walk2.key += "U(";
10668
11256
  if (onConflict.constraint !== undefined) {
10669
11257
  walk2.key += "K";
@@ -10729,6 +11317,7 @@ function walkStatement(walk2, dialect, statement) {
10729
11317
  case "rlsPolicy":
10730
11318
  case "rlsContext":
10731
11319
  case "rlsRole":
11320
+ case "advisoryLock":
10732
11321
  return false;
10733
11322
  }
10734
11323
  }
@@ -10773,11 +11362,11 @@ function renderStatementCached(params) {
10773
11362
  }
10774
11363
 
10775
11364
  // packages/runtime/src/strict-args.ts
10776
- import { VibeError as VibeError21 } from "@vibeorm/schema";
11365
+ import { VibeError as VibeError24 } from "@vibeorm/schema";
10777
11366
  var SKIP = Symbol.for("vibeorm.skip");
10778
11367
  function explicitUndefined(params) {
10779
11368
  const { model, method, path } = params;
10780
- return new VibeError21({
11369
+ return new VibeError24({
10781
11370
  code: "VIBE_VALIDATION",
10782
11371
  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.`,
10783
11372
  meta: { model, method, path }
@@ -10954,10 +11543,10 @@ function bindRlsContext(params) {
10954
11543
  }
10955
11544
 
10956
11545
  // packages/runtime/src/telemetry/recorder.ts
10957
- import { VibeError as VibeError23 } from "@vibeorm/schema";
11546
+ import { VibeError as VibeError26 } from "@vibeorm/schema";
10958
11547
 
10959
11548
  // packages/runtime/src/telemetry/config.ts
10960
- import { VibeError as VibeError22 } from "@vibeorm/schema";
11549
+ import { VibeError as VibeError25 } from "@vibeorm/schema";
10961
11550
  var TELEMETRY_DEFAULTS = Object.freeze({
10962
11551
  captureSql: false,
10963
11552
  captureParameterValues: false,
@@ -10972,7 +11561,7 @@ var TELEMETRY_DEFAULTS = Object.freeze({
10972
11561
  latencyBucketsMs: Object.freeze([1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000])
10973
11562
  });
10974
11563
  function configError(params) {
10975
- return new VibeError22({
11564
+ return new VibeError25({
10976
11565
  code: "VIBE_CONFIG",
10977
11566
  message: `telemetry: ${params.message}`,
10978
11567
  ...params.meta === undefined ? {} : { meta: params.meta }
@@ -11060,7 +11649,7 @@ function deriveStage(params) {
11060
11649
  return "decode";
11061
11650
  }
11062
11651
  function errorCodeOf(error) {
11063
- return error instanceof VibeError23 ? error.code : undefined;
11652
+ return error instanceof VibeError26 ? error.code : undefined;
11064
11653
  }
11065
11654
  function newHistogram(bounds) {
11066
11655
  return { counts: new Array(bounds.length + 1).fill(0), sum: 0, count: 0 };
@@ -11434,15 +12023,23 @@ async function instrumentStatement(params) {
11434
12023
  // packages/runtime/src/client.ts
11435
12024
  function notFound2(params) {
11436
12025
  const { model, method } = params;
11437
- return new VibeError24({
12026
+ return new VibeError27({
11438
12027
  code: "VIBE_NOT_FOUND",
11439
12028
  message: `no ${model} record matched the where clause of ${method}()`,
11440
12029
  meta: { model, method }
11441
12030
  });
11442
12031
  }
12032
+ function transactionLifecycleRefusal(params) {
12033
+ const { method } = params;
12034
+ return new VibeError27({
12035
+ code: "VIBE_VALIDATION",
12036
+ message: `${method}() is not available on a transaction client \u2014 the connection is owned by the open transaction; call it on the client that started the transaction`,
12037
+ meta: { method, inTransaction: true }
12038
+ });
12039
+ }
11443
12040
  function viewIsReadOnly(params) {
11444
12041
  const { model, method } = params;
11445
- return new VibeError24({
12042
+ return new VibeError27({
11446
12043
  code: "VIBE_VALIDATION",
11447
12044
  message: `${model} is a view \u2014 ${method}() is not available on views (they are read-only); write to the underlying table instead`,
11448
12045
  meta: { model, method }
@@ -11548,7 +12145,7 @@ async function engineTransaction(params) {
11548
12145
  const support = engine.adapter.budgets?.transactionDeadline;
11549
12146
  const requested = options.deadline.enforcement ?? "between-statements";
11550
12147
  if (support === undefined || support === "unsupported" || requested === "cancel-running-statements" && support !== "cancel-running-statements") {
11551
- throw new VibeError24({
12148
+ throw new VibeError27({
11552
12149
  code: "VIBE_UNSUPPORTED_CAPABILITY",
11553
12150
  message: "the adapter does not declare support for the requested transaction deadline",
11554
12151
  meta: { provider: engine.adapter.provider, feature: "transactionDeadline", requested, supported: support ?? "undeclared" }
@@ -11647,105 +12244,141 @@ function settleWith(params) {
11647
12244
  scope.settle({ outcome: "failure", error });
11648
12245
  });
11649
12246
  }
12247
+ var createQueryPromise;
12248
+ var queryPromiseMethod;
12249
+ var queryPromiseOwnerError;
12250
+ var refuseQueryPromise;
12251
+ var claimQueryPromise;
12252
+ var restrictQueryPromise;
12253
+ var queryPromises = new WeakSet;
12254
+ function isQueryPromise(value) {
12255
+ return typeof value === "object" && value !== null && queryPromises.has(value);
12256
+ }
11650
12257
 
11651
12258
  class QueryPromise extends Promise {
11652
12259
  static get [Symbol.species]() {
11653
12260
  return Promise;
11654
12261
  }
11655
- target;
11656
- dispatch;
11657
- settle;
11658
- started = false;
11659
- scope;
11660
- static create(params) {
12262
+ #target;
12263
+ #operationMethod;
12264
+ #dispatch;
12265
+ #settle;
12266
+ #started = false;
12267
+ #scope;
12268
+ static {
12269
+ createQueryPromise = QueryPromise.#create;
12270
+ queryPromiseMethod = ({ value }) => value.#operationMethod;
12271
+ queryPromiseOwnerError = ({ value, engine }) => value.#ownerError({ engine });
12272
+ refuseQueryPromise = ({ value, reason }) => value.#refuse({ reason });
12273
+ claimQueryPromise = ({ value }) => value.#claim();
12274
+ restrictQueryPromise = ({ value }) => {
12275
+ value.#target = { ...value.#target, readOnlySql: true };
12276
+ };
12277
+ }
12278
+ static #create(params) {
11661
12279
  let resolve;
11662
12280
  let reject;
11663
12281
  const instance = new QueryPromise((res, rej) => {
11664
12282
  resolve = res;
11665
12283
  reject = rej;
11666
12284
  });
11667
- instance.target = params.engine;
11668
- instance.dispatch = params.run;
11669
- instance.settle = { resolve, reject };
11670
- instance.scope = beginOperation({
12285
+ queryPromises.add(instance);
12286
+ instance.#target = params.engine;
12287
+ instance.#operationMethod = params.method ?? "unknown";
12288
+ instance.#dispatch = params.run;
12289
+ instance.#settle = { resolve, reject };
12290
+ instance.#scope = beginOperation({
11671
12291
  engine: params.engine,
11672
12292
  kind: params.kind ?? "model",
11673
12293
  model: params.model ?? null,
11674
12294
  method: params.method ?? "unknown"
11675
12295
  });
11676
12296
  queueMicrotask(() => {
11677
- instance.start();
12297
+ instance.#start();
11678
12298
  });
11679
12299
  return instance;
11680
12300
  }
11681
- start() {
11682
- if (this.started)
12301
+ #start() {
12302
+ if (this.#started)
11683
12303
  return;
11684
- this.started = true;
11685
- const scope = this.scope;
12304
+ this.#started = true;
12305
+ const scope = this.#scope;
11686
12306
  scope?.dispatch();
11687
- const target = scope === undefined ? this.target : { ...this.target, operation: scope };
11688
- const running = target.rlsSchema === undefined && target.lifetime === undefined ? this.dispatch(target) : executeOperation({ engine: target, run: this.dispatch });
12307
+ const target = scope === undefined ? this.#target : { ...this.#target, operation: scope };
12308
+ const running = target.rlsSchema === undefined && target.lifetime === undefined ? this.#dispatch(target) : executeOperation({ engine: target, run: this.#dispatch });
11689
12309
  if (scope !== undefined)
11690
12310
  settleWith({ scope, running });
11691
- running.then(this.settle.resolve, this.settle.reject);
12311
+ running.then(this.#settle.resolve, this.#settle.reject);
11692
12312
  }
11693
12313
  then(onfulfilled, onrejected) {
11694
- this.start();
12314
+ this.#start();
11695
12315
  return super.then(onfulfilled, onrejected);
11696
12316
  }
11697
- ownerError(params) {
12317
+ #ownerError(params) {
11698
12318
  const { engine } = params;
11699
- const state = engineStateError({ engine: this.target });
12319
+ const state = engineStateError({ engine: this.#target });
11700
12320
  if (state !== undefined)
11701
12321
  return state;
11702
- if (engine.rlsSchema === undefined && this.target.rlsSchema === undefined)
12322
+ if (engine.rlsSchema === undefined && this.#target.rlsSchema === undefined)
11703
12323
  return;
11704
- if (this.target.rootIdentity !== engine.rootIdentity || this.target.lifetime !== engine.lifetime || !sameRecord(this.target.context?.settings ?? {}, engine.context?.settings ?? {}))
12324
+ if (this.#target.rootIdentity !== engine.rootIdentity || this.#target.lifetime !== engine.lifetime || !sameRecord(this.#target.context?.settings ?? {}, engine.context?.settings ?? {}))
11705
12325
  return new RlsContextError2({ reason: "conflicting" });
11706
12326
  return;
11707
12327
  }
11708
- refuse(params) {
11709
- if (this.started)
12328
+ #refuse(params) {
12329
+ if (this.#started)
11710
12330
  return;
11711
- this.started = true;
11712
- this.scope?.settle({ outcome: "failure", stage: "refused", error: params.reason });
11713
- this.settle.reject(params.reason);
12331
+ this.#started = true;
12332
+ this.#scope?.settle({ outcome: "failure", stage: "refused", error: params.reason });
12333
+ this.#settle.reject(params.reason);
11714
12334
  super.then(undefined, () => {});
11715
12335
  }
11716
- claim() {
11717
- if (this.started) {
11718
- throw new VibeError24({
12336
+ #claim() {
12337
+ if (this.#started) {
12338
+ throw new VibeError27({
11719
12339
  code: "VIBE_VALIDATION",
11720
12340
  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)"
11721
12341
  });
11722
12342
  }
11723
- this.started = true;
11724
- const scope = this.scope;
12343
+ this.#started = true;
12344
+ const scope = this.#scope;
11725
12345
  return {
11726
12346
  start: (target) => {
12347
+ if (this.#target.readOnlySql === true)
12348
+ target = { ...target, readOnlySql: true };
11727
12349
  if (scope !== undefined && target.operation !== undefined) {
11728
12350
  scope.reparent({ parentOperationId: target.operation.id });
11729
12351
  }
11730
12352
  scope?.dispatch();
11731
12353
  const running = executeOperation({
11732
12354
  engine: scope === undefined ? target : { ...target, operation: scope },
11733
- run: this.dispatch
12355
+ run: this.#dispatch
11734
12356
  });
11735
12357
  if (scope !== undefined)
11736
12358
  settleWith({ scope, running });
11737
- running.then(this.settle.resolve, this.settle.reject);
12359
+ running.then(this.#settle.resolve, this.#settle.reject);
11738
12360
  super.then(undefined, () => {});
11739
12361
  return running;
11740
12362
  },
11741
12363
  abort: (reason) => {
11742
12364
  scope?.settle({ outcome: "failure", stage: "refused", error: reason });
11743
- this.settle.reject(reason);
12365
+ this.#settle.reject(reason);
11744
12366
  super.then(undefined, () => {});
11745
12367
  }
11746
12368
  };
11747
12369
  }
11748
12370
  }
12371
+ var LAZY_OPERATIONS = {
12372
+ restrictSql: ({ value }) => {
12373
+ if (isQueryPromise(value))
12374
+ restrictQueryPromise({ value });
12375
+ },
12376
+ methodOf: (params) => isQueryPromise(params.value) ? queryPromiseMethod({ value: params.value }) : undefined,
12377
+ refuse: (params) => {
12378
+ if (isQueryPromise(params.value))
12379
+ refuseQueryPromise({ value: params.value, reason: params.reason });
12380
+ }
12381
+ };
11749
12382
  async function runRows(params) {
11750
12383
  const { engine, plan } = params;
11751
12384
  const renderStarted = renderClock({ engine });
@@ -11772,9 +12405,9 @@ async function runRows(params) {
11772
12405
  }
11773
12406
  async function runBoundAffected(params) {
11774
12407
  const { adapter, text: text2, values } = params;
11775
- const bound = adapter.executeAffected;
11776
- if (bound !== undefined)
11777
- return await bound.call(adapter, { text: text2, values });
12408
+ const bound2 = adapter.executeAffected;
12409
+ if (bound2 !== undefined)
12410
+ return await bound2.call(adapter, { text: text2, values });
11778
12411
  return await adapter.executeUnsafe({ text: text2, values });
11779
12412
  }
11780
12413
  async function runAffected(params) {
@@ -11813,14 +12446,26 @@ async function runAffected(params) {
11813
12446
  });
11814
12447
  return result.affectedRows;
11815
12448
  }
12449
+ var LOCKABLE_METHODS = Object.freeze(["findMany", "findFirst", "findUnique"]);
11816
12450
  function plan(params) {
11817
12451
  const { engine, model, method, args } = params;
11818
- if (args !== undefined && args["lock"] !== undefined && engine.inTransaction !== true) {
11819
- throw new VibeError24({
11820
- code: "VIBE_VALIDATION",
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)`,
11822
- meta: { model, method }
11823
- });
12452
+ if (engine.readOnlySql === true && containsSqlFragment(args))
12453
+ throw readOnlyRefusal({ path: `${model}.${method}`, reason: "raw" });
12454
+ if (args !== undefined && args["lock"] !== undefined) {
12455
+ if (engine.inTransaction !== true) {
12456
+ throw new VibeError27({
12457
+ code: "VIBE_VALIDATION",
12458
+ 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)`,
12459
+ meta: { model, method }
12460
+ });
12461
+ }
12462
+ if (!LOCKABLE_METHODS.includes(method)) {
12463
+ throw new VibeError27({
12464
+ code: "VIBE_VALIDATION",
12465
+ message: `lock on ${model}.${method}() is not supported \u2014 only ${LOCKABLE_METHODS.join(", ")} take a lock. ` + `Lock the rows with a find inside the transaction, then write them`,
12466
+ meta: { model, method }
12467
+ });
12468
+ }
11824
12469
  }
11825
12470
  return buildQuery({
11826
12471
  meta: engine.meta,
@@ -11835,6 +12480,19 @@ function plan(params) {
11835
12480
  }
11836
12481
  });
11837
12482
  }
12483
+ var OVERLAY_ROW_LOCK = Object.freeze({ strength: "update", wait: "wait" });
12484
+ function readAcquiredVerdict(params) {
12485
+ const value = params.result.rows[0]?.[ADVISORY_LOCK_RESULT_COLUMN];
12486
+ if (value === true || value === 1 || value === "t" || value === "true" || value === "1")
12487
+ return true;
12488
+ if (value === false || value === 0 || value === "f" || value === "false" || value === "0")
12489
+ return false;
12490
+ throw new VibeError27({
12491
+ code: "VIBE_INTERNAL",
12492
+ message: `internal: ${params.method}() expected a boolean "${ADVISORY_LOCK_RESULT_COLUMN}" column, got ${String(value)}`,
12493
+ meta: { method: params.method, value: String(value) }
12494
+ });
12495
+ }
11838
12496
  async function refetch(params) {
11839
12497
  const { engine, source, where } = params;
11840
12498
  const selectArg = source.selectArg;
@@ -11844,7 +12502,7 @@ async function refetch(params) {
11844
12502
  method: "findUnique",
11845
12503
  args: selectArg === null ? { where } : { where, select: selectArg }
11846
12504
  });
11847
- const locked = params.lock === true ? { ...followUp, statement: { ...followUp.statement, lockForUpdate: true } } : followUp;
12505
+ const locked = params.lock === true ? { ...followUp, statement: { ...followUp.statement, rowLock: OVERLAY_ROW_LOCK } } : followUp;
11848
12506
  const rows = await runRows({ engine, plan: locked });
11849
12507
  return rows[0] ?? null;
11850
12508
  }
@@ -11871,7 +12529,7 @@ async function insertedKeyWhere(params) {
11871
12529
  const keyField = source.model.primaryKey[0];
11872
12530
  const lastInsertIdSql = engine.dialect.lastInsertIdSql;
11873
12531
  if (lastInsertIdSql === null || source.model.primaryKey.length !== 1 || keyField === undefined) {
11874
- throw unsupportedCapability7({
12532
+ throw unsupportedCapability8({
11875
12533
  dialect: engine.dialect.name,
11876
12534
  feature: `${source.method} on ${source.model.name} without RETURNING`,
11877
12535
  hint: "this model's key is assigned by the database and cannot be read back on this dialect"
@@ -11889,7 +12547,7 @@ async function insertedKeyWhere(params) {
11889
12547
  });
11890
12548
  const id = result.rows[0]?.id;
11891
12549
  if (id === undefined || id === null) {
11892
- throw new VibeError24({
12550
+ throw new VibeError27({
11893
12551
  code: "VIBE_INTERNAL",
11894
12552
  message: `could not read back the generated key for ${source.model.name}`,
11895
12553
  meta: { model: source.model.name }
@@ -12019,7 +12677,7 @@ async function runUpsertFallback(params) {
12019
12677
  const probeSource = plan({ engine, model: model.name, method: "findUnique", args: { where: args.where } });
12020
12678
  const probePlan = {
12021
12679
  ...probeSource,
12022
- statement: { ...probeSource.statement, lockForUpdate: true }
12680
+ statement: { ...probeSource.statement, rowLock: OVERLAY_ROW_LOCK }
12023
12681
  };
12024
12682
  const createSource = plan({
12025
12683
  engine,
@@ -12047,6 +12705,8 @@ async function runUpsertFallback(params) {
12047
12705
  function relationExecutor(params) {
12048
12706
  const { engine } = params;
12049
12707
  return async ({ statement, model, method }) => {
12708
+ if (engine.readOnlySql === true && containsSqlFragment(statement))
12709
+ throw readOnlyRefusal({ path: `${model}.${method}`, reason: "raw" });
12050
12710
  const rendered = renderStatementCached({ dialect: engine.dialect, statement, cache: engine.renderCache });
12051
12711
  return await runStatement({
12052
12712
  engine,
@@ -12146,7 +12806,7 @@ function makeWriteContext(params) {
12146
12806
  transactional: (fn) => engine.adapter.transaction((txAdapter) => fn(makeRunner({ engine: { ...engine, adapter: txAdapter, inTransaction: true } })))
12147
12807
  };
12148
12808
  }
12149
- function readOnlyDelegate(params) {
12809
+ function readOnlyDelegate2(params) {
12150
12810
  const { delegate, model } = params;
12151
12811
  const refuse2 = (method) => async () => {
12152
12812
  throw viewIsReadOnly({ model, method });
@@ -12206,7 +12866,7 @@ function createDelegate(params) {
12206
12866
  const skipping = args.onConflict !== undefined;
12207
12867
  if (hasNestedWrites({ model: modelMeta, data: args.data })) {
12208
12868
  if (skipping) {
12209
- throw new VibeError24({
12869
+ throw new VibeError27({
12210
12870
  code: "VIBE_VALIDATION",
12211
12871
  message: `onConflict cannot combine with nested writes on ${modelMeta.name}.create() \u2014 insert the parent row alone, then connect relations`,
12212
12872
  meta: { model: modelMeta.name }
@@ -12307,7 +12967,7 @@ function createDelegate(params) {
12307
12967
  const upsert = async (target, args) => {
12308
12968
  const modelMeta = getModelMeta({ meta: target.meta, model });
12309
12969
  if (hasNestedWrites({ model: modelMeta, data: args.create }) || hasNestedWrites({ model: modelMeta, data: args.update })) {
12310
- throw new VibeError24({
12970
+ throw new VibeError27({
12311
12971
  code: "VIBE_VALIDATION",
12312
12972
  message: `nested writes are not supported inside upsert arms on ${modelMeta.name} \u2014 use create()/update() with nested data`,
12313
12973
  meta: { model: modelMeta.name }
@@ -12315,7 +12975,7 @@ function createDelegate(params) {
12315
12975
  }
12316
12976
  if (!upsertUsesNativeStatement({ model: modelMeta, args })) {
12317
12977
  if (args.onConflict !== undefined) {
12318
- throw new VibeError24({
12978
+ throw new VibeError27({
12319
12979
  code: "VIBE_VALIDATION",
12320
12980
  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`,
12321
12981
  meta: { model: modelMeta.name }
@@ -12410,7 +13070,7 @@ function createDelegate(params) {
12410
13070
  return assembleAggregates({ engine: target, model: built.model, spec, row, into: out });
12411
13071
  });
12412
13072
  };
12413
- const lazy = (method, run) => QueryPromise.create({ engine, run, model, method });
13073
+ const lazy = (method, run) => createQueryPromise({ engine, run, model, method });
12414
13074
  const delegate = {
12415
13075
  findMany: (args) => lazy("findMany", (target) => findMany(target, args)),
12416
13076
  findFirst: (args) => lazy("findFirst", (target) => findFirst(target, args)),
@@ -12442,7 +13102,7 @@ function createDelegate(params) {
12442
13102
  groupBy: (args) => lazy("groupBy", (target) => groupBy(target, args))
12443
13103
  };
12444
13104
  const meta = getModelMeta({ meta: engine.meta, model });
12445
- return meta.isView ? readOnlyDelegate({ delegate, model: meta.name }) : delegate;
13105
+ return meta.isView ? readOnlyDelegate2({ delegate, model: meta.name }) : delegate;
12446
13106
  }
12447
13107
  var WRITE_METHODS = [
12448
13108
  "create",
@@ -12508,13 +13168,13 @@ function buildClient(params) {
12508
13168
  models[model.name] = delegate;
12509
13169
  models[model.clientName] = delegate;
12510
13170
  }
12511
- const runRaw = async (method, statement) => {
13171
+ const runRaw = async (method, statement, decodeRows2) => {
12512
13172
  const scope = beginOperation({ engine, kind: "raw", model: null, method });
12513
13173
  scope?.dispatch();
12514
13174
  const running = executeOperation({
12515
13175
  engine: scope === undefined ? engine : { ...engine, operation: scope },
12516
13176
  run: async (target) => {
12517
- return await runStatement({
13177
+ const result = await runStatement({
12518
13178
  engine: target,
12519
13179
  kind: "raw",
12520
13180
  model: null,
@@ -12524,6 +13184,8 @@ function buildClient(params) {
12524
13184
  rowCount: (value) => method.startsWith("$query") ? value.rows.length : value.affectedRows,
12525
13185
  run: () => statement.values.length > 0 ? runBoundAffected({ adapter: target.adapter, text: statement.text, values: statement.values }) : target.adapter.executeUnsafe(statement)
12526
13186
  });
13187
+ decodeRows2?.(result.rows);
13188
+ return result;
12527
13189
  }
12528
13190
  });
12529
13191
  if (scope === undefined)
@@ -12538,33 +13200,67 @@ function buildClient(params) {
12538
13200
  });
12539
13201
  return await running;
12540
13202
  };
13203
+ const runAdvisoryLock = async (params2) => {
13204
+ const { method, options } = params2;
13205
+ assertEngineActive({ engine });
13206
+ const statement = compileAdvisoryLock({
13207
+ dialect: engine.dialect,
13208
+ method,
13209
+ inTransaction: engine.inTransaction === true,
13210
+ options
13211
+ });
13212
+ const rendered = renderStatement5({ dialect: engine.dialect, statement });
13213
+ const scope = beginOperation({ engine, kind: "raw", model: null, method });
13214
+ scope?.dispatch();
13215
+ const target = scope === undefined ? engine : { ...engine, operation: scope };
13216
+ const running = runStatement({
13217
+ engine: target,
13218
+ kind: "raw",
13219
+ model: null,
13220
+ method,
13221
+ text: rendered.text,
13222
+ values: rendered.values,
13223
+ rowCount: (value) => value.rows.length,
13224
+ run: () => runBoundAffected({
13225
+ adapter: target.adapter,
13226
+ text: rendered.text,
13227
+ values: bindValues({ adapter: target.adapter, values: rendered.values })
13228
+ })
13229
+ });
13230
+ if (scope !== undefined)
13231
+ settleWith({ scope, running });
13232
+ const result = await running;
13233
+ if (statement.wait === "wait")
13234
+ return true;
13235
+ return readAcquiredVerdict({ method, result });
13236
+ };
12541
13237
  const runOperations = (operations, options) => {
12542
13238
  const scope = beginOperation({ engine, kind: "transaction", model: null, method: "$transaction" });
12543
13239
  const claimed = [];
12544
13240
  let claimError = null;
12545
13241
  let identityError = engineStateError({ engine });
12546
13242
  for (const operation of operations) {
12547
- if (identityError === undefined && operation instanceof QueryPromise)
12548
- identityError = operation.ownerError({ engine });
13243
+ if (identityError === undefined && isQueryPromise(operation))
13244
+ identityError = queryPromiseOwnerError({ value: operation, engine });
12549
13245
  }
12550
13246
  if (identityError !== undefined) {
12551
13247
  for (const operation of operations) {
12552
- if (operation instanceof QueryPromise)
12553
- operation.refuse({ reason: identityError });
13248
+ if (isQueryPromise(operation))
13249
+ refuseQueryPromise({ value: operation, reason: identityError });
12554
13250
  }
12555
13251
  scope?.settle({ outcome: "failure", stage: "refused", error: identityError });
12556
13252
  return Promise.reject(identityError);
12557
13253
  }
12558
13254
  for (const operation of operations) {
12559
- if (!(operation instanceof QueryPromise)) {
12560
- claimError = new VibeError24({
13255
+ if (!isQueryPromise(operation)) {
13256
+ claimError = new VibeError27({
12561
13257
  code: "VIBE_VALIDATION",
12562
13258
  message: "$transaction([...]) takes query promises from model delegate calls \u2014 a foreign promise is already running and cannot join the transaction"
12563
13259
  });
12564
13260
  break;
12565
13261
  }
12566
13262
  try {
12567
- claimed.push(operation.claim());
13263
+ claimed.push(claimQueryPromise({ value: operation }));
12568
13264
  } catch (error) {
12569
13265
  claimError = error;
12570
13266
  break;
@@ -12574,8 +13270,8 @@ function buildClient(params) {
12574
13270
  for (const entry of claimed)
12575
13271
  entry.abort(claimError);
12576
13272
  for (const operation of operations) {
12577
- if (operation instanceof QueryPromise)
12578
- operation.refuse({ reason: claimError });
13273
+ if (isQueryPromise(operation))
13274
+ refuseQueryPromise({ value: operation, reason: claimError });
12579
13275
  }
12580
13276
  scope?.settle({ outcome: "failure", stage: "refused", error: claimError });
12581
13277
  return Promise.reject(claimError);
@@ -12622,7 +13318,7 @@ function buildClient(params) {
12622
13318
  $models: models,
12623
13319
  $on: (event, listener) => {
12624
13320
  if (event !== "query") {
12625
- throw new VibeError24({
13321
+ throw new VibeError27({
12626
13322
  code: "VIBE_VALIDATION",
12627
13323
  message: `unknown client event "${String(event)}" \u2014 supported: "query"`,
12628
13324
  meta: { event }
@@ -12634,15 +13330,34 @@ function buildClient(params) {
12634
13330
  };
12635
13331
  },
12636
13332
  $transaction: (fnOrOperations, options) => Array.isArray(fnOrOperations) ? runOperations(fnOrOperations, options) : runCallbackTransaction(fnOrOperations, options),
13333
+ $querySql: async ({ query, decoder }) => {
13334
+ assertEngineActive({ engine });
13335
+ if (decoder === null || typeof decoder !== "object" || typeof decoder.decode !== "function")
13336
+ throw new VibeError27({ code: "VIBE_VALIDATION", message: "$querySql requires an explicit result decoder" });
13337
+ const statement = compileSql({ dialect: engine.dialect, query });
13338
+ let output = [];
13339
+ await runRaw("$querySql", statement, (rows) => {
13340
+ output = rows.map((value) => decoder.decode({ value, dialect: engine.dialect.name, ...engine.adapter.wire === undefined ? {} : { wire: engine.adapter.wire } }));
13341
+ });
13342
+ return output;
13343
+ },
13344
+ $executeSql: async ({ query }) => {
13345
+ assertEngineActive({ engine });
13346
+ return (await runRaw("$executeSql", compileSql({ dialect: engine.dialect, query }))).affectedRows;
13347
+ },
12637
13348
  $queryRaw: async (strings, ...values) => (await runRaw("$queryRaw", buildRawStatement({ dialect: engine.dialect, strings, values }))).rows,
12638
13349
  $executeRaw: async (strings, ...values) => (await runRaw("$executeRaw", buildRawStatement({ dialect: engine.dialect, strings, values }))).affectedRows,
12639
13350
  $queryRawUnsafe: async (text2, ...values) => (await runRaw("$queryRawUnsafe", { text: text2, values })).rows,
12640
13351
  $executeRawUnsafe: async (text2, ...values) => (await runRaw("$executeRawUnsafe", { text: text2, values })).affectedRows,
13352
+ $advisoryLock: async (options) => {
13353
+ await runAdvisoryLock({ method: "$advisoryLock", options });
13354
+ },
13355
+ $tryAdvisoryLock: (options) => runAdvisoryLock({ method: "$tryAdvisoryLock", options }),
12641
13356
  $connect: async () => {
12642
13357
  assertEngineActive({ engine, requireContext: false });
13358
+ if (engine.inTransaction === true)
13359
+ throw transactionLifecycleRefusal({ method: "$connect" });
12643
13360
  if (engine.rlsSchema !== undefined) {
12644
- if (engine.inTransaction)
12645
- throw new RlsContextError2({ reason: "conflicting" });
12646
13361
  await engine.adapter.connect();
12647
13362
  await engineTransaction({ engine, requireContext: false, run: async (target) => {
12648
13363
  for (const hook of engine.connectHooks ?? []) {
@@ -12660,8 +13375,8 @@ function buildClient(params) {
12660
13375
  },
12661
13376
  $disconnect: async () => {
12662
13377
  assertEngineActive({ engine, requireContext: false });
12663
- if (engine.rlsSchema !== undefined && engine.inTransaction)
12664
- throw new RlsContextError2({ reason: "conflicting" });
13378
+ if (engine.inTransaction === true)
13379
+ throw transactionLifecycleRefusal({ method: "$disconnect" });
12665
13380
  await engine.adapter.disconnect();
12666
13381
  await engine.telemetry?.flush();
12667
13382
  }
@@ -12678,7 +13393,7 @@ function buildClient(params) {
12678
13393
  if (engine.rlsSchema === undefined)
12679
13394
  attachScoped({ client, meta: engine.meta, ...wrapDelegate === undefined ? {} : { wrapDelegate } });
12680
13395
  if (engine.rlsSchema === undefined) {
12681
- registerPolicyOperations({ client, factory: (run) => QueryPromise.create({
13396
+ registerPolicyOperations({ client, factory: (run) => createQueryPromise({
12682
13397
  engine,
12683
13398
  model: null,
12684
13399
  method: "$policyOperation",
@@ -12686,9 +13401,15 @@ function buildClient(params) {
12686
13401
  run: (target) => engineTransaction({ engine: target, run: (transactionEngine) => run(buildClient({ engine: transactionEngine })) })
12687
13402
  }) });
12688
13403
  attachPolicy({ client, meta: engine.meta, ...wrapDelegate === undefined ? {} : { wrapDelegate } });
13404
+ attachReadOnly({
13405
+ client,
13406
+ meta: engine.meta,
13407
+ ...engine.extensionMounts === undefined ? {} : { mounts: engine.extensionMounts },
13408
+ operations: LAZY_OPERATIONS
13409
+ });
12689
13410
  } else {
12690
13411
  const refuseLegacy = () => {
12691
- throw new VibeError24({ code: "VIBE_CONFIG", message: "Native row-level security cannot be combined with application policy or tenant scoping" });
13412
+ throw new VibeError27({ code: "VIBE_CONFIG", message: "Native row-level security cannot be combined with application policy or tenant scoping" });
12692
13413
  };
12693
13414
  Object.defineProperties(client, {
12694
13415
  $withContext: { enumerable: true, value: (context) => {
@@ -12701,6 +13422,14 @@ function buildClient(params) {
12701
13422
  $withPolicy: { value: refuseLegacy },
12702
13423
  $bypassPolicy: { value: refuseLegacy }
12703
13424
  });
13425
+ if (engine.context !== undefined) {
13426
+ attachReadOnly({
13427
+ client,
13428
+ meta: engine.meta,
13429
+ ...engine.extensionMounts === undefined ? {} : { mounts: engine.extensionMounts },
13430
+ operations: LAZY_OPERATIONS
13431
+ });
13432
+ }
12704
13433
  for (const delegate of Object.values(models))
12705
13434
  Object.freeze(delegate);
12706
13435
  Object.freeze(models);
@@ -12750,7 +13479,7 @@ function makeMountContext(params) {
12750
13479
  compileWhere: ({ where, model: modelName, qualify }) => {
12751
13480
  const target = modelName === undefined ? model : getModelMeta({ meta: engine.meta, model: modelName });
12752
13481
  if (target === null) {
12753
- throw new VibeError24({
13482
+ throw new VibeError27({
12754
13483
  code: "VIBE_INTERNAL",
12755
13484
  message: `extension "${extension}" compiled a where with no model in scope`,
12756
13485
  meta: { extension }
@@ -12773,30 +13502,30 @@ function createMountSurface(params) {
12773
13502
  return original;
12774
13503
  const wrap = (value, path) => {
12775
13504
  if (typeof value === "function") {
12776
- return (...args) => QueryPromise.create({ engine, run: async (target) => {
13505
+ return (...args) => createQueryPromise({ engine, run: async (target) => {
12777
13506
  let current = mount.create(makeMountContext({ engine: target, extension: mount.extension, model }));
12778
13507
  let receiver = undefined;
12779
13508
  for (const key of path) {
12780
13509
  receiver = current;
12781
13510
  if (current === null || typeof current !== "object" && typeof current !== "function")
12782
- throw new VibeError24({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
13511
+ throw new VibeError27({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
12783
13512
  current = Reflect.get(current, key);
12784
13513
  }
12785
13514
  if (typeof current !== "function")
12786
- throw new VibeError24({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
13515
+ throw new VibeError27({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
12787
13516
  return Reflect.apply(current, receiver, args);
12788
13517
  } });
12789
13518
  }
12790
13519
  if (value === null || typeof value !== "object")
12791
13520
  return value;
12792
13521
  if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
12793
- throw new VibeError24({ code: "VIBE_CONFIG", message: "Native RLS extension mounts must expose plain objects and methods" });
13522
+ throw new VibeError27({ code: "VIBE_CONFIG", message: "Native RLS extension mounts must expose plain objects and methods" });
12794
13523
  }
12795
13524
  const result = {};
12796
13525
  for (const key of Reflect.ownKeys(value)) {
12797
13526
  const descriptor = Object.getOwnPropertyDescriptor(value, key);
12798
13527
  if (!("value" in descriptor))
12799
- throw new VibeError24({ code: "VIBE_CONFIG", message: "Native RLS extension mount accessors are unsupported" });
13528
+ throw new VibeError27({ code: "VIBE_CONFIG", message: "Native RLS extension mount accessors are unsupported" });
12800
13529
  Object.defineProperty(result, key, { value: wrap(descriptor.value, [...path, key]), enumerable: descriptor.enumerable });
12801
13530
  }
12802
13531
  return Object.freeze(result);
@@ -12813,7 +13542,7 @@ function attachExtensionMounts(params) {
12813
13542
  const model = getModelMeta({ meta: engine.meta, model: mount.model ?? "" });
12814
13543
  const delegate = models[model.name];
12815
13544
  if (delegate === undefined || mount.property in delegate) {
12816
- throw new VibeError24({
13545
+ throw new VibeError27({
12817
13546
  code: "VIBE_CONFIG",
12818
13547
  message: `extension "${mount.extension}" cannot mount "${mount.property}" on ${model.name} \u2014 the name is taken`,
12819
13548
  meta: { extension: mount.extension, model: model.name, property: mount.property }
@@ -12824,7 +13553,7 @@ function attachExtensionMounts(params) {
12824
13553
  continue;
12825
13554
  }
12826
13555
  if (!mount.property.startsWith("$") || mount.property in client) {
12827
- throw new VibeError24({
13556
+ throw new VibeError27({
12828
13557
  code: "VIBE_CONFIG",
12829
13558
  message: `extension "${mount.extension}" cannot mount client property "${mount.property}" \u2014 root mounts are $-prefixed and must not collide`,
12830
13559
  meta: { extension: mount.extension, property: mount.property }
@@ -12841,7 +13570,7 @@ function createClient(params) {
12841
13570
  if (schema.rls !== undefined) {
12842
13571
  const dialect = getSqlDialect5({ dialect: dialectName });
12843
13572
  if (!dialect.capabilities.nativeRowLevelSecurity)
12844
- throw unsupportedCapability7({ dialect: dialectName, feature: "nativeRowLevelSecurity" });
13573
+ throw unsupportedCapability8({ dialect: dialectName, feature: "nativeRowLevelSecurity" });
12845
13574
  assertValidSchemaIR2({ schema });
12846
13575
  for (const model of schema.models) {
12847
13576
  if (model.rls !== "unprotected")
@@ -12850,9 +13579,9 @@ function createClient(params) {
12850
13579
  const role = options?.nativeRls?.runtimeRole;
12851
13580
  if (adapter.provider === "pglite") {
12852
13581
  if (typeof role !== "string" || role.length === 0 || role.includes("\x00"))
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" });
13582
+ throw new VibeError27({ 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" });
12854
13583
  } else if (role !== undefined) {
12855
- throw new VibeError24({ code: "VIBE_CONFIG", message: "Server native row-level security must use the runtime login role, not a runtimeRole switch" });
13584
+ throw new VibeError27({ code: "VIBE_CONFIG", message: "Server native row-level security must use the runtime login role, not a runtimeRole switch" });
12856
13585
  }
12857
13586
  }
12858
13587
  if (extensions?.manifest !== undefined) {
@@ -12862,7 +13591,7 @@ function createClient(params) {
12862
13591
  assertComputedManifest({ manifest: computed.manifest, specs: computed.specs });
12863
13592
  }
12864
13593
  if (computed === undefined && options !== undefined && options["computed"] !== undefined) {
12865
- throw new VibeError24({
13594
+ throw new VibeError27({
12866
13595
  code: "VIBE_CONFIG",
12867
13596
  message: "this client was generated without computed fields \u2014 declare them in vibeorm.config.ts and re-run vibeorm generate",
12868
13597
  meta: {}
@@ -12893,9 +13622,9 @@ function createClient(params) {
12893
13622
  });
12894
13623
  }
12895
13624
  // packages/runtime/src/keyset-iterator.ts
12896
- import { VibeError as VibeError25 } from "@vibeorm/schema";
13625
+ import { VibeError as VibeError28 } from "@vibeorm/schema";
12897
13626
  function validation(params) {
12898
- return new VibeError25({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
13627
+ return new VibeError28({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
12899
13628
  }
12900
13629
  function projectionFor(params) {
12901
13630
  const { plan: plan2, select, omit } = params;
@@ -12972,7 +13701,7 @@ function iterateKeyset(options) {
12972
13701
  });
12973
13702
  if (advanced === false) {
12974
13703
  stop();
12975
- throw new VibeError25({
13704
+ throw new VibeError28({
12976
13705
  code: "VIBE_DATA_INVALID",
12977
13706
  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.",
12978
13707
  meta: { model: options.model, order: plan2.signature, reason: "keyset-position-not-advancing" }
@@ -13045,7 +13774,7 @@ function iterateKeyset(options) {
13045
13774
  return iterator;
13046
13775
  }
13047
13776
  // packages/runtime/src/telemetry/collector.ts
13048
- import { VibeError as VibeError26 } from "@vibeorm/schema";
13777
+ import { VibeError as VibeError29 } from "@vibeorm/schema";
13049
13778
  function createTelemetryCollector(params) {
13050
13779
  const throwOn = new Set(params?.throwOn ?? []);
13051
13780
  const operations = [];
@@ -13055,7 +13784,7 @@ function createTelemetryCollector(params) {
13055
13784
  let shutdowns = 0;
13056
13785
  const hostile = (phase) => {
13057
13786
  if (throwOn.has(phase)) {
13058
- throw new VibeError26({
13787
+ throw new VibeError29({
13059
13788
  code: "VIBE_INTERNAL",
13060
13789
  message: `telemetry collector: deliberate ${phase} failure`,
13061
13790
  meta: { reason: "telemetry-collector-deliberate-failure", phase }
@@ -13111,7 +13840,7 @@ function createTelemetryCollector(params) {
13111
13840
  operationFor: (lookup) => {
13112
13841
  const matches = operations.filter((event) => event.method === lookup.method && (lookup.model === undefined || event.model === lookup.model));
13113
13842
  if (matches.length > 1) {
13114
- throw new VibeError26({
13843
+ throw new VibeError29({
13115
13844
  code: "VIBE_VALIDATION",
13116
13845
  message: `telemetry collector: ${matches.length} operations match ${lookup.model ?? "*"}.${lookup.method}`,
13117
13846
  meta: {
@@ -13131,6 +13860,8 @@ export {
13131
13860
  updateWithNested,
13132
13861
  toSqlExecutor,
13133
13862
  toClientName,
13863
+ sqlProjection,
13864
+ sql2 as sql,
13134
13865
  rewriteScopedCall,
13135
13866
  resolveTelemetryConfig,
13136
13867
  resolveScopedModels,
@@ -13139,6 +13870,7 @@ export {
13139
13870
  resolveRelationLink,
13140
13871
  recommendFromPlan,
13141
13872
  readWorkloadStatistics,
13873
+ readOnlyRefusal,
13142
13874
  queryFingerprint,
13143
13875
  previewOperation,
13144
13876
  parsePostgresPlan,
@@ -13151,6 +13883,7 @@ export {
13151
13883
  isDbNow,
13152
13884
  inspectNativeRlsTransaction,
13153
13885
  inspectNativeRls,
13886
+ inheritReadOnly,
13154
13887
  hasNestedWrites,
13155
13888
  getModelMeta,
13156
13889
  getFieldMeta,
@@ -13165,17 +13898,20 @@ export {
13165
13898
  explainOperation,
13166
13899
  encodeValue,
13167
13900
  encodeKeysetToken,
13901
+ delegateMethodAccess,
13168
13902
  decodeValue,
13169
13903
  decodeRows,
13170
13904
  decodeRow,
13171
13905
  decodeKeysetToken,
13172
13906
  decodeIsNoop2 as decodeIsNoop,
13173
13907
  decodeAggregateValue,
13908
+ decode2 as decode,
13174
13909
  dbNow,
13175
13910
  createWithNested,
13176
13911
  createView,
13177
13912
  createTelemetryRecorder,
13178
13913
  createTelemetryCollector,
13914
+ createSqlModel,
13179
13915
  createScopeRewriter,
13180
13916
  createClient,
13181
13917
  compileWhere,
@@ -13183,6 +13919,8 @@ export {
13183
13919
  compileOrderBy,
13184
13920
  compileKeysetOrderPlan,
13185
13921
  compileKeysetOrderInputs,
13922
+ compileAdvisoryLock,
13923
+ clientMemberAccess,
13186
13924
  checkStrictArguments,
13187
13925
  buildRuntimeMeta,
13188
13926
  buildQuery,
@@ -13190,6 +13928,7 @@ export {
13190
13928
  buildConnectHooks,
13191
13929
  attachScoped,
13192
13930
  attachRelations,
13931
+ attachReadOnly,
13193
13932
  attachPolicy,
13194
13933
  attachLateralRelations,
13195
13934
  attachDefineView,
@@ -13200,10 +13939,12 @@ export {
13200
13939
  applyComputedFields,
13201
13940
  analyzeOperationTelemetry,
13202
13941
  affectedRowsAreRowCounts,
13942
+ WRITE_DELEGATE_METHODS,
13203
13943
  ViewResult,
13204
13944
  TELEMETRY_DEFAULTS,
13205
13945
  SQLITE_CODECS,
13206
13946
  SKIP,
13947
+ READ_DELEGATE_METHODS,
13207
13948
  POSTGRES_CODECS,
13208
13949
  PLAN_DEFAULTS,
13209
13950
  MYSQL_CODECS,
@@ -13212,7 +13953,9 @@ export {
13212
13953
  KEYSET_MAX_PAGE_SIZE,
13213
13954
  DIALECT_CODECS2 as DIALECT_CODECS,
13214
13955
  DIAGNOSTIC_THRESHOLDS,
13215
- COUNT_ALIAS
13956
+ DELEGATE_METHOD_ACCESS,
13957
+ COUNT_ALIAS,
13958
+ CLIENT_MEMBER_ACCESS
13216
13959
  };
13217
13960
 
13218
- //# debugId=ADD0DD01D02385B864756E2164756E21
13961
+ //# debugId=89575B8D7F621EF764756E2164756E21