@vibeorm/runtime 2.0.0-alpha.2 → 2.0.0-alpha.4
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/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/db-now.d.ts +41 -0
- package/dist/db-now.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +454 -92
- package/dist/index.js.map +8 -7
- package/dist/model-meta.d.ts.map +1 -1
- package/dist/query-builder.d.ts.map +1 -1
- package/dist/render-cache.d.ts.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -10,8 +10,31 @@ function toSqlExecutor(params) {
|
|
|
10
10
|
return result.rows;
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
+
// packages/runtime/src/db-now.ts
|
|
14
|
+
import { VibeError } from "@vibeorm/schema";
|
|
15
|
+
function dbNow(options) {
|
|
16
|
+
const offset = options?.plusSeconds ?? 0;
|
|
17
|
+
if (typeof offset !== "number" || !Number.isInteger(offset)) {
|
|
18
|
+
throw new VibeError({
|
|
19
|
+
code: "VIBE_VALIDATION",
|
|
20
|
+
message: `dbNow plusSeconds must be a whole number of seconds, got ${String(offset)}`,
|
|
21
|
+
meta: { plusSeconds: offset }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return { __vibeDbNow: true, offsetSeconds: offset };
|
|
25
|
+
}
|
|
26
|
+
function isDbNow(value) {
|
|
27
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
28
|
+
return false;
|
|
29
|
+
const keys = Object.keys(value);
|
|
30
|
+
if (keys.length !== 2 || !keys.includes("__vibeDbNow") || !keys.includes("offsetSeconds")) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const candidate = value;
|
|
34
|
+
return candidate.__vibeDbNow === true && typeof candidate.offsetSeconds === "number" && Number.isInteger(candidate.offsetSeconds);
|
|
35
|
+
}
|
|
13
36
|
// packages/runtime/src/model-meta.ts
|
|
14
|
-
import { VibeError, isEnumType, validateComputedConfig } from "@vibeorm/schema";
|
|
37
|
+
import { VibeError as VibeError2, isEnumType, validateComputedConfig } from "@vibeorm/schema";
|
|
15
38
|
function toClientName(params) {
|
|
16
39
|
const { name } = params;
|
|
17
40
|
if (name.length === 0)
|
|
@@ -132,7 +155,7 @@ function buildModelMeta(params) {
|
|
|
132
155
|
for (const name of model.primaryKey.fields) {
|
|
133
156
|
const field = fieldByName.get(name);
|
|
134
157
|
if (field === undefined) {
|
|
135
|
-
throw new
|
|
158
|
+
throw new VibeError2({
|
|
136
159
|
code: "VIBE_SCHEMA_INVALID",
|
|
137
160
|
message: `model "${model.name}" declares composite primary-key field "${name}", which does not exist`,
|
|
138
161
|
meta: { model: model.name, field: name }
|
|
@@ -162,8 +185,9 @@ function buildModelMeta(params) {
|
|
|
162
185
|
if (primaryKey.length > 1)
|
|
163
186
|
addCompound(primaryKey.map((field) => field.name));
|
|
164
187
|
for (const index of model.indexes) {
|
|
165
|
-
if (index.kind === "unique")
|
|
188
|
+
if (index.kind === "unique" && index.where === undefined) {
|
|
166
189
|
addCompound(index.fields.map((entry) => entry.name));
|
|
190
|
+
}
|
|
167
191
|
}
|
|
168
192
|
const computedSpecs = params.computed;
|
|
169
193
|
const computed = computedSpecs === undefined || Object.keys(computedSpecs).length === 0 ? null : new Map(Object.entries(computedSpecs).map(([name, spec]) => [
|
|
@@ -210,7 +234,7 @@ function getModelMeta(params) {
|
|
|
210
234
|
const { meta, model } = params;
|
|
211
235
|
const found = meta.modelByName.get(model);
|
|
212
236
|
if (found === undefined) {
|
|
213
|
-
throw new
|
|
237
|
+
throw new VibeError2({
|
|
214
238
|
code: "VIBE_VALIDATION",
|
|
215
239
|
message: `unknown model "${model}"`,
|
|
216
240
|
meta: { model, known: [...new Set(meta.models.map((entry) => entry.name))] }
|
|
@@ -223,20 +247,20 @@ function getFieldMeta(params) {
|
|
|
223
247
|
const found = model.fieldByName.get(field);
|
|
224
248
|
if (found === undefined) {
|
|
225
249
|
if (model.computed !== null && model.computed.has(field)) {
|
|
226
|
-
throw new
|
|
250
|
+
throw new VibeError2({
|
|
227
251
|
code: "VIBE_VALIDATION",
|
|
228
252
|
message: `"${field}" on model "${model.name}" is a computed field \u2014 computed fields appear in results (select/omit) only, not in ${context}`,
|
|
229
253
|
meta: { model: model.name, field, context, computed: true }
|
|
230
254
|
});
|
|
231
255
|
}
|
|
232
256
|
if (model.relationByName.has(field)) {
|
|
233
|
-
throw new
|
|
257
|
+
throw new VibeError2({
|
|
234
258
|
code: "VIBE_VALIDATION",
|
|
235
259
|
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)`,
|
|
236
260
|
meta: { model: model.name, field, context }
|
|
237
261
|
});
|
|
238
262
|
}
|
|
239
|
-
throw new
|
|
263
|
+
throw new VibeError2({
|
|
240
264
|
code: "VIBE_VALIDATION",
|
|
241
265
|
message: `unknown field "${field}" on model "${model.name}" in ${context}`,
|
|
242
266
|
meta: { model: model.name, field, context }
|
|
@@ -245,7 +269,7 @@ function getFieldMeta(params) {
|
|
|
245
269
|
return found;
|
|
246
270
|
}
|
|
247
271
|
// packages/runtime/src/codecs.ts
|
|
248
|
-
import { VibeError as
|
|
272
|
+
import { VibeError as VibeError3, isEnumType as isEnumType2 } from "@vibeorm/schema";
|
|
249
273
|
import { DIALECT_CODECS, decodeIsNoop } from "@vibeorm/sql";
|
|
250
274
|
import {
|
|
251
275
|
DIALECT_CODECS as DIALECT_CODECS2,
|
|
@@ -341,7 +365,7 @@ var DECIMAL_LITERAL = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
|
341
365
|
function toRealNumber(params) {
|
|
342
366
|
const { field, value } = params;
|
|
343
367
|
const refuse = () => {
|
|
344
|
-
throw new
|
|
368
|
+
throw new VibeError3({
|
|
345
369
|
code: "VIBE_VALIDATION",
|
|
346
370
|
message: `"${String(value)}" is not a numeric value for the Decimal field "${field.name}"`,
|
|
347
371
|
meta: { field: field.name, value }
|
|
@@ -556,10 +580,11 @@ function decodeAggregateValue(params) {
|
|
|
556
580
|
return field === null ? value : decodeValue({ dialect, field, value });
|
|
557
581
|
}
|
|
558
582
|
// packages/runtime/src/query-builder.ts
|
|
559
|
-
import { VibeError as
|
|
583
|
+
import { VibeError as VibeError5, unsupportedCapability } from "@vibeorm/schema";
|
|
560
584
|
import {
|
|
561
585
|
SQL_DEFAULT_CELL,
|
|
562
586
|
containsPattern,
|
|
587
|
+
dbNowMarker,
|
|
563
588
|
endsWithPattern,
|
|
564
589
|
escapeLikePattern,
|
|
565
590
|
jsonParamCell,
|
|
@@ -567,9 +592,9 @@ import {
|
|
|
567
592
|
} from "@vibeorm/sql";
|
|
568
593
|
|
|
569
594
|
// packages/runtime/src/relation-plan.ts
|
|
570
|
-
import { VibeError as
|
|
595
|
+
import { VibeError as VibeError4, implicitJoinTable } from "@vibeorm/schema";
|
|
571
596
|
function validationError(params) {
|
|
572
|
-
return new
|
|
597
|
+
return new VibeError4({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
573
598
|
}
|
|
574
599
|
function isPlainObject(value) {
|
|
575
600
|
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
@@ -593,7 +618,7 @@ function singleField(params) {
|
|
|
593
618
|
}
|
|
594
619
|
const field = model.fieldByName.get(names[0]);
|
|
595
620
|
if (field === undefined) {
|
|
596
|
-
throw new
|
|
621
|
+
throw new VibeError4({
|
|
597
622
|
code: "VIBE_SCHEMA_INVALID",
|
|
598
623
|
message: `relation "${relation.name}" on ${relation.model} names unknown ${what} field "${String(names[0])}"`,
|
|
599
624
|
meta: { model: relation.model, relation: relation.name }
|
|
@@ -662,7 +687,7 @@ function resolveRelationLinkUncached(params) {
|
|
|
662
687
|
}
|
|
663
688
|
const reverse = findReverseRelation({ relation, target });
|
|
664
689
|
if (reverse === null || reverse.fields === undefined || reverse.fields.length === 0) {
|
|
665
|
-
throw new
|
|
690
|
+
throw new VibeError4({
|
|
666
691
|
code: "VIBE_SCHEMA_INVALID",
|
|
667
692
|
message: `relation "${relation.name}" on ${relation.model} has no foreign-key counterpart on ${relation.targetModel}`,
|
|
668
693
|
meta: { model: relation.model, relation: relation.name, target: relation.targetModel }
|
|
@@ -959,15 +984,15 @@ var ATOMIC_OPERATIONS = {
|
|
|
959
984
|
divide: "divide"
|
|
960
985
|
};
|
|
961
986
|
var ALLOWED_ARGS = {
|
|
962
|
-
findMany: ["where", "orderBy", "take", "skip", "cursor", "distinct", "select", "include", "omit", "relationStrategy"],
|
|
963
|
-
findFirst: ["where", "orderBy", "take", "skip", "cursor", "distinct", "select", "include", "omit", "relationStrategy"],
|
|
964
|
-
findUnique: ["where", "select", "include", "omit", "relationStrategy"],
|
|
965
|
-
create: ["data", "select", "include", "omit"],
|
|
987
|
+
findMany: ["where", "orderBy", "take", "skip", "cursor", "distinct", "select", "include", "omit", "relationStrategy", "lock"],
|
|
988
|
+
findFirst: ["where", "orderBy", "take", "skip", "cursor", "distinct", "select", "include", "omit", "relationStrategy", "lock"],
|
|
989
|
+
findUnique: ["where", "select", "include", "omit", "relationStrategy", "lock"],
|
|
990
|
+
create: ["data", "select", "include", "omit", "onConflict"],
|
|
966
991
|
createMany: ["data", "skipDuplicates"],
|
|
967
992
|
createManyAndReturn: ["data", "skipDuplicates", "select", "include", "omit"],
|
|
968
|
-
update: ["where", "data", "select", "include", "omit"],
|
|
969
|
-
updateMany: ["where", "data"],
|
|
970
|
-
upsert: ["where", "create", "update", "select", "include", "omit"],
|
|
993
|
+
update: ["where", "data", "select", "include", "omit", "skipUpdatedAt"],
|
|
994
|
+
updateMany: ["where", "data", "skipUpdatedAt"],
|
|
995
|
+
upsert: ["where", "create", "update", "select", "include", "omit", "onConflict"],
|
|
971
996
|
delete: ["where", "select", "include", "omit"],
|
|
972
997
|
deleteMany: ["where"],
|
|
973
998
|
count: ["where", "select", "take", "skip", "orderBy"],
|
|
@@ -983,7 +1008,7 @@ var AGG_FN_BY_KEY = {
|
|
|
983
1008
|
_max: "max"
|
|
984
1009
|
};
|
|
985
1010
|
function validationError2(params) {
|
|
986
|
-
return new
|
|
1011
|
+
return new VibeError5({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
987
1012
|
}
|
|
988
1013
|
function isPlainObject2(value) {
|
|
989
1014
|
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
|
|
@@ -1003,6 +1028,121 @@ function columnRef(params) {
|
|
|
1003
1028
|
const { field, table } = params;
|
|
1004
1029
|
return table === undefined ? { name: field.columnName } : { table, name: field.columnName };
|
|
1005
1030
|
}
|
|
1031
|
+
function compileLock(params) {
|
|
1032
|
+
const { args, model, method, hasRelations } = params;
|
|
1033
|
+
if (args.lock === undefined)
|
|
1034
|
+
return false;
|
|
1035
|
+
if (model.isView) {
|
|
1036
|
+
throw validationError2({
|
|
1037
|
+
message: `lock on ${model.clientName}.${method}() \u2014 a view row cannot be locked; lock the underlying table's rows instead`,
|
|
1038
|
+
meta: { model: model.name, method }
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
if (args.lock !== "update") {
|
|
1042
|
+
throw validationError2({
|
|
1043
|
+
message: `lock on ${model.clientName}.${method}() must be "update", got ${String(args.lock)}`,
|
|
1044
|
+
meta: { model: model.name, method, lock: args.lock }
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
if (args.distinct !== undefined) {
|
|
1048
|
+
throw validationError2({
|
|
1049
|
+
message: `lock cannot combine with distinct on ${model.clientName}.${method}() \u2014 postgres rejects FOR UPDATE with DISTINCT`,
|
|
1050
|
+
meta: { model: model.name, method }
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
if (hasRelations) {
|
|
1054
|
+
throw validationError2({
|
|
1055
|
+
message: `lock cannot combine with relation loading on ${model.clientName}.${method}() \u2014 lock the parent row alone, then read relations inside the same transaction`,
|
|
1056
|
+
meta: { model: model.name, method }
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
return true;
|
|
1060
|
+
}
|
|
1061
|
+
function compileConflictTarget(params) {
|
|
1062
|
+
const { dialect, meta, model, method, target, where } = params;
|
|
1063
|
+
if ((target !== undefined || where !== undefined) && dialect.doNothingForm === "insertIgnore") {
|
|
1064
|
+
throw unsupportedCapability({
|
|
1065
|
+
dialect: dialect.name,
|
|
1066
|
+
feature: `a conflict target on ${model.clientName}.${method}()`,
|
|
1067
|
+
hint: "mysql's INSERT IGNORE / ON DUPLICATE KEY fire on ANY duplicate key \u2014 no target can be named; gate the call per dialect"
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
const out = {};
|
|
1071
|
+
if (target !== undefined) {
|
|
1072
|
+
if (Array.isArray(target)) {
|
|
1073
|
+
if (target.length === 0) {
|
|
1074
|
+
throw validationError2({
|
|
1075
|
+
message: `onConflict target on ${model.clientName}.${method}() must name at least one field`,
|
|
1076
|
+
meta: { model: model.name, method }
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
out.columns = target.map((name) => getFieldMeta({ model, field: String(name), context: "onConflict target" }).columnName);
|
|
1080
|
+
} else if (isPlainObject2(target) && typeof target.constraint === "string" && target.constraint.length > 0 && Object.keys(target).length === 1) {
|
|
1081
|
+
if (!dialect.capabilities.conflictTargetConstraint) {
|
|
1082
|
+
throw unsupportedCapability({
|
|
1083
|
+
dialect: dialect.name,
|
|
1084
|
+
feature: "a named-constraint conflict target (ON CONFLICT ON CONSTRAINT)",
|
|
1085
|
+
hint: "only postgres supports ON CONSTRAINT targets \u2014 name the indexed columns instead"
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
out.constraint = target.constraint;
|
|
1089
|
+
} else {
|
|
1090
|
+
throw validationError2({
|
|
1091
|
+
message: `onConflict target on ${model.clientName}.${method}() must be a field-name array or { constraint: string }`,
|
|
1092
|
+
meta: { model: model.name, method }
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (where !== undefined) {
|
|
1097
|
+
if (out.columns === undefined) {
|
|
1098
|
+
throw validationError2({
|
|
1099
|
+
message: `onConflict where on ${model.clientName}.${method}() needs a column-list target \u2014 the predicate refines "(columns) WHERE \u2026"`,
|
|
1100
|
+
meta: { model: model.name, method }
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
if (!dialect.capabilities.partialIndexes) {
|
|
1104
|
+
throw unsupportedCapability({
|
|
1105
|
+
dialect: dialect.name,
|
|
1106
|
+
feature: "a partial-index conflict target",
|
|
1107
|
+
hint: "this dialect has no partial indexes to target"
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1110
|
+
if (typeof where !== "string" || where.trim().length === 0) {
|
|
1111
|
+
throw validationError2({
|
|
1112
|
+
message: `onConflict where on ${model.clientName}.${method}() must be the partial index's raw predicate string (the schema declaration's where: text)`,
|
|
1113
|
+
meta: { model: model.name, method }
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
out.conflictWhere = where;
|
|
1117
|
+
}
|
|
1118
|
+
return out;
|
|
1119
|
+
}
|
|
1120
|
+
function compileDbNowWrite(params) {
|
|
1121
|
+
const { model, field, value } = params;
|
|
1122
|
+
if (field.scalarType !== "DateTime" || field.isList) {
|
|
1123
|
+
throw validationError2({
|
|
1124
|
+
message: `dbNow() writes the database clock into a DateTime field \u2014 ${model.name}.${field.name} is ${String(field.enumName ?? field.scalarType)}${field.isList ? "[]" : ""}`,
|
|
1125
|
+
meta: { model: model.name, field: field.name }
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
if (field.isId || field.isUnique || model.primaryKey.some((candidate) => candidate.name === field.name)) {
|
|
1129
|
+
throw validationError2({
|
|
1130
|
+
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`,
|
|
1131
|
+
meta: { model: model.name, field: field.name }
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
return dbNowMarker({ offsetSeconds: value.offsetSeconds });
|
|
1135
|
+
}
|
|
1136
|
+
function compileDbNowFilter(params) {
|
|
1137
|
+
const { model, field, value } = params;
|
|
1138
|
+
if (field.scalarType !== "DateTime" || field.isList) {
|
|
1139
|
+
throw validationError2({
|
|
1140
|
+
message: `dbNow() compares against a DateTime field \u2014 ${model.name}.${field.name} is ${String(field.enumName ?? field.scalarType)}${field.isList ? "[]" : ""}`,
|
|
1141
|
+
meta: { model: model.name, field: field.name }
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
return dbNowMarker({ offsetSeconds: value.offsetSeconds });
|
|
1145
|
+
}
|
|
1006
1146
|
function requireObject(params) {
|
|
1007
1147
|
const { value, what, model } = params;
|
|
1008
1148
|
if (!isPlainObject2(value)) {
|
|
@@ -1319,7 +1459,7 @@ function compileOperatorFilter(params) {
|
|
|
1319
1459
|
kind: "cmp",
|
|
1320
1460
|
op: "eq",
|
|
1321
1461
|
column,
|
|
1322
|
-
value: encodeFilterValue({ dialect: dialect.name, field, value: operand })
|
|
1462
|
+
value: isDbNow(operand) ? compileDbNowFilter({ model, field, value: operand }) : encodeFilterValue({ dialect: dialect.name, field, value: operand })
|
|
1323
1463
|
});
|
|
1324
1464
|
break;
|
|
1325
1465
|
}
|
|
@@ -1343,7 +1483,7 @@ function compileOperatorFilter(params) {
|
|
|
1343
1483
|
kind: "cmp",
|
|
1344
1484
|
op: "ne",
|
|
1345
1485
|
column,
|
|
1346
|
-
value: encodeFilterValue({ dialect: dialect.name, field, value: operand })
|
|
1486
|
+
value: isDbNow(operand) ? compileDbNowFilter({ model, field, value: operand }) : encodeFilterValue({ dialect: dialect.name, field, value: operand })
|
|
1347
1487
|
});
|
|
1348
1488
|
break;
|
|
1349
1489
|
}
|
|
@@ -1355,6 +1495,14 @@ function compileOperatorFilter(params) {
|
|
|
1355
1495
|
meta: { model: model.name, field: field.name, operator }
|
|
1356
1496
|
});
|
|
1357
1497
|
}
|
|
1498
|
+
for (const item of operand) {
|
|
1499
|
+
if (isDbNow(item)) {
|
|
1500
|
+
throw validationError2({
|
|
1501
|
+
message: `dbNow() cannot ride an "${operator}" list on ${model.name}.${field.name} \u2014 compare with lt/lte/gt/gte/equals instead`,
|
|
1502
|
+
meta: { model: model.name, field: field.name, operator }
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1358
1506
|
conditions.push({
|
|
1359
1507
|
kind: "inArray",
|
|
1360
1508
|
column,
|
|
@@ -1371,7 +1519,7 @@ function compileOperatorFilter(params) {
|
|
|
1371
1519
|
kind: "cmp",
|
|
1372
1520
|
op: operator,
|
|
1373
1521
|
column,
|
|
1374
|
-
value: encodeFilterValue({ dialect: dialect.name, field, value: operand })
|
|
1522
|
+
value: isDbNow(operand) ? compileDbNowFilter({ model, field, value: operand }) : encodeFilterValue({ dialect: dialect.name, field, value: operand })
|
|
1375
1523
|
});
|
|
1376
1524
|
break;
|
|
1377
1525
|
}
|
|
@@ -1427,6 +1575,9 @@ function compileFieldFilter(params) {
|
|
|
1427
1575
|
if (isDirectValue(value)) {
|
|
1428
1576
|
return { kind: "cmp", op: "eq", column, value: encodeFilterValue({ dialect: dialect.name, field, value }) };
|
|
1429
1577
|
}
|
|
1578
|
+
if (isDbNow(value)) {
|
|
1579
|
+
return { kind: "cmp", op: "eq", column, value: compileDbNowFilter({ model, field, value }) };
|
|
1580
|
+
}
|
|
1430
1581
|
if (Array.isArray(value)) {
|
|
1431
1582
|
if (!field.isList) {
|
|
1432
1583
|
throw validationError2({
|
|
@@ -2074,6 +2225,17 @@ function compileCreateRow(params) {
|
|
|
2074
2225
|
}
|
|
2075
2226
|
if (entry.listRefused)
|
|
2076
2227
|
assertListWritable({ dialect, model, field: entry.field });
|
|
2228
|
+
if (isDbNow(value)) {
|
|
2229
|
+
raw.set(entry.field.name, value);
|
|
2230
|
+
values[entry.field.columnName] = compileDbNowWrite({ model, field: entry.field, value });
|
|
2231
|
+
continue;
|
|
2232
|
+
}
|
|
2233
|
+
if (Array.isArray(value) && value.some((item) => isDbNow(item))) {
|
|
2234
|
+
throw validationError2({
|
|
2235
|
+
message: `dbNow() cannot be a list element (${model.name}.${entry.field.name})`,
|
|
2236
|
+
meta: { model: model.name, field: entry.field.name }
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2077
2239
|
raw.set(entry.field.name, value);
|
|
2078
2240
|
values[entry.field.columnName] = encodeWith(entry, value);
|
|
2079
2241
|
}
|
|
@@ -2145,6 +2307,16 @@ function compileUpdateSet(params) {
|
|
|
2145
2307
|
if (entry.listRefused)
|
|
2146
2308
|
assertListWritable({ dialect, model, field });
|
|
2147
2309
|
touched.add(field.name);
|
|
2310
|
+
if (isDbNow(value)) {
|
|
2311
|
+
set[field.columnName] = compileDbNowWrite({ model, field, value });
|
|
2312
|
+
continue;
|
|
2313
|
+
}
|
|
2314
|
+
if (Array.isArray(value) && value.some((item) => isDbNow(item))) {
|
|
2315
|
+
throw validationError2({
|
|
2316
|
+
message: `dbNow() cannot be a list element (${model.name}.${field.name})`,
|
|
2317
|
+
meta: { model: model.name, field: field.name }
|
|
2318
|
+
});
|
|
2319
|
+
}
|
|
2148
2320
|
if (isPlainObject2(value) && field.scalarType !== "Json") {
|
|
2149
2321
|
const entries = Object.entries(value).filter(([, operand2]) => operand2 !== undefined);
|
|
2150
2322
|
if (entries.length !== 1) {
|
|
@@ -2155,7 +2327,7 @@ function compileUpdateSet(params) {
|
|
|
2155
2327
|
}
|
|
2156
2328
|
const [operator, operand] = entries[0];
|
|
2157
2329
|
if (operator === "set") {
|
|
2158
|
-
set[field.columnName] = encodeWith(entry, operand);
|
|
2330
|
+
set[field.columnName] = isDbNow(operand) ? compileDbNowWrite({ model, field, value: operand }) : encodeWith(entry, operand);
|
|
2159
2331
|
continue;
|
|
2160
2332
|
}
|
|
2161
2333
|
if (operator === "push" && field.isList) {
|
|
@@ -2174,10 +2346,12 @@ function compileUpdateSet(params) {
|
|
|
2174
2346
|
}
|
|
2175
2347
|
set[field.columnName] = encodeWith(entry, value);
|
|
2176
2348
|
}
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2349
|
+
if (params.skipUpdatedAt !== true) {
|
|
2350
|
+
for (const entry of plan.injected) {
|
|
2351
|
+
if (!entry.field.isUpdatedAt || touched.has(entry.field.name))
|
|
2352
|
+
continue;
|
|
2353
|
+
set[entry.field.columnName] = encodeWith(entry, new Date);
|
|
2354
|
+
}
|
|
2181
2355
|
}
|
|
2182
2356
|
if (Object.keys(set).length === 0) {
|
|
2183
2357
|
throw validationError2({
|
|
@@ -3099,6 +3273,7 @@ function buildQuery(params) {
|
|
|
3099
3273
|
switch (method) {
|
|
3100
3274
|
case "findMany":
|
|
3101
3275
|
case "findFirst": {
|
|
3276
|
+
const locked = compileLock({ args, model, method, hasRelations: relations !== null });
|
|
3102
3277
|
const shape = buildFindShape({
|
|
3103
3278
|
dialect,
|
|
3104
3279
|
meta,
|
|
@@ -3111,12 +3286,13 @@ function buildQuery(params) {
|
|
|
3111
3286
|
});
|
|
3112
3287
|
return {
|
|
3113
3288
|
...base,
|
|
3114
|
-
statement: shape.statement,
|
|
3289
|
+
statement: locked ? { ...shape.statement, lockForUpdate: true } : shape.statement,
|
|
3115
3290
|
reverseRows: shape.reverseRows,
|
|
3116
3291
|
resortBy: shape.resortBy
|
|
3117
3292
|
};
|
|
3118
3293
|
}
|
|
3119
3294
|
case "findUnique": {
|
|
3295
|
+
const locked = compileLock({ args, model, method, hasRelations: relations !== null });
|
|
3120
3296
|
const where = expandCompoundWhere({
|
|
3121
3297
|
model,
|
|
3122
3298
|
where: requireObject({ value: args.where, what: "where", model })
|
|
@@ -3131,7 +3307,10 @@ function buildQuery(params) {
|
|
|
3131
3307
|
...useLateral ? { selection: relations } : {},
|
|
3132
3308
|
clientOptions: undefined
|
|
3133
3309
|
});
|
|
3134
|
-
return {
|
|
3310
|
+
return {
|
|
3311
|
+
...base,
|
|
3312
|
+
statement: locked ? { ...shape.statement, lockForUpdate: true } : shape.statement
|
|
3313
|
+
};
|
|
3135
3314
|
}
|
|
3136
3315
|
case "count": {
|
|
3137
3316
|
const where = args.where === undefined ? undefined : compileWhere({ dialect, meta, model, where: requireObject({ value: args.where, what: "where", model }) });
|
|
@@ -3321,6 +3500,38 @@ function buildQuery(params) {
|
|
|
3321
3500
|
};
|
|
3322
3501
|
}
|
|
3323
3502
|
case "create": {
|
|
3503
|
+
let conflictClause;
|
|
3504
|
+
if (args.onConflict !== undefined) {
|
|
3505
|
+
const spec = requireObject({ value: args.onConflict, what: "onConflict", model });
|
|
3506
|
+
for (const key of Object.keys(spec)) {
|
|
3507
|
+
if (key !== "action" && key !== "target" && key !== "where") {
|
|
3508
|
+
throw validationError2({
|
|
3509
|
+
message: `unknown onConflict key "${key}" on ${model.clientName}.create() \u2014 expected action/target/where`,
|
|
3510
|
+
meta: { model: model.name, key }
|
|
3511
|
+
});
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
if (spec.action !== "nothing") {
|
|
3515
|
+
throw validationError2({
|
|
3516
|
+
message: `onConflict.action on ${model.clientName}.create() must be "nothing" (conflicting rows are skipped \u2014 to update instead, use upsert())`,
|
|
3517
|
+
meta: { model: model.name }
|
|
3518
|
+
});
|
|
3519
|
+
}
|
|
3520
|
+
const compiled = compileConflictTarget({
|
|
3521
|
+
dialect,
|
|
3522
|
+
meta,
|
|
3523
|
+
model,
|
|
3524
|
+
method,
|
|
3525
|
+
target: spec.target,
|
|
3526
|
+
where: spec.where
|
|
3527
|
+
});
|
|
3528
|
+
conflictClause = {
|
|
3529
|
+
doNothing: true,
|
|
3530
|
+
...compiled.columns === undefined ? {} : { columns: compiled.columns },
|
|
3531
|
+
...compiled.constraint === undefined ? {} : { constraint: compiled.constraint },
|
|
3532
|
+
...compiled.conflictWhere === undefined ? {} : { conflictWhere: compiled.conflictWhere }
|
|
3533
|
+
};
|
|
3534
|
+
}
|
|
3324
3535
|
const row = compileCreateRow({ dialect, model, data: args.data });
|
|
3325
3536
|
const columns = orderedColumns({ model, values: row.values });
|
|
3326
3537
|
if (columns.length === 0) {
|
|
@@ -3337,6 +3548,7 @@ function buildQuery(params) {
|
|
|
3337
3548
|
table: { name: model.tableName },
|
|
3338
3549
|
columns,
|
|
3339
3550
|
rows: [columns.map((column) => row.values[column])],
|
|
3551
|
+
...conflictClause === undefined ? {} : { onConflict: conflictClause },
|
|
3340
3552
|
returning: returningColumns({ model, selectedFields })
|
|
3341
3553
|
}
|
|
3342
3554
|
};
|
|
@@ -3351,13 +3563,40 @@ function buildQuery(params) {
|
|
|
3351
3563
|
});
|
|
3352
3564
|
}
|
|
3353
3565
|
const groups = compileCreateRows({ dialect, model, data: args.data });
|
|
3354
|
-
|
|
3566
|
+
let manyConflict;
|
|
3567
|
+
if (args.skipDuplicates === true) {
|
|
3568
|
+
manyConflict = { doNothing: true };
|
|
3569
|
+
} else if (args.skipDuplicates !== undefined && args.skipDuplicates !== false) {
|
|
3570
|
+
const spec = requireObject({ value: args.skipDuplicates, what: "skipDuplicates", model });
|
|
3571
|
+
for (const key of Object.keys(spec)) {
|
|
3572
|
+
if (key !== "target" && key !== "where") {
|
|
3573
|
+
throw validationError2({
|
|
3574
|
+
message: `unknown skipDuplicates key "${key}" on ${model.clientName}.${method}() \u2014 expected target/where`,
|
|
3575
|
+
meta: { model: model.name, key }
|
|
3576
|
+
});
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
const compiled = compileConflictTarget({
|
|
3580
|
+
dialect,
|
|
3581
|
+
meta,
|
|
3582
|
+
model,
|
|
3583
|
+
method,
|
|
3584
|
+
target: spec.target,
|
|
3585
|
+
where: spec.where
|
|
3586
|
+
});
|
|
3587
|
+
manyConflict = {
|
|
3588
|
+
doNothing: true,
|
|
3589
|
+
...compiled.columns === undefined ? {} : { columns: compiled.columns },
|
|
3590
|
+
...compiled.constraint === undefined ? {} : { constraint: compiled.constraint },
|
|
3591
|
+
...compiled.conflictWhere === undefined ? {} : { conflictWhere: compiled.conflictWhere }
|
|
3592
|
+
};
|
|
3593
|
+
}
|
|
3355
3594
|
const statements = groups.map((group) => ({
|
|
3356
3595
|
kind: "insert",
|
|
3357
3596
|
table: { name: model.tableName },
|
|
3358
3597
|
columns: group.columns,
|
|
3359
3598
|
rows: group.rows,
|
|
3360
|
-
...
|
|
3599
|
+
...manyConflict === undefined ? {} : { onConflict: manyConflict },
|
|
3361
3600
|
...method === "createManyAndReturn" ? { returning: returningColumns({ model, selectedFields }) } : {}
|
|
3362
3601
|
}));
|
|
3363
3602
|
return {
|
|
@@ -3369,6 +3608,12 @@ function buildQuery(params) {
|
|
|
3369
3608
|
case "update":
|
|
3370
3609
|
case "updateMany": {
|
|
3371
3610
|
const isUnique = method === "update";
|
|
3611
|
+
if (args.skipUpdatedAt !== undefined && typeof args.skipUpdatedAt !== "boolean") {
|
|
3612
|
+
throw validationError2({
|
|
3613
|
+
message: `skipUpdatedAt on ${model.clientName}.${method}() must be a boolean, got ${String(args.skipUpdatedAt)}`,
|
|
3614
|
+
meta: { model: model.name, method }
|
|
3615
|
+
});
|
|
3616
|
+
}
|
|
3372
3617
|
const raw = requireObject({ value: args.where, what: "where", model });
|
|
3373
3618
|
const where = isUnique ? expandCompoundWhere({ model, where: raw }) : raw;
|
|
3374
3619
|
if (isUnique)
|
|
@@ -3380,13 +3625,36 @@ function buildQuery(params) {
|
|
|
3380
3625
|
statement: {
|
|
3381
3626
|
kind: "update",
|
|
3382
3627
|
table: { name: model.tableName },
|
|
3383
|
-
set: compileUpdateSet({
|
|
3628
|
+
set: compileUpdateSet({
|
|
3629
|
+
dialect,
|
|
3630
|
+
model,
|
|
3631
|
+
data: args.data,
|
|
3632
|
+
...args.skipUpdatedAt === true ? { skipUpdatedAt: true } : {}
|
|
3633
|
+
}),
|
|
3384
3634
|
...compiled === undefined ? {} : { where: compiled },
|
|
3385
3635
|
...isUnique ? { returning: returningColumns({ model, selectedFields }) } : {}
|
|
3386
3636
|
}
|
|
3387
3637
|
};
|
|
3388
3638
|
}
|
|
3389
3639
|
case "upsert": {
|
|
3640
|
+
let constraintTarget;
|
|
3641
|
+
if (args.onConflict !== undefined) {
|
|
3642
|
+
const spec = requireObject({ value: args.onConflict, what: "onConflict", model });
|
|
3643
|
+
if (typeof spec.constraint !== "string" || spec.constraint.length === 0 || Object.keys(spec).length !== 1) {
|
|
3644
|
+
throw validationError2({
|
|
3645
|
+
message: `onConflict on ${model.clientName}.upsert() must be { constraint: string } \u2014 the named unique constraint that arbitrates the conflict`,
|
|
3646
|
+
meta: { model: model.name }
|
|
3647
|
+
});
|
|
3648
|
+
}
|
|
3649
|
+
if (!dialect.capabilities.conflictTargetConstraint) {
|
|
3650
|
+
throw unsupportedCapability({
|
|
3651
|
+
dialect: dialect.name,
|
|
3652
|
+
feature: "a named-constraint conflict target on upsert",
|
|
3653
|
+
hint: "only postgres supports ON CONFLICT ON CONSTRAINT \u2014 rely on the where-derived column target instead"
|
|
3654
|
+
});
|
|
3655
|
+
}
|
|
3656
|
+
constraintTarget = spec.constraint;
|
|
3657
|
+
}
|
|
3390
3658
|
const where = expandCompoundWhere({
|
|
3391
3659
|
model,
|
|
3392
3660
|
where: requireObject({ value: args.where, what: "where", model })
|
|
@@ -3397,7 +3665,7 @@ function buildQuery(params) {
|
|
|
3397
3665
|
const targetFields = conflictTargetFields({ model, where });
|
|
3398
3666
|
for (const field of targetFields) {
|
|
3399
3667
|
if (!(field.columnName in row.values)) {
|
|
3400
|
-
throw new
|
|
3668
|
+
throw new VibeError5({
|
|
3401
3669
|
code: "VIBE_INTERNAL",
|
|
3402
3670
|
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`,
|
|
3403
3671
|
meta: { model: model.name, column: field.columnName }
|
|
@@ -3408,7 +3676,7 @@ function buildQuery(params) {
|
|
|
3408
3676
|
for (const [key, value] of Object.entries(where)) {
|
|
3409
3677
|
if (value === undefined || nativeTargetNames.has(key))
|
|
3410
3678
|
continue;
|
|
3411
|
-
throw new
|
|
3679
|
+
throw new VibeError5({
|
|
3412
3680
|
code: "VIBE_INTERNAL",
|
|
3413
3681
|
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`,
|
|
3414
3682
|
meta: { model: model.name, field: key }
|
|
@@ -3428,6 +3696,7 @@ function buildQuery(params) {
|
|
|
3428
3696
|
rows: [columns.map((column) => row.values[column])],
|
|
3429
3697
|
onConflict: {
|
|
3430
3698
|
columns: targetFields.map((field) => field.columnName),
|
|
3699
|
+
...constraintTarget === undefined ? {} : { constraint: constraintTarget },
|
|
3431
3700
|
update,
|
|
3432
3701
|
...odkuKeyColumn === undefined ? {} : { lastInsertIdColumn: odkuKeyColumn }
|
|
3433
3702
|
},
|
|
@@ -3457,10 +3726,10 @@ function buildQuery(params) {
|
|
|
3457
3726
|
}
|
|
3458
3727
|
}
|
|
3459
3728
|
// packages/runtime/src/relation-loader.ts
|
|
3460
|
-
import { VibeError as
|
|
3729
|
+
import { VibeError as VibeError7 } from "@vibeorm/schema";
|
|
3461
3730
|
|
|
3462
3731
|
// packages/runtime/src/computed.ts
|
|
3463
|
-
import { VibeError as
|
|
3732
|
+
import { VibeError as VibeError6 } from "@vibeorm/schema";
|
|
3464
3733
|
function sameNeeds(a, b) {
|
|
3465
3734
|
if (a.length !== b.length)
|
|
3466
3735
|
return false;
|
|
@@ -3496,7 +3765,7 @@ function assertComputedManifest(params) {
|
|
|
3496
3765
|
problems.push(`"${key}" is configured but missing from the generated client`);
|
|
3497
3766
|
}
|
|
3498
3767
|
if (problems.length > 0) {
|
|
3499
|
-
throw new
|
|
3768
|
+
throw new VibeError6({
|
|
3500
3769
|
code: "VIBE_STALE_CLIENT",
|
|
3501
3770
|
message: `computed-field config changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
|
|
3502
3771
|
meta: { problems }
|
|
@@ -3660,7 +3929,7 @@ async function loadManyToMany(params) {
|
|
|
3660
3929
|
const link = node.link;
|
|
3661
3930
|
const join = link.join;
|
|
3662
3931
|
if (join === null) {
|
|
3663
|
-
throw new
|
|
3932
|
+
throw new VibeError7({
|
|
3664
3933
|
code: "VIBE_INTERNAL",
|
|
3665
3934
|
message: `manyToMany relation "${node.name}" resolved without join-table wiring`
|
|
3666
3935
|
});
|
|
@@ -3858,7 +4127,7 @@ async function attachLateralRelations(params) {
|
|
|
3858
4127
|
try {
|
|
3859
4128
|
parsed = JSON.parse(raw);
|
|
3860
4129
|
} catch (cause) {
|
|
3861
|
-
throw new
|
|
4130
|
+
throw new VibeError7({
|
|
3862
4131
|
code: "VIBE_INTERNAL",
|
|
3863
4132
|
message: `relation column "${column}" returned text that is not valid JSON`,
|
|
3864
4133
|
meta: { relation: node.name },
|
|
@@ -3895,12 +4164,12 @@ async function attachLateralRelations(params) {
|
|
|
3895
4164
|
await loadCounts({ ctx, model, rows, counts: selection.counts });
|
|
3896
4165
|
}
|
|
3897
4166
|
// packages/runtime/src/nested-writes.ts
|
|
3898
|
-
import { VibeError as
|
|
4167
|
+
import { VibeError as VibeError8 } from "@vibeorm/schema";
|
|
3899
4168
|
function validationError3(params) {
|
|
3900
|
-
return new
|
|
4169
|
+
return new VibeError8({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
3901
4170
|
}
|
|
3902
4171
|
function notFound(params) {
|
|
3903
|
-
return new
|
|
4172
|
+
return new VibeError8({ code: "VIBE_NOT_FOUND", message: params.message, meta: params.meta });
|
|
3904
4173
|
}
|
|
3905
4174
|
function isPlainObject3(value) {
|
|
3906
4175
|
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
|
|
@@ -4730,7 +4999,7 @@ async function updateWithNested(params) {
|
|
|
4730
4999
|
});
|
|
4731
5000
|
}
|
|
4732
5001
|
// packages/runtime/src/extensions.ts
|
|
4733
|
-
import { VibeError as
|
|
5002
|
+
import { VibeError as VibeError9, extensionManifestEntry } from "@vibeorm/schema";
|
|
4734
5003
|
function assertExtensionManifest(params) {
|
|
4735
5004
|
const { manifest } = params;
|
|
4736
5005
|
const instances = params.instances ?? [];
|
|
@@ -4753,7 +5022,7 @@ function assertExtensionManifest(params) {
|
|
|
4753
5022
|
problems.push(`"${name}" is configured but missing from the generated client`);
|
|
4754
5023
|
}
|
|
4755
5024
|
if (problems.length > 0) {
|
|
4756
|
-
throw new
|
|
5025
|
+
throw new VibeError9({
|
|
4757
5026
|
code: "VIBE_STALE_CLIENT",
|
|
4758
5027
|
message: `extension list changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
|
|
4759
5028
|
meta: { manifest, problems }
|
|
@@ -4768,7 +5037,7 @@ function buildExtensionOperatorMap(params) {
|
|
|
4768
5037
|
for (const operator of extension.sql?.whereOperators ?? []) {
|
|
4769
5038
|
const existing = owner.get(operator.name);
|
|
4770
5039
|
if (existing !== undefined) {
|
|
4771
|
-
throw new
|
|
5040
|
+
throw new VibeError9({
|
|
4772
5041
|
code: "VIBE_CONFIG",
|
|
4773
5042
|
message: `where operator "${operator.name}" is claimed by both "${existing}" and "${extension.name}"`,
|
|
4774
5043
|
meta: { operator: operator.name }
|
|
@@ -4784,7 +5053,7 @@ function buildExtensionOperatorMap(params) {
|
|
|
4784
5053
|
options: extension.options
|
|
4785
5054
|
});
|
|
4786
5055
|
if ("error" in compiled) {
|
|
4787
|
-
throw new
|
|
5056
|
+
throw new VibeError9({
|
|
4788
5057
|
code: "VIBE_VALIDATION",
|
|
4789
5058
|
message: `"${operator.name}" on ${model}.${field}: ${compiled.error}`,
|
|
4790
5059
|
meta: { model, field, operator: operator.name, extension: extension.name }
|
|
@@ -4816,9 +5085,9 @@ function buildConnectHooks(params) {
|
|
|
4816
5085
|
return hooks;
|
|
4817
5086
|
}
|
|
4818
5087
|
// packages/runtime/src/views.ts
|
|
4819
|
-
import { VibeError as
|
|
5088
|
+
import { VibeError as VibeError10 } from "@vibeorm/schema";
|
|
4820
5089
|
function validationError4(params) {
|
|
4821
|
-
return new
|
|
5090
|
+
return new VibeError10({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
4822
5091
|
}
|
|
4823
5092
|
|
|
4824
5093
|
class ViewResult {
|
|
@@ -5170,7 +5439,7 @@ function createView(params) {
|
|
|
5170
5439
|
for (const [clientName, mask] of resolved) {
|
|
5171
5440
|
const delegate = models[clientName];
|
|
5172
5441
|
if (delegate === undefined) {
|
|
5173
|
-
throw new
|
|
5442
|
+
throw new VibeError10({
|
|
5174
5443
|
code: "VIBE_INTERNAL",
|
|
5175
5444
|
message: `view "${name}": no delegate for model "${mask.model.name}"`,
|
|
5176
5445
|
meta: { view: name, model: mask.model.name }
|
|
@@ -5186,11 +5455,11 @@ function attachDefineView(params) {
|
|
|
5186
5455
|
Object.defineProperty(client, "$defineView", { value, enumerable: true });
|
|
5187
5456
|
}
|
|
5188
5457
|
// packages/runtime/src/client.ts
|
|
5189
|
-
import { VibeError as
|
|
5458
|
+
import { VibeError as VibeError11, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
|
|
5190
5459
|
import { ParamCollector, getSqlDialect } from "@vibeorm/sql";
|
|
5191
5460
|
|
|
5192
5461
|
// packages/runtime/src/render-cache.ts
|
|
5193
|
-
import { isListSetOperation, isSetOperation, isSqlDefaultCell, renderStatement, strictMatchPattern } from "@vibeorm/sql";
|
|
5462
|
+
import { isDbNowMarker, isListSetOperation, isSetOperation, isSqlDefaultCell, renderStatement, strictMatchPattern } from "@vibeorm/sql";
|
|
5194
5463
|
var MAX_ENTRIES = 512;
|
|
5195
5464
|
|
|
5196
5465
|
class StatementRenderCache {
|
|
@@ -5240,6 +5509,13 @@ function addSetEntry(walk, name, value) {
|
|
|
5240
5509
|
walk.values.push(value.value);
|
|
5241
5510
|
return;
|
|
5242
5511
|
}
|
|
5512
|
+
if (isDbNowMarker(value)) {
|
|
5513
|
+
walk.key += value.offsetSeconds === 0 ? "=N0" : "=N1";
|
|
5514
|
+
addIdent(walk, name);
|
|
5515
|
+
if (value.offsetSeconds !== 0)
|
|
5516
|
+
walk.values.push(value.offsetSeconds);
|
|
5517
|
+
return;
|
|
5518
|
+
}
|
|
5243
5519
|
if (isListSetOperation(value)) {
|
|
5244
5520
|
walk.key += `=l${value.__listOp}`;
|
|
5245
5521
|
addIdent(walk, name);
|
|
@@ -5263,6 +5539,13 @@ function addReturning(walk, returning) {
|
|
|
5263
5539
|
function walkExpr(walk, dialect, expr) {
|
|
5264
5540
|
switch (expr.kind) {
|
|
5265
5541
|
case "cmp":
|
|
5542
|
+
if (isDbNowMarker(expr.value)) {
|
|
5543
|
+
walk.key += `cN${expr.op}${expr.value.offsetSeconds === 0 ? "0" : "1"}`;
|
|
5544
|
+
addCol(walk, expr.column);
|
|
5545
|
+
if (expr.value.offsetSeconds !== 0)
|
|
5546
|
+
walk.values.push(expr.value.offsetSeconds);
|
|
5547
|
+
return true;
|
|
5548
|
+
}
|
|
5266
5549
|
walk.key += `c${expr.op}`;
|
|
5267
5550
|
addCol(walk, expr.column);
|
|
5268
5551
|
walk.values.push(expr.value);
|
|
@@ -5478,7 +5761,7 @@ function walkSelect(walk, dialect, statement) {
|
|
|
5478
5761
|
walk.key += "}";
|
|
5479
5762
|
return true;
|
|
5480
5763
|
}
|
|
5481
|
-
function walkInsert(walk, statement) {
|
|
5764
|
+
function walkInsert(walk, dialect, statement) {
|
|
5482
5765
|
walk.key += "I{";
|
|
5483
5766
|
addTable(walk, statement.table);
|
|
5484
5767
|
walk.key += "c(";
|
|
@@ -5494,7 +5777,11 @@ function walkInsert(walk, statement) {
|
|
|
5494
5777
|
const value = row[cell];
|
|
5495
5778
|
if (isSqlDefaultCell(value))
|
|
5496
5779
|
walk.key += `D${rowIndex}:${cell};`;
|
|
5497
|
-
else
|
|
5780
|
+
else if (isDbNowMarker(value)) {
|
|
5781
|
+
walk.key += `T${rowIndex}:${cell}:${value.offsetSeconds === 0 ? "0" : "1"};`;
|
|
5782
|
+
if (value.offsetSeconds !== 0)
|
|
5783
|
+
walk.values.push(value.offsetSeconds);
|
|
5784
|
+
} else
|
|
5498
5785
|
walk.values.push(value);
|
|
5499
5786
|
}
|
|
5500
5787
|
}
|
|
@@ -5502,13 +5789,25 @@ function walkInsert(walk, statement) {
|
|
|
5502
5789
|
if (onConflict !== undefined) {
|
|
5503
5790
|
if ("doNothing" in onConflict) {
|
|
5504
5791
|
walk.key += "N(";
|
|
5792
|
+
if (onConflict.constraint !== undefined) {
|
|
5793
|
+
walk.key += "K";
|
|
5794
|
+
addIdent(walk, onConflict.constraint);
|
|
5795
|
+
}
|
|
5505
5796
|
if (onConflict.columns !== undefined) {
|
|
5506
5797
|
for (const name of onConflict.columns)
|
|
5507
5798
|
addIdent(walk, name);
|
|
5508
5799
|
}
|
|
5800
|
+
if (onConflict.conflictWhere !== undefined) {
|
|
5801
|
+
walk.key += "w";
|
|
5802
|
+
addIdent(walk, onConflict.conflictWhere);
|
|
5803
|
+
}
|
|
5509
5804
|
walk.key += ")";
|
|
5510
5805
|
} else {
|
|
5511
5806
|
walk.key += "U(";
|
|
5807
|
+
if (onConflict.constraint !== undefined) {
|
|
5808
|
+
walk.key += "K";
|
|
5809
|
+
addIdent(walk, onConflict.constraint);
|
|
5810
|
+
}
|
|
5512
5811
|
for (const name of onConflict.columns)
|
|
5513
5812
|
addIdent(walk, name);
|
|
5514
5813
|
walk.key += ":";
|
|
@@ -5556,7 +5855,7 @@ function walkStatement(walk, dialect, statement) {
|
|
|
5556
5855
|
case "select":
|
|
5557
5856
|
return walkSelect(walk, dialect, statement);
|
|
5558
5857
|
case "insert":
|
|
5559
|
-
return walkInsert(walk, statement);
|
|
5858
|
+
return walkInsert(walk, dialect, statement);
|
|
5560
5859
|
case "update":
|
|
5561
5860
|
return walkUpdate(walk, dialect, statement);
|
|
5562
5861
|
case "delete":
|
|
@@ -5606,7 +5905,7 @@ function renderStatementCached(params) {
|
|
|
5606
5905
|
// packages/runtime/src/client.ts
|
|
5607
5906
|
function notFound2(params) {
|
|
5608
5907
|
const { model, method } = params;
|
|
5609
|
-
return new
|
|
5908
|
+
return new VibeError11({
|
|
5610
5909
|
code: "VIBE_NOT_FOUND",
|
|
5611
5910
|
message: `no ${model} record matched the where clause of ${method}()`,
|
|
5612
5911
|
meta: { model, method }
|
|
@@ -5614,7 +5913,7 @@ function notFound2(params) {
|
|
|
5614
5913
|
}
|
|
5615
5914
|
function viewIsReadOnly(params) {
|
|
5616
5915
|
const { model, method } = params;
|
|
5617
|
-
return new
|
|
5916
|
+
return new VibeError11({
|
|
5618
5917
|
code: "VIBE_VALIDATION",
|
|
5619
5918
|
message: `${model} is a view \u2014 ${method}() is not available on views (they are read-only); write to the underlying table instead`,
|
|
5620
5919
|
meta: { model, method }
|
|
@@ -5743,7 +6042,7 @@ class QueryPromise extends Promise {
|
|
|
5743
6042
|
}
|
|
5744
6043
|
claim() {
|
|
5745
6044
|
if (this.started) {
|
|
5746
|
-
throw new
|
|
6045
|
+
throw new VibeError11({
|
|
5747
6046
|
code: "VIBE_VALIDATION",
|
|
5748
6047
|
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)"
|
|
5749
6048
|
});
|
|
@@ -5790,7 +6089,7 @@ async function runAffected(params) {
|
|
|
5790
6089
|
if (plan.batch !== undefined && plan.batch.length > 1) {
|
|
5791
6090
|
const batch = plan.batch;
|
|
5792
6091
|
return await engine.adapter.transaction(async (txAdapter) => {
|
|
5793
|
-
const txEngine = { ...engine, adapter: txAdapter };
|
|
6092
|
+
const txEngine = { ...engine, adapter: txAdapter, inTransaction: true };
|
|
5794
6093
|
let total = 0;
|
|
5795
6094
|
for (const statement of batch) {
|
|
5796
6095
|
total += await runAffected({ engine: txEngine, plan: { ...plan, statement, batch: undefined } });
|
|
@@ -5820,6 +6119,13 @@ async function runAffected(params) {
|
|
|
5820
6119
|
}
|
|
5821
6120
|
function plan(params) {
|
|
5822
6121
|
const { engine, model, method, args } = params;
|
|
6122
|
+
if (args !== undefined && args["lock"] !== undefined && engine.inTransaction !== true) {
|
|
6123
|
+
throw new VibeError11({
|
|
6124
|
+
code: "VIBE_VALIDATION",
|
|
6125
|
+
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)`,
|
|
6126
|
+
meta: { model, method }
|
|
6127
|
+
});
|
|
6128
|
+
}
|
|
5823
6129
|
return buildQuery({
|
|
5824
6130
|
meta: engine.meta,
|
|
5825
6131
|
dialect: engine.dialect,
|
|
@@ -5887,7 +6193,7 @@ async function insertedKeyWhere(params) {
|
|
|
5887
6193
|
});
|
|
5888
6194
|
const id = result.rows[0]?.id;
|
|
5889
6195
|
if (id === undefined || id === null) {
|
|
5890
|
-
throw new
|
|
6196
|
+
throw new VibeError11({
|
|
5891
6197
|
code: "VIBE_INTERNAL",
|
|
5892
6198
|
message: `could not read back the generated key for ${source.model.name}`,
|
|
5893
6199
|
meta: { model: source.model.name }
|
|
@@ -5902,8 +6208,11 @@ async function runSingleMutation(params) {
|
|
|
5902
6208
|
if (requested !== undefined && requested.length > 0 && engine.dialect.capabilities.returning) {
|
|
5903
6209
|
const rows = await runRows({ engine, plan: source });
|
|
5904
6210
|
const row = rows[0];
|
|
5905
|
-
if (row === undefined)
|
|
6211
|
+
if (row === undefined) {
|
|
6212
|
+
if (params.allowZeroRows === true)
|
|
6213
|
+
return null;
|
|
5906
6214
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
6215
|
+
}
|
|
5907
6216
|
return row;
|
|
5908
6217
|
}
|
|
5909
6218
|
const rendered = renderStatementCached({ dialect: engine.dialect, statement, cache: engine.renderCache });
|
|
@@ -5928,7 +6237,7 @@ async function runSingleMutation(params) {
|
|
|
5928
6237
|
if (where === null)
|
|
5929
6238
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
5930
6239
|
return await engine.adapter.transaction(async (txAdapter) => {
|
|
5931
|
-
const txEngine = { ...engine, adapter: txAdapter };
|
|
6240
|
+
const txEngine = { ...engine, adapter: txAdapter, inTransaction: true };
|
|
5932
6241
|
const before = await refetch({ engine: txEngine, source, where, lock: true });
|
|
5933
6242
|
if (before === null)
|
|
5934
6243
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
@@ -5942,7 +6251,7 @@ async function runSingleMutation(params) {
|
|
|
5942
6251
|
const knownWhere = source.refetchWhere;
|
|
5943
6252
|
if (source.method === "upsert") {
|
|
5944
6253
|
return await engine.adapter.transaction(async (txAdapter) => {
|
|
5945
|
-
const txEngine = { ...engine, adapter: txAdapter };
|
|
6254
|
+
const txEngine = { ...engine, adapter: txAdapter, inTransaction: true };
|
|
5946
6255
|
await executeWrite(txEngine);
|
|
5947
6256
|
const byWhere = await refetch({ engine: txEngine, source, where: knownWhere });
|
|
5948
6257
|
if (byWhere !== null)
|
|
@@ -5966,9 +6275,9 @@ async function runSingleMutation(params) {
|
|
|
5966
6275
|
}
|
|
5967
6276
|
if (source.method === "update") {
|
|
5968
6277
|
return await engine.adapter.transaction(async (txAdapter) => {
|
|
5969
|
-
const txEngine = { ...engine, adapter: txAdapter };
|
|
5970
|
-
const
|
|
5971
|
-
if (
|
|
6278
|
+
const txEngine = { ...engine, adapter: txAdapter, inTransaction: true };
|
|
6279
|
+
const affected2 = await executeWrite(txEngine);
|
|
6280
|
+
if (affected2 === 0)
|
|
5972
6281
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
5973
6282
|
const after2 = await refetch({ engine: txEngine, source, where: knownWhere });
|
|
5974
6283
|
if (after2 === null)
|
|
@@ -5976,15 +6285,19 @@ async function runSingleMutation(params) {
|
|
|
5976
6285
|
return after2;
|
|
5977
6286
|
});
|
|
5978
6287
|
}
|
|
5979
|
-
await executeWrite(engine);
|
|
6288
|
+
const affected = await executeWrite(engine);
|
|
6289
|
+
if (affected === 0 && params.allowZeroRows === true)
|
|
6290
|
+
return null;
|
|
5980
6291
|
const after = await refetch({ engine, source, where: knownWhere });
|
|
5981
6292
|
if (after === null)
|
|
5982
6293
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
5983
6294
|
return after;
|
|
5984
6295
|
}
|
|
5985
6296
|
return await engine.adapter.transaction(async (txAdapter) => {
|
|
5986
|
-
const txEngine = { ...engine, adapter: txAdapter };
|
|
5987
|
-
await executeWrite(txEngine);
|
|
6297
|
+
const txEngine = { ...engine, adapter: txAdapter, inTransaction: true };
|
|
6298
|
+
const affected = await executeWrite(txEngine);
|
|
6299
|
+
if (affected === 0 && params.allowZeroRows === true)
|
|
6300
|
+
return null;
|
|
5988
6301
|
const where = await insertedKeyWhere({ engine: txEngine, source });
|
|
5989
6302
|
const after = await refetch({ engine: txEngine, source, where });
|
|
5990
6303
|
if (after === null)
|
|
@@ -5992,6 +6305,11 @@ async function runSingleMutation(params) {
|
|
|
5992
6305
|
return after;
|
|
5993
6306
|
});
|
|
5994
6307
|
}
|
|
6308
|
+
function requireRow(params) {
|
|
6309
|
+
if (params.row === null)
|
|
6310
|
+
throw notFound2({ model: params.model, method: params.method });
|
|
6311
|
+
return params.row;
|
|
6312
|
+
}
|
|
5995
6313
|
async function runUpsertFallback(params) {
|
|
5996
6314
|
const { engine, model, args } = params;
|
|
5997
6315
|
const passthrough = {};
|
|
@@ -6019,10 +6337,14 @@ async function runUpsertFallback(params) {
|
|
|
6019
6337
|
args: { ...passthrough, where: args.where, data: args.update }
|
|
6020
6338
|
});
|
|
6021
6339
|
return await engine.adapter.transaction(async (txAdapter) => {
|
|
6022
|
-
const txEngine = { ...engine, adapter: txAdapter };
|
|
6340
|
+
const txEngine = { ...engine, adapter: txAdapter, inTransaction: true };
|
|
6023
6341
|
const existing = (await runRows({ engine: txEngine, plan: probePlan }))[0];
|
|
6024
6342
|
const source = existing === undefined ? createSource : updateSource;
|
|
6025
|
-
return
|
|
6343
|
+
return requireRow({
|
|
6344
|
+
row: await runSingleMutation({ engine: txEngine, source, readBeforeWrite: false }),
|
|
6345
|
+
model: model.name,
|
|
6346
|
+
method: "upsert"
|
|
6347
|
+
});
|
|
6026
6348
|
});
|
|
6027
6349
|
}
|
|
6028
6350
|
function relationExecutor(params) {
|
|
@@ -6079,10 +6401,14 @@ async function finalizeRow(params) {
|
|
|
6079
6401
|
function makeRunner(params) {
|
|
6080
6402
|
const { engine } = params;
|
|
6081
6403
|
return {
|
|
6082
|
-
single: async ({ model, method, args }) =>
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6404
|
+
single: async ({ model, method, args }) => requireRow({
|
|
6405
|
+
row: await runSingleMutation({
|
|
6406
|
+
engine,
|
|
6407
|
+
source: plan({ engine, model, method, args }),
|
|
6408
|
+
readBeforeWrite: method === "delete"
|
|
6409
|
+
}),
|
|
6410
|
+
model,
|
|
6411
|
+
method
|
|
6086
6412
|
}),
|
|
6087
6413
|
rows: async ({ model, method, args }) => await runRows({ engine, plan: plan({ engine, model, method, args }) }),
|
|
6088
6414
|
affected: async ({ model, method, args }) => await runAffected({ engine, plan: plan({ engine, model, method, args }) }),
|
|
@@ -6108,7 +6434,7 @@ function makeWriteContext(params) {
|
|
|
6108
6434
|
meta: engine.meta,
|
|
6109
6435
|
dialect: engine.dialect,
|
|
6110
6436
|
run: makeRunner({ engine }),
|
|
6111
|
-
transactional: (fn) => engine.adapter.transaction((txAdapter) => fn(makeRunner({ engine: { ...engine, adapter: txAdapter } })))
|
|
6437
|
+
transactional: (fn) => engine.adapter.transaction((txAdapter) => fn(makeRunner({ engine: { ...engine, adapter: txAdapter, inTransaction: true } })))
|
|
6112
6438
|
};
|
|
6113
6439
|
}
|
|
6114
6440
|
function readOnlyDelegate(params) {
|
|
@@ -6166,7 +6492,15 @@ function createDelegate(params) {
|
|
|
6166
6492
|
};
|
|
6167
6493
|
const create = async (target, args) => {
|
|
6168
6494
|
const modelMeta = getModelMeta({ meta: target.meta, model });
|
|
6495
|
+
const skipping = args.onConflict !== undefined;
|
|
6169
6496
|
if (hasNestedWrites({ model: modelMeta, data: args.data })) {
|
|
6497
|
+
if (skipping) {
|
|
6498
|
+
throw new VibeError11({
|
|
6499
|
+
code: "VIBE_VALIDATION",
|
|
6500
|
+
message: `onConflict cannot combine with nested writes on ${modelMeta.name}.create() \u2014 insert the parent row alone, then connect relations`,
|
|
6501
|
+
meta: { model: modelMeta.name }
|
|
6502
|
+
});
|
|
6503
|
+
}
|
|
6170
6504
|
const shape = resolveResultShape({ meta: target.meta, model: modelMeta, method: "create", args });
|
|
6171
6505
|
const row2 = await createWithNested({
|
|
6172
6506
|
ctx: makeWriteContext({ engine: target }),
|
|
@@ -6181,7 +6515,14 @@ function createDelegate(params) {
|
|
|
6181
6515
|
});
|
|
6182
6516
|
}
|
|
6183
6517
|
const built = plan({ engine: target, model, method: "create", args });
|
|
6184
|
-
const row = await runSingleMutation({
|
|
6518
|
+
const row = await runSingleMutation({
|
|
6519
|
+
engine: target,
|
|
6520
|
+
source: built,
|
|
6521
|
+
readBeforeWrite: false,
|
|
6522
|
+
...skipping ? { allowZeroRows: true } : {}
|
|
6523
|
+
});
|
|
6524
|
+
if (row === null)
|
|
6525
|
+
return null;
|
|
6185
6526
|
return await finalizeRow({ engine: target, model: built.model, shape: shapeOf(built), row });
|
|
6186
6527
|
};
|
|
6187
6528
|
const update = async (target, args) => {
|
|
@@ -6202,7 +6543,11 @@ function createDelegate(params) {
|
|
|
6202
6543
|
});
|
|
6203
6544
|
}
|
|
6204
6545
|
const built = plan({ engine: target, model, method: "update", args });
|
|
6205
|
-
const row =
|
|
6546
|
+
const row = requireRow({
|
|
6547
|
+
row: await runSingleMutation({ engine: target, source: built, readBeforeWrite: false }),
|
|
6548
|
+
model: built.model.name,
|
|
6549
|
+
method: "update"
|
|
6550
|
+
});
|
|
6206
6551
|
return await finalizeRow({ engine: target, model: built.model, shape: shapeOf(built), row });
|
|
6207
6552
|
};
|
|
6208
6553
|
const createMany = async (target, args) => {
|
|
@@ -6234,13 +6579,20 @@ function createDelegate(params) {
|
|
|
6234
6579
|
const upsert = async (target, args) => {
|
|
6235
6580
|
const modelMeta = getModelMeta({ meta: target.meta, model });
|
|
6236
6581
|
if (hasNestedWrites({ model: modelMeta, data: args.create }) || hasNestedWrites({ model: modelMeta, data: args.update })) {
|
|
6237
|
-
throw new
|
|
6582
|
+
throw new VibeError11({
|
|
6238
6583
|
code: "VIBE_VALIDATION",
|
|
6239
6584
|
message: `nested writes are not supported inside upsert arms on ${modelMeta.name} \u2014 use create()/update() with nested data`,
|
|
6240
6585
|
meta: { model: modelMeta.name }
|
|
6241
6586
|
});
|
|
6242
6587
|
}
|
|
6243
6588
|
if (!upsertUsesNativeStatement({ model: modelMeta, args })) {
|
|
6589
|
+
if (args.onConflict !== undefined) {
|
|
6590
|
+
throw new VibeError11({
|
|
6591
|
+
code: "VIBE_VALIDATION",
|
|
6592
|
+
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`,
|
|
6593
|
+
meta: { model: modelMeta.name }
|
|
6594
|
+
});
|
|
6595
|
+
}
|
|
6244
6596
|
const shape = resolveResultShape({
|
|
6245
6597
|
meta: target.meta,
|
|
6246
6598
|
model: modelMeta,
|
|
@@ -6256,12 +6608,20 @@ function createDelegate(params) {
|
|
|
6256
6608
|
});
|
|
6257
6609
|
}
|
|
6258
6610
|
const built = plan({ engine: target, model, method: "upsert", args });
|
|
6259
|
-
const row =
|
|
6611
|
+
const row = requireRow({
|
|
6612
|
+
row: await runSingleMutation({ engine: target, source: built, readBeforeWrite: false }),
|
|
6613
|
+
model: built.model.name,
|
|
6614
|
+
method: "upsert"
|
|
6615
|
+
});
|
|
6260
6616
|
return await finalizeRow({ engine: target, model: built.model, shape: shapeOf(built), row });
|
|
6261
6617
|
};
|
|
6262
6618
|
const deleteOne = async (target, args) => {
|
|
6263
6619
|
const built = plan({ engine: target, model, method: "delete", args });
|
|
6264
|
-
const row =
|
|
6620
|
+
const row = requireRow({
|
|
6621
|
+
row: await runSingleMutation({ engine: target, source: built, readBeforeWrite: true }),
|
|
6622
|
+
model: built.model.name,
|
|
6623
|
+
method: "delete"
|
|
6624
|
+
});
|
|
6265
6625
|
return await finalizeRow({ engine: target, model: built.model, shape: shapeOf(built), row });
|
|
6266
6626
|
};
|
|
6267
6627
|
const deleteMany = async (target, args) => {
|
|
@@ -6389,7 +6749,7 @@ function buildClient(params) {
|
|
|
6389
6749
|
let claimError = null;
|
|
6390
6750
|
for (const operation of operations) {
|
|
6391
6751
|
if (!(operation instanceof QueryPromise)) {
|
|
6392
|
-
claimError = new
|
|
6752
|
+
claimError = new VibeError11({
|
|
6393
6753
|
code: "VIBE_VALIDATION",
|
|
6394
6754
|
message: "$transaction([...]) takes query promises from model delegate calls \u2014 a foreign promise is already running and cannot join the transaction"
|
|
6395
6755
|
});
|
|
@@ -6408,7 +6768,7 @@ function buildClient(params) {
|
|
|
6408
6768
|
return Promise.reject(claimError);
|
|
6409
6769
|
}
|
|
6410
6770
|
return engine.adapter.transaction(async (txAdapter) => {
|
|
6411
|
-
const txEngine = { ...engine, adapter: txAdapter };
|
|
6771
|
+
const txEngine = { ...engine, adapter: txAdapter, inTransaction: true };
|
|
6412
6772
|
const results = [];
|
|
6413
6773
|
let index = 0;
|
|
6414
6774
|
try {
|
|
@@ -6428,7 +6788,7 @@ function buildClient(params) {
|
|
|
6428
6788
|
$models: models,
|
|
6429
6789
|
$on: (event, listener) => {
|
|
6430
6790
|
if (event !== "query") {
|
|
6431
|
-
throw new
|
|
6791
|
+
throw new VibeError11({
|
|
6432
6792
|
code: "VIBE_VALIDATION",
|
|
6433
6793
|
message: `unknown client event "${String(event)}" \u2014 supported: "query"`,
|
|
6434
6794
|
meta: { event }
|
|
@@ -6439,7 +6799,7 @@ function buildClient(params) {
|
|
|
6439
6799
|
engine.listeners.delete(listener);
|
|
6440
6800
|
};
|
|
6441
6801
|
},
|
|
6442
|
-
$transaction: (fnOrOperations, options) => Array.isArray(fnOrOperations) ? runOperations(fnOrOperations, options) : engine.adapter.transaction((txAdapter) => fnOrOperations(buildClient({ engine: { ...engine, adapter: txAdapter } })), options),
|
|
6802
|
+
$transaction: (fnOrOperations, options) => Array.isArray(fnOrOperations) ? runOperations(fnOrOperations, options) : engine.adapter.transaction((txAdapter) => fnOrOperations(buildClient({ engine: { ...engine, adapter: txAdapter, inTransaction: true } })), options),
|
|
6443
6803
|
$queryRaw: async (strings, ...values) => (await runRaw("$queryRaw", buildRawStatement({ dialect: engine.dialect, strings, values }))).rows,
|
|
6444
6804
|
$executeRaw: async (strings, ...values) => (await runRaw("$executeRaw", buildRawStatement({ dialect: engine.dialect, strings, values }))).affectedRows,
|
|
6445
6805
|
$queryRawUnsafe: async (text, ...values) => (await runRaw("$queryRawUnsafe", { text, values })).rows,
|
|
@@ -6491,7 +6851,7 @@ function makeMountContext(params) {
|
|
|
6491
6851
|
compileWhere: ({ where, model: modelName, qualify }) => {
|
|
6492
6852
|
const target = modelName === undefined ? model : getModelMeta({ meta: engine.meta, model: modelName });
|
|
6493
6853
|
if (target === null) {
|
|
6494
|
-
throw new
|
|
6854
|
+
throw new VibeError11({
|
|
6495
6855
|
code: "VIBE_INTERNAL",
|
|
6496
6856
|
message: `extension "${extension}" compiled a where with no model in scope`,
|
|
6497
6857
|
meta: { extension }
|
|
@@ -6517,7 +6877,7 @@ function attachExtensionMounts(params) {
|
|
|
6517
6877
|
const model = getModelMeta({ meta: engine.meta, model: mount.model ?? "" });
|
|
6518
6878
|
const delegate = models[model.name];
|
|
6519
6879
|
if (delegate === undefined || mount.property in delegate) {
|
|
6520
|
-
throw new
|
|
6880
|
+
throw new VibeError11({
|
|
6521
6881
|
code: "VIBE_CONFIG",
|
|
6522
6882
|
message: `extension "${mount.extension}" cannot mount "${mount.property}" on ${model.name} \u2014 the name is taken`,
|
|
6523
6883
|
meta: { extension: mount.extension, model: model.name, property: mount.property }
|
|
@@ -6528,7 +6888,7 @@ function attachExtensionMounts(params) {
|
|
|
6528
6888
|
continue;
|
|
6529
6889
|
}
|
|
6530
6890
|
if (!mount.property.startsWith("$") || mount.property in client) {
|
|
6531
|
-
throw new
|
|
6891
|
+
throw new VibeError11({
|
|
6532
6892
|
code: "VIBE_CONFIG",
|
|
6533
6893
|
message: `extension "${mount.extension}" cannot mount client property "${mount.property}" \u2014 root mounts are $-prefixed and must not collide`,
|
|
6534
6894
|
meta: { extension: mount.extension, property: mount.property }
|
|
@@ -6548,7 +6908,7 @@ function createClient(params) {
|
|
|
6548
6908
|
assertComputedManifest({ manifest: computed.manifest, specs: computed.specs });
|
|
6549
6909
|
}
|
|
6550
6910
|
if (computed === undefined && options !== undefined && options["computed"] !== undefined) {
|
|
6551
|
-
throw new
|
|
6911
|
+
throw new VibeError11({
|
|
6552
6912
|
code: "VIBE_CONFIG",
|
|
6553
6913
|
message: "this client was generated without computed fields \u2014 declare them in vibeorm.config.ts and re-run vibeorm generate",
|
|
6554
6914
|
meta: {}
|
|
@@ -6583,6 +6943,7 @@ export {
|
|
|
6583
6943
|
resolveRelationSelection,
|
|
6584
6944
|
resolveRelationLink,
|
|
6585
6945
|
lateralColumnAlias,
|
|
6946
|
+
isDbNow,
|
|
6586
6947
|
hasNestedWrites,
|
|
6587
6948
|
getModelMeta,
|
|
6588
6949
|
getFieldMeta,
|
|
@@ -6598,6 +6959,7 @@ export {
|
|
|
6598
6959
|
decodeRow,
|
|
6599
6960
|
decodeIsNoop2 as decodeIsNoop,
|
|
6600
6961
|
decodeAggregateValue,
|
|
6962
|
+
dbNow,
|
|
6601
6963
|
createWithNested,
|
|
6602
6964
|
createView,
|
|
6603
6965
|
createClient,
|
|
@@ -6622,4 +6984,4 @@ export {
|
|
|
6622
6984
|
COUNT_ALIAS
|
|
6623
6985
|
};
|
|
6624
6986
|
|
|
6625
|
-
//# debugId=
|
|
6987
|
+
//# debugId=DDDCD7D2210268EA64756E2164756E21
|