@prisma/client-engine-runtime 6.9.0-dev.3 → 6.9.0-dev.30

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
@@ -30,26 +30,253 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ DataMapperError: () => DataMapperError,
33
34
  QueryInterpreter: () => QueryInterpreter,
34
35
  TransactionManager: () => TransactionManager,
35
36
  TransactionManagerError: () => TransactionManagerError,
36
37
  UserFacingError: () => UserFacingError,
38
+ doKeysMatch: () => doKeysMatch,
39
+ isDeepStrictEqual: () => isDeepStrictEqual,
40
+ isPrismaValueBigInt: () => isPrismaValueBigInt,
37
41
  isPrismaValueBytes: () => isPrismaValueBytes,
38
42
  isPrismaValueGenerator: () => isPrismaValueGenerator,
39
43
  isPrismaValuePlaceholder: () => isPrismaValuePlaceholder,
40
- noopTracingHelper: () => noopTracingHelper
44
+ noopTracingHelper: () => noopTracingHelper,
45
+ safeJsonStringify: () => safeJsonStringify
41
46
  });
42
47
  module.exports = __toCommonJS(index_exports);
43
48
 
44
- // src/interpreter/QueryInterpreter.ts
45
- var import_api = require("@opentelemetry/api");
49
+ // src/interpreter/DataMapper.ts
50
+ var import_decimal2 = __toESM(require("decimal.js"));
46
51
 
47
52
  // src/utils.ts
53
+ var import_decimal = __toESM(require("decimal.js"));
48
54
  function assertNever(_, message) {
49
55
  throw new Error(message);
50
56
  }
57
+ function isDeepStrictEqual(a, b) {
58
+ return a === b || a !== null && b !== null && typeof a === "object" && typeof b === "object" && Object.keys(a).length === Object.keys(b).length && Object.keys(a).every((key) => isDeepStrictEqual(a[key], b[key]));
59
+ }
60
+ function doKeysMatch(lhs, rhs) {
61
+ const lhsKeys = Object.keys(lhs);
62
+ const rhsKeys = Object.keys(rhs);
63
+ const smallerKeyList = lhsKeys.length < rhsKeys.length ? lhsKeys : rhsKeys;
64
+ return smallerKeyList.every((key) => {
65
+ if (typeof lhs[key] !== typeof rhs[key]) {
66
+ if (typeof lhs[key] === "number" || typeof rhs[key] === "number") {
67
+ return `${lhs[key]}` === `${rhs[key]}`;
68
+ } else if (typeof lhs[key] === "bigint" || typeof rhs[key] === "bigint") {
69
+ return BigInt(`${lhs[key]}`.replace(/n$/, "")) === BigInt(`${rhs[key]}`.replace(/n$/, ""));
70
+ } else if (lhs[key] instanceof Date || rhs[key] instanceof Date) {
71
+ return (/* @__PURE__ */ new Date(`${lhs[key]}`)).getTime() === (/* @__PURE__ */ new Date(`${rhs[key]}`)).getTime();
72
+ } else if (import_decimal.default.isDecimal(lhs[key]) || import_decimal.default.isDecimal(rhs[key])) {
73
+ return new import_decimal.default(`${lhs[key]}`).equals(new import_decimal.default(`${rhs[key]}`));
74
+ }
75
+ }
76
+ return isDeepStrictEqual(lhs[key], rhs[key]);
77
+ });
78
+ }
79
+ function safeJsonStringify(obj) {
80
+ return JSON.stringify(obj, (_key, val) => {
81
+ if (typeof val === "bigint") {
82
+ return val.toString();
83
+ } else if (val instanceof Uint8Array) {
84
+ return Buffer.from(val).toString("base64");
85
+ }
86
+ return val;
87
+ });
88
+ }
89
+
90
+ // src/interpreter/DataMapper.ts
91
+ var DataMapperError = class extends Error {
92
+ name = "DataMapperError";
93
+ };
94
+ function applyDataMap(data, structure) {
95
+ switch (structure.type) {
96
+ case "Object":
97
+ return mapArrayOrObject(data, structure.fields);
98
+ case "Value":
99
+ return mapValue(data, "<result>", structure.resultType);
100
+ default:
101
+ assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
102
+ }
103
+ }
104
+ function mapArrayOrObject(data, fields) {
105
+ if (data === null) return null;
106
+ if (Array.isArray(data)) {
107
+ const rows = data;
108
+ return rows.map((row) => mapObject(row, fields));
109
+ }
110
+ if (typeof data === "object") {
111
+ const row = data;
112
+ return mapObject(row, fields);
113
+ }
114
+ if (typeof data === "string") {
115
+ let decodedData;
116
+ try {
117
+ decodedData = JSON.parse(data);
118
+ } catch (error) {
119
+ throw new DataMapperError(`Expected an array or object, got a string that is not valid JSON`, {
120
+ cause: error
121
+ });
122
+ }
123
+ return mapArrayOrObject(decodedData, fields);
124
+ }
125
+ throw new DataMapperError(`Expected an array or an object, got: ${typeof data}`);
126
+ }
127
+ function mapObject(data, fields) {
128
+ if (typeof data !== "object") {
129
+ throw new DataMapperError(`Expected an object, but got '${typeof data}'`);
130
+ }
131
+ const result = {};
132
+ for (const [name, node] of Object.entries(fields)) {
133
+ switch (node.type) {
134
+ case "Object": {
135
+ if (!node.flattened && !Object.hasOwn(data, name)) {
136
+ throw new DataMapperError(
137
+ `Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
138
+ );
139
+ }
140
+ const target = node.flattened ? data : data[name];
141
+ result[name] = mapArrayOrObject(target, node.fields);
142
+ break;
143
+ }
144
+ case "Value":
145
+ {
146
+ const dbName = node.dbName;
147
+ if (Object.hasOwn(data, dbName)) {
148
+ result[name] = mapValue(data[dbName], dbName, node.resultType);
149
+ } else {
150
+ throw new DataMapperError(
151
+ `Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
152
+ );
153
+ }
154
+ }
155
+ break;
156
+ default:
157
+ assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
158
+ }
159
+ }
160
+ return result;
161
+ }
162
+ function mapValue(value, columnName, resultType) {
163
+ if (value === null) return null;
164
+ switch (resultType.type) {
165
+ case "Any":
166
+ return value;
167
+ case "String": {
168
+ if (typeof value !== "string") {
169
+ throw new DataMapperError(`Expected a string in column '${columnName}', got ${typeof value}: ${value}`);
170
+ }
171
+ return value;
172
+ }
173
+ case "Int": {
174
+ switch (typeof value) {
175
+ case "number": {
176
+ return Math.trunc(value);
177
+ }
178
+ case "string": {
179
+ const numberValue = Math.trunc(Number(value));
180
+ if (Number.isNaN(numberValue) || !Number.isFinite(numberValue)) {
181
+ throw new DataMapperError(`Expected an integer in column '${columnName}', got string: ${value}`);
182
+ }
183
+ if (!Number.isSafeInteger(numberValue)) {
184
+ throw new DataMapperError(
185
+ `Integer value in column '${columnName}' is too large to represent as a JavaScript number without loss of precision, got: ${value}. Consider using BigInt type.`
186
+ );
187
+ }
188
+ return numberValue;
189
+ }
190
+ default:
191
+ throw new DataMapperError(`Expected an integer in column '${columnName}', got ${typeof value}: ${value}`);
192
+ }
193
+ }
194
+ case "BigInt": {
195
+ if (typeof value !== "number" && typeof value !== "string") {
196
+ throw new DataMapperError(`Expected a bigint in column '${columnName}', got ${typeof value}: ${value}`);
197
+ }
198
+ return { $type: "BigInt", value };
199
+ }
200
+ case "Float": {
201
+ if (typeof value === "number") return value;
202
+ if (typeof value === "string") {
203
+ const parsedValue = Number(value);
204
+ if (Number.isNaN(parsedValue) && !/^[-+]?nan$/.test(value.toLowerCase())) {
205
+ throw new DataMapperError(`Expected a float in column '${columnName}', got string: ${value}`);
206
+ }
207
+ return parsedValue;
208
+ }
209
+ throw new DataMapperError(`Expected a float in column '${columnName}', got ${typeof value}: ${value}`);
210
+ }
211
+ case "Boolean": {
212
+ if (typeof value === "boolean") return value;
213
+ if (typeof value === "number") return value === 1;
214
+ if (typeof value === "string") {
215
+ if (value === "true" || value === "TRUE" || value === "1") {
216
+ return true;
217
+ } else if (value === "false" || value === "FALSE" || value === "0") {
218
+ return false;
219
+ } else {
220
+ throw new DataMapperError(`Expected a boolean in column '${columnName}', got ${typeof value}: ${value}`);
221
+ }
222
+ }
223
+ throw new DataMapperError(`Expected a boolean in column '${columnName}', got ${typeof value}: ${value}`);
224
+ }
225
+ case "Decimal":
226
+ if (typeof value !== "number" && typeof value !== "string" && !import_decimal2.default.isDecimal(value)) {
227
+ throw new DataMapperError(`Expected a decimal in column '${columnName}', got ${typeof value}: ${value}`);
228
+ }
229
+ return { $type: "Decimal", value };
230
+ case "Date": {
231
+ if (typeof value === "string") {
232
+ return { $type: "DateTime", value: ensureTimezoneInIsoString(value) };
233
+ }
234
+ if (typeof value === "number" || value instanceof Date) {
235
+ return { $type: "DateTime", value };
236
+ }
237
+ throw new DataMapperError(`Expected a date in column '${columnName}', got ${typeof value}: ${value}`);
238
+ }
239
+ case "Time": {
240
+ if (typeof value === "string") {
241
+ return { $type: "DateTime", value: `1970-01-01T${ensureTimezoneInIsoString(value)}` };
242
+ }
243
+ throw new DataMapperError(`Expected a time in column '${columnName}', got ${typeof value}: ${value}`);
244
+ }
245
+ case "Array": {
246
+ const values = value;
247
+ return values.map((v, i) => mapValue(v, `${columnName}[${i}]`, resultType.inner));
248
+ }
249
+ case "Object": {
250
+ const jsonValue = typeof value === "string" ? value : safeJsonStringify(value);
251
+ return { $type: "Json", value: jsonValue };
252
+ }
253
+ case "Bytes": {
254
+ if (typeof value === "string" && value.startsWith("\\x")) {
255
+ return { $type: "Bytes", value: Buffer.from(value.slice(2), "hex").toString("base64") };
256
+ }
257
+ if (Array.isArray(value)) {
258
+ return { $type: "Bytes", value: Buffer.from(value).toString("base64") };
259
+ }
260
+ throw new DataMapperError(`Expected a byte array in column '${columnName}', got ${typeof value}: ${value}`);
261
+ }
262
+ default:
263
+ assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
264
+ }
265
+ }
266
+ var TIMEZONE_PATTERN = /Z$|(?<!\d{4}-\d{2})[+-]\d{2}(:?\d{2})?$/;
267
+ function ensureTimezoneInIsoString(dt) {
268
+ const results = TIMEZONE_PATTERN.exec(dt);
269
+ if (results === null) {
270
+ return `${dt}Z`;
271
+ } else if (results[0] !== "Z" && results[1] === void 0) {
272
+ return `${dt}:00`;
273
+ } else {
274
+ return dt;
275
+ }
276
+ }
51
277
 
52
278
  // src/tracing.ts
279
+ var import_api = require("@opentelemetry/api");
53
280
  var noopTracingHelper = {
54
281
  runInChildSpan(_, callback) {
55
282
  return callback();
@@ -67,6 +294,37 @@ function providerToOtelSystem(provider) {
67
294
  assertNever(provider, `Unknown provider: ${provider}`);
68
295
  }
69
296
  }
297
+ async function withQuerySpanAndEvent({
298
+ query,
299
+ queryable,
300
+ tracingHelper,
301
+ onQuery,
302
+ execute
303
+ }) {
304
+ return await tracingHelper.runInChildSpan(
305
+ {
306
+ name: "db_query",
307
+ kind: import_api.SpanKind.CLIENT,
308
+ attributes: {
309
+ "db.query.text": query.sql,
310
+ "db.system.name": providerToOtelSystem(queryable.provider)
311
+ }
312
+ },
313
+ async () => {
314
+ const timestamp = /* @__PURE__ */ new Date();
315
+ const startInstant = performance.now();
316
+ const result = await execute();
317
+ const endInstant = performance.now();
318
+ onQuery?.({
319
+ timestamp,
320
+ duration: endInstant - startInstant,
321
+ query: query.sql,
322
+ params: query.args
323
+ });
324
+ return result;
325
+ }
326
+ );
327
+ }
70
328
 
71
329
  // src/UserFacingError.ts
72
330
  var import_driver_adapter_utils = require("@prisma/driver-adapter-utils");
@@ -77,7 +335,7 @@ var UserFacingError = class extends Error {
77
335
  constructor(message, code, meta) {
78
336
  super(message);
79
337
  this.code = code;
80
- this.meta = meta;
338
+ this.meta = meta ?? {};
81
339
  }
82
340
  toQueryResponseErrorObject() {
83
341
  return {
@@ -100,7 +358,7 @@ function rethrowAsUserFacing(error) {
100
358
  if (!code || !message) {
101
359
  throw error;
102
360
  }
103
- throw new UserFacingError(message, code, error);
361
+ throw new UserFacingError(message, code, { driverAdapterError: error });
104
362
  }
105
363
  function getErrorCode(err) {
106
364
  switch (err.cause.kind) {
@@ -211,101 +469,6 @@ function renderConstraint(constraint) {
211
469
  return "(not available)";
212
470
  }
213
471
 
214
- // src/interpreter/DataMapper.ts
215
- var import_decimal = __toESM(require("decimal.js"));
216
- function applyDataMap(data, structure) {
217
- switch (structure.type) {
218
- case "Object":
219
- return mapArrayOrObject(data, structure.fields);
220
- case "Value":
221
- return mapValue(data, structure.resultType);
222
- default:
223
- assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
224
- }
225
- }
226
- function mapArrayOrObject(data, fields) {
227
- if (data === null) return null;
228
- if (Array.isArray(data)) {
229
- const rows = data;
230
- return rows.map((row) => mapObject(row, fields));
231
- }
232
- if (typeof data === "object") {
233
- const row = data;
234
- return mapObject(row, fields);
235
- }
236
- throw new Error(`DataMapper: Expected an array or an object, got: ${typeof data}`);
237
- }
238
- function mapObject(data, fields) {
239
- if (typeof data !== "object") {
240
- throw new Error(`DataMapper: Expected an object, but got '${typeof data}'`);
241
- }
242
- const result = {};
243
- for (const [name, node] of Object.entries(fields)) {
244
- switch (node.type) {
245
- case "Object": {
246
- if (!node.flattened && !Object.hasOwn(data, name)) {
247
- throw new Error(
248
- `DataMapper: Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
249
- );
250
- }
251
- const target = node.flattened ? data : data[name];
252
- result[name] = mapArrayOrObject(target, node.fields);
253
- break;
254
- }
255
- case "Value":
256
- {
257
- const dbName = node.dbName;
258
- if (Object.hasOwn(data, dbName)) {
259
- result[name] = mapValue(data[dbName], node.resultType);
260
- } else {
261
- throw new Error(
262
- `DataMapper: Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
263
- );
264
- }
265
- }
266
- break;
267
- default:
268
- assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
269
- }
270
- }
271
- return result;
272
- }
273
- function mapValue(value, resultType) {
274
- if (value === null) return null;
275
- switch (resultType.type) {
276
- case "Any":
277
- return value;
278
- case "String":
279
- return typeof value === "string" ? value : `${value}`;
280
- case "Int":
281
- return typeof value === "number" ? value : parseInt(`${value}`, 10);
282
- case "BigInt":
283
- return typeof value === "bigint" ? value : BigInt(`${value}`);
284
- case "Float":
285
- return typeof value === "number" ? value : parseFloat(`${value}`);
286
- case "Boolean":
287
- return typeof value === "boolean" ? value : value !== "0";
288
- case "Decimal":
289
- return typeof value === "number" ? new import_decimal.default(value) : new import_decimal.default(`${value}`);
290
- case "Date":
291
- return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
292
- case "Array": {
293
- const values = value;
294
- return values.map((v) => mapValue(v, resultType.inner));
295
- }
296
- case "Object":
297
- return typeof value === "string" ? value : JSON.stringify(value);
298
- case "Bytes": {
299
- if (!Array.isArray(value)) {
300
- throw new Error(`DataMapper: Bytes data is invalid, got: ${typeof value}`);
301
- }
302
- return new Uint8Array(value);
303
- }
304
- default:
305
- assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
306
- }
307
- }
308
-
309
472
  // src/interpreter/generators.ts
310
473
  var import_cuid = __toESM(require("@bugsnag/cuid"));
311
474
  var import_cuid2 = require("@paralleldrive/cuid2");
@@ -410,6 +573,9 @@ function isPrismaValueGenerator(value) {
410
573
  function isPrismaValueBytes(value) {
411
574
  return typeof value === "object" && value !== null && value["prisma__type"] === "bytes";
412
575
  }
576
+ function isPrismaValueBigInt(value) {
577
+ return typeof value === "object" && value !== null && value["prisma__type"] === "bigint";
578
+ }
413
579
 
414
580
  // src/interpreter/renderQuery.ts
415
581
  function renderQuery(dbQuery, scope, generators) {
@@ -452,9 +618,10 @@ function evaluateParam(param, scope, generators) {
452
618
  }
453
619
  if (Array.isArray(value)) {
454
620
  value = value.map((el) => evaluateParam(el, scope, generators));
455
- }
456
- if (isPrismaValueBytes(value)) {
621
+ } else if (isPrismaValueBytes(value)) {
457
622
  value = Buffer.from(value.prisma__value, "base64");
623
+ } else if (isPrismaValueBigInt(value)) {
624
+ value = BigInt(value.prisma__value);
458
625
  }
459
626
  return value;
460
627
  }
@@ -548,6 +715,7 @@ function doesRequireEvaluation(param) {
548
715
  }
549
716
 
550
717
  // src/interpreter/serializeSql.ts
718
+ var import_driver_adapter_utils2 = require("@prisma/driver-adapter-utils");
551
719
  function serializeSql(resultSet) {
552
720
  return resultSet.rows.map(
553
721
  (row) => row.reduce((acc, value, index) => {
@@ -568,6 +736,96 @@ function serializeSql(resultSet) {
568
736
  }, {})
569
737
  );
570
738
  }
739
+ function serializeRawSql(resultSet) {
740
+ const types = resultSet.columnTypes.map((type) => serializeColumnType(type));
741
+ const mappers = types.map((type) => {
742
+ switch (type) {
743
+ case "int":
744
+ return (value) => value === null ? null : typeof value === "number" ? value : parseInt(`${value}`, 10);
745
+ case "bigint":
746
+ return (value) => value === null ? null : typeof value === "bigint" ? value : BigInt(`${value}`);
747
+ default:
748
+ return (value) => value;
749
+ }
750
+ });
751
+ return {
752
+ columns: resultSet.columnNames,
753
+ types: resultSet.columnTypes.map((type) => serializeColumnType(type)),
754
+ rows: resultSet.rows.map((row) => row.map((value, index) => mappers[index](value)))
755
+ };
756
+ }
757
+ function serializeColumnType(columnType) {
758
+ switch (columnType) {
759
+ case import_driver_adapter_utils2.ColumnTypeEnum.Int32:
760
+ return "int";
761
+ case import_driver_adapter_utils2.ColumnTypeEnum.Int64:
762
+ return "bigint";
763
+ case import_driver_adapter_utils2.ColumnTypeEnum.Float:
764
+ return "float";
765
+ case import_driver_adapter_utils2.ColumnTypeEnum.Double:
766
+ return "double";
767
+ case import_driver_adapter_utils2.ColumnTypeEnum.Text:
768
+ return "string";
769
+ case import_driver_adapter_utils2.ColumnTypeEnum.Enum:
770
+ return "enum";
771
+ case import_driver_adapter_utils2.ColumnTypeEnum.Bytes:
772
+ return "bytes";
773
+ case import_driver_adapter_utils2.ColumnTypeEnum.Boolean:
774
+ return "bool";
775
+ case import_driver_adapter_utils2.ColumnTypeEnum.Character:
776
+ return "char";
777
+ case import_driver_adapter_utils2.ColumnTypeEnum.Numeric:
778
+ return "decimal";
779
+ case import_driver_adapter_utils2.ColumnTypeEnum.Json:
780
+ return "json";
781
+ case import_driver_adapter_utils2.ColumnTypeEnum.Uuid:
782
+ return "uuid";
783
+ case import_driver_adapter_utils2.ColumnTypeEnum.DateTime:
784
+ return "datetime";
785
+ case import_driver_adapter_utils2.ColumnTypeEnum.Date:
786
+ return "date";
787
+ case import_driver_adapter_utils2.ColumnTypeEnum.Time:
788
+ return "time";
789
+ case import_driver_adapter_utils2.ColumnTypeEnum.Int32Array:
790
+ return "int-array";
791
+ case import_driver_adapter_utils2.ColumnTypeEnum.Int64Array:
792
+ return "bigint-array";
793
+ case import_driver_adapter_utils2.ColumnTypeEnum.FloatArray:
794
+ return "float-array";
795
+ case import_driver_adapter_utils2.ColumnTypeEnum.DoubleArray:
796
+ return "double-array";
797
+ case import_driver_adapter_utils2.ColumnTypeEnum.TextArray:
798
+ return "string-array";
799
+ case import_driver_adapter_utils2.ColumnTypeEnum.EnumArray:
800
+ return "string-array";
801
+ case import_driver_adapter_utils2.ColumnTypeEnum.BytesArray:
802
+ return "bytes-array";
803
+ case import_driver_adapter_utils2.ColumnTypeEnum.BooleanArray:
804
+ return "bool-array";
805
+ case import_driver_adapter_utils2.ColumnTypeEnum.CharacterArray:
806
+ return "char-array";
807
+ case import_driver_adapter_utils2.ColumnTypeEnum.NumericArray:
808
+ return "decimal-array";
809
+ case import_driver_adapter_utils2.ColumnTypeEnum.JsonArray:
810
+ return "json-array";
811
+ case import_driver_adapter_utils2.ColumnTypeEnum.UuidArray:
812
+ return "uuid-array";
813
+ case import_driver_adapter_utils2.ColumnTypeEnum.DateTimeArray:
814
+ return "datetime-array";
815
+ case import_driver_adapter_utils2.ColumnTypeEnum.DateArray:
816
+ return "date-array";
817
+ case import_driver_adapter_utils2.ColumnTypeEnum.TimeArray:
818
+ return "time-array";
819
+ case import_driver_adapter_utils2.ColumnTypeEnum.UnknownNumber:
820
+ return "unknown";
821
+ /// The following PlanetScale type IDs are mapped into Set:
822
+ /// - SET (SET) -> e.g. `"foo,bar"` (String-encoded, comma-separated)
823
+ case import_driver_adapter_utils2.ColumnTypeEnum.Set:
824
+ return "string";
825
+ default:
826
+ assertNever(columnType, `Unexpected column type: ${columnType}`);
827
+ }
828
+ }
571
829
 
572
830
  // src/interpreter/validation.ts
573
831
  function performValidation(data, rules, error) {
@@ -595,6 +853,8 @@ function doesSatisfyRule(data, rule) {
595
853
  return rule.args !== 0;
596
854
  }
597
855
  return rule.args !== 1;
856
+ case "affectedRowCountEq":
857
+ return data === rule.args;
598
858
  case "never":
599
859
  return false;
600
860
  default:
@@ -612,7 +872,9 @@ function renderMessage(data, error) {
612
872
  return `An operation failed because it depends on one or more records that were required but not found. No '${error.context.model}' record${hint} was found for ${error.context.operation} on ${error.context.relationType} relation '${error.context.relation}'.`;
613
873
  }
614
874
  case "INCOMPLETE_CONNECT_INPUT":
615
- return `An operation failed because it depends on one or more records that were required but not found. Expected ${error.context.expectedRows} records to be connected, found only ${Array.isArray(data) ? data.length : 0}.`;
875
+ return `An operation failed because it depends on one or more records that were required but not found. Expected ${error.context.expectedRows} records to be connected, found only ${Array.isArray(data) ? data.length : data}.`;
876
+ case "INCOMPLETE_CONNECT_OUTPUT":
877
+ return `The required connected records were not found. Expected ${error.context.expectedRows} records to be connected after connect operation on ${error.context.relationType} relation '${error.context.relation}', found ${Array.isArray(data) ? data.length : data}.`;
616
878
  case "RECORDS_NOT_CONNECTED":
617
879
  return `The records for relation \`${error.context.relation}\` between the \`${error.context.parent}\` and \`${error.context.child}\` models are not connected.`;
618
880
  default:
@@ -625,6 +887,8 @@ function getErrorCode2(error) {
625
887
  return "P2014";
626
888
  case "RECORDS_NOT_CONNECTED":
627
889
  return "P2017";
890
+ case "INCOMPLETE_CONNECT_OUTPUT":
891
+ return "P2018";
628
892
  case "MISSING_RECORD":
629
893
  case "MISSING_RELATED_RECORD":
630
894
  case "INCOMPLETE_CONNECT_INPUT":
@@ -642,12 +906,21 @@ var QueryInterpreter = class _QueryInterpreter {
642
906
  #generators = new GeneratorRegistry();
643
907
  #tracingHelper;
644
908
  #serializer;
645
- constructor({ transactionManager, placeholderValues, onQuery, tracingHelper, serializer }) {
909
+ #rawSerializer;
910
+ constructor({
911
+ transactionManager,
912
+ placeholderValues,
913
+ onQuery,
914
+ tracingHelper,
915
+ serializer,
916
+ rawSerializer
917
+ }) {
646
918
  this.#transactionManager = transactionManager;
647
919
  this.#placeholderValues = placeholderValues;
648
920
  this.#onQuery = onQuery;
649
921
  this.#tracingHelper = tracingHelper;
650
922
  this.#serializer = serializer;
923
+ this.#rawSerializer = rawSerializer ?? serializer;
651
924
  }
652
925
  static forSql(options) {
653
926
  return new _QueryInterpreter({
@@ -655,7 +928,8 @@ var QueryInterpreter = class _QueryInterpreter {
655
928
  placeholderValues: options.placeholderValues,
656
929
  onQuery: options.onQuery,
657
930
  tracingHelper: options.tracingHelper,
658
- serializer: serializeSql
931
+ serializer: serializeSql,
932
+ rawSerializer: serializeRawSql
659
933
  });
660
934
  }
661
935
  async run(queryPlan, queryable) {
@@ -666,8 +940,11 @@ var QueryInterpreter = class _QueryInterpreter {
666
940
  async interpretNode(node, queryable, scope, generators) {
667
941
  switch (node.type) {
668
942
  case "seq": {
669
- const results = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
670
- return results[results.length - 1];
943
+ let result;
944
+ for (const arg of node.args) {
945
+ result = await this.interpretNode(arg, queryable, scope, generators);
946
+ }
947
+ return result;
671
948
  }
672
949
  case "get": {
673
950
  return scope[node.args.name];
@@ -690,22 +967,26 @@ var QueryInterpreter = class _QueryInterpreter {
690
967
  }
691
968
  case "concat": {
692
969
  const parts = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
693
- return parts.reduce((acc, part) => acc.concat(asList(part)), []);
970
+ return parts.length > 0 ? parts.reduce((acc, part) => acc.concat(asList(part)), []) : [];
694
971
  }
695
972
  case "sum": {
696
973
  const parts = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
697
- return parts.reduce((acc, part) => asNumber(acc) + asNumber(part));
974
+ return parts.length > 0 ? parts.reduce((acc, part) => asNumber(acc) + asNumber(part)) : 0;
698
975
  }
699
976
  case "execute": {
700
977
  const query = renderQuery(node.args, scope, generators);
701
- return this.#withQueryEvent(query, queryable, async () => {
978
+ return this.#withQuerySpanAndEvent(query, queryable, async () => {
702
979
  return await queryable.executeRaw(query);
703
980
  });
704
981
  }
705
982
  case "query": {
706
983
  const query = renderQuery(node.args, scope, generators);
707
- return this.#withQueryEvent(query, queryable, async () => {
708
- return this.#serializer(await queryable.queryRaw(query));
984
+ return this.#withQuerySpanAndEvent(query, queryable, async () => {
985
+ if (node.args.type === "rawSql") {
986
+ return this.#rawSerializer(await queryable.queryRaw(query));
987
+ } else {
988
+ return this.#serializer(await queryable.queryRaw(query));
989
+ }
709
990
  });
710
991
  }
711
992
  case "reverse": {
@@ -735,6 +1016,9 @@ var QueryInterpreter = class _QueryInterpreter {
735
1016
  }
736
1017
  case "join": {
737
1018
  const parent = await this.interpretNode(node.args.parent, queryable, scope, generators);
1019
+ if (parent === null) {
1020
+ return null;
1021
+ }
738
1022
  const children = await Promise.all(
739
1023
  node.args.children.map(async (joinExpr) => ({
740
1024
  joinExpr,
@@ -791,34 +1075,50 @@ var QueryInterpreter = class _QueryInterpreter {
791
1075
  const toSet = new Set(asList(to));
792
1076
  return asList(from).filter((item) => !toSet.has(item));
793
1077
  }
1078
+ case "distinctBy": {
1079
+ const value = await this.interpretNode(node.args.expr, queryable, scope, generators);
1080
+ const seen = /* @__PURE__ */ new Set();
1081
+ const result = [];
1082
+ for (const item of asList(value)) {
1083
+ const key = getRecordKey(item, node.args.fields);
1084
+ if (!seen.has(key)) {
1085
+ seen.add(key);
1086
+ result.push(item);
1087
+ }
1088
+ }
1089
+ return result;
1090
+ }
1091
+ case "paginate": {
1092
+ const value = await this.interpretNode(node.args.expr, queryable, scope, generators);
1093
+ const list = asList(value);
1094
+ const linkingFields = node.args.pagination.linkingFields;
1095
+ if (linkingFields !== null) {
1096
+ const groupedByParent = /* @__PURE__ */ new Map();
1097
+ for (const item of list) {
1098
+ const parentKey = getRecordKey(item, linkingFields);
1099
+ if (!groupedByParent.has(parentKey)) {
1100
+ groupedByParent.set(parentKey, []);
1101
+ }
1102
+ groupedByParent.get(parentKey).push(item);
1103
+ }
1104
+ const groupList = Array.from(groupedByParent.entries());
1105
+ groupList.sort(([aId], [bId]) => aId < bId ? -1 : aId > bId ? 1 : 0);
1106
+ return groupList.flatMap(([, elems]) => paginate(elems, node.args.pagination));
1107
+ }
1108
+ return paginate(list, node.args.pagination);
1109
+ }
794
1110
  default:
795
1111
  assertNever(node, `Unexpected node type: ${node.type}`);
796
1112
  }
797
1113
  }
798
- #withQueryEvent(query, queryable, execute) {
799
- return this.#tracingHelper.runInChildSpan(
800
- {
801
- name: "db_query",
802
- kind: import_api.SpanKind.CLIENT,
803
- attributes: {
804
- "db.query.text": query.sql,
805
- "db.system.name": providerToOtelSystem(queryable.provider)
806
- }
807
- },
808
- async () => {
809
- const timestamp = /* @__PURE__ */ new Date();
810
- const startInstant = performance.now();
811
- const result = await execute();
812
- const endInstant = performance.now();
813
- this.#onQuery?.({
814
- timestamp,
815
- duration: endInstant - startInstant,
816
- query: query.sql,
817
- params: query.args
818
- });
819
- return result;
820
- }
821
- );
1114
+ #withQuerySpanAndEvent(query, queryable, execute) {
1115
+ return withQuerySpanAndEvent({
1116
+ query,
1117
+ queryable,
1118
+ execute,
1119
+ tracingHelper: this.#tracingHelper,
1120
+ onQuery: this.#onQuery
1121
+ });
822
1122
  }
823
1123
  };
824
1124
  function isEmpty(value) {
@@ -862,7 +1162,12 @@ function attachChildrenToParent(parentRecord, children) {
862
1162
  }
863
1163
  function filterChildRecords(records, parentRecord, joinExpr) {
864
1164
  if (Array.isArray(records)) {
865
- return records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
1165
+ const filtered = records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
1166
+ if (joinExpr.isRelationUnique) {
1167
+ return filtered.length > 0 ? filtered[0] : null;
1168
+ } else {
1169
+ return filtered;
1170
+ }
866
1171
  } else if (records === null) {
867
1172
  return null;
868
1173
  } else {
@@ -878,6 +1183,18 @@ function childRecordMatchesParent(childRecord, parentRecord, joinExpr) {
878
1183
  }
879
1184
  return true;
880
1185
  }
1186
+ function paginate(list, { cursor, skip, take }) {
1187
+ const cursorIndex = cursor !== null ? list.findIndex((item) => doKeysMatch(item, cursor)) : 0;
1188
+ if (cursorIndex === -1) {
1189
+ return [];
1190
+ }
1191
+ const start = cursorIndex + (skip ?? 0);
1192
+ const end = take !== null ? start + take : list.length;
1193
+ return list.slice(start, end);
1194
+ }
1195
+ function getRecordKey(record, fields) {
1196
+ return JSON.stringify(fields.map((field) => record[field]));
1197
+ }
881
1198
 
882
1199
  // src/transactionManager/TransactionManager.ts
883
1200
  var import_debug = require("@prisma/debug");
@@ -892,12 +1209,11 @@ async function randomUUID() {
892
1209
  }
893
1210
 
894
1211
  // src/transactionManager/TransactionManagerErrors.ts
895
- var TransactionManagerError = class extends Error {
1212
+ var TransactionManagerError = class extends UserFacingError {
1213
+ name = "TransactionManagerError";
896
1214
  constructor(message, meta) {
897
- super("Transaction API error: " + message);
898
- this.meta = meta;
1215
+ super("Transaction API error: " + message, "P2028", meta);
899
1216
  }
900
- code = "P2028";
901
1217
  };
902
1218
  var TransactionNotFoundError = class extends TransactionManagerError {
903
1219
  constructor() {
@@ -908,12 +1224,12 @@ var TransactionNotFoundError = class extends TransactionManagerError {
908
1224
  };
909
1225
  var TransactionClosedError = class extends TransactionManagerError {
910
1226
  constructor(operation) {
911
- super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction`);
1227
+ super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction.`);
912
1228
  }
913
1229
  };
914
1230
  var TransactionRolledBackError = class extends TransactionManagerError {
915
1231
  constructor(operation) {
916
- super(`Transaction already closed: A ${operation} cannot be executed on a transaction that was rolled back`);
1232
+ super(`Transaction already closed: A ${operation} cannot be executed on a transaction that was rolled back.`);
917
1233
  }
918
1234
  };
919
1235
  var TransactionStartTimeoutError = class extends TransactionManagerError {
@@ -945,6 +1261,16 @@ var MAX_CLOSED_TRANSACTIONS = 100;
945
1261
  var debug = (0, import_debug.Debug)("prisma:client:transactionManager");
946
1262
  var COMMIT_QUERY = () => ({ sql: "COMMIT", args: [], argTypes: [] });
947
1263
  var ROLLBACK_QUERY = () => ({ sql: "ROLLBACK", args: [], argTypes: [] });
1264
+ var PHANTOM_COMMIT_QUERY = () => ({
1265
+ sql: '-- Implicit "COMMIT" query via underlying driver',
1266
+ args: [],
1267
+ argTypes: []
1268
+ });
1269
+ var PHANTOM_ROLLBACK_QUERY = () => ({
1270
+ sql: '-- Implicit "ROLLBACK" query via underlying driver',
1271
+ args: [],
1272
+ argTypes: []
1273
+ });
948
1274
  var TransactionManager = class {
949
1275
  // The map of active transactions.
950
1276
  transactions = /* @__PURE__ */ new Map();
@@ -954,14 +1280,17 @@ var TransactionManager = class {
954
1280
  driverAdapter;
955
1281
  transactionOptions;
956
1282
  tracingHelper;
1283
+ #onQuery;
957
1284
  constructor({
958
1285
  driverAdapter,
959
1286
  transactionOptions,
960
- tracingHelper
1287
+ tracingHelper,
1288
+ onQuery
961
1289
  }) {
962
1290
  this.driverAdapter = driverAdapter;
963
1291
  this.transactionOptions = transactionOptions;
964
1292
  this.tracingHelper = tracingHelper;
1293
+ this.#onQuery = onQuery;
965
1294
  }
966
1295
  async startTransaction(options) {
967
1296
  return await this.tracingHelper.runInChildSpan("start_transaction", () => this.#startTransactionImpl(options));
@@ -1065,14 +1394,20 @@ var TransactionManager = class {
1065
1394
  debug("Closing transaction.", { transactionId: tx.id, status });
1066
1395
  tx.status = status;
1067
1396
  if (tx.transaction && status === "committed") {
1068
- await tx.transaction.commit();
1069
- if (!tx.transaction.options.usePhantomQuery) {
1070
- await tx.transaction.executeRaw(COMMIT_QUERY());
1397
+ if (tx.transaction.options.usePhantomQuery) {
1398
+ await this.#withQuerySpanAndEvent(PHANTOM_COMMIT_QUERY(), tx.transaction, () => tx.transaction.commit());
1399
+ } else {
1400
+ await tx.transaction.commit();
1401
+ const query = COMMIT_QUERY();
1402
+ await this.#withQuerySpanAndEvent(query, tx.transaction, () => tx.transaction.executeRaw(query));
1071
1403
  }
1072
1404
  } else if (tx.transaction) {
1073
- await tx.transaction.rollback();
1074
- if (!tx.transaction.options.usePhantomQuery) {
1075
- await tx.transaction.executeRaw(ROLLBACK_QUERY());
1405
+ if (tx.transaction.options.usePhantomQuery) {
1406
+ await this.#withQuerySpanAndEvent(PHANTOM_ROLLBACK_QUERY(), tx.transaction, () => tx.transaction.rollback());
1407
+ } else {
1408
+ await tx.transaction.rollback();
1409
+ const query = ROLLBACK_QUERY();
1410
+ await this.#withQuerySpanAndEvent(query, tx.transaction, () => tx.transaction.executeRaw(query));
1076
1411
  }
1077
1412
  }
1078
1413
  clearTimeout(tx.timer);
@@ -1093,15 +1428,29 @@ var TransactionManager = class {
1093
1428
  maxWait: options.maxWait
1094
1429
  };
1095
1430
  }
1431
+ #withQuerySpanAndEvent(query, queryable, execute) {
1432
+ return withQuerySpanAndEvent({
1433
+ query,
1434
+ queryable,
1435
+ execute,
1436
+ tracingHelper: this.tracingHelper,
1437
+ onQuery: this.#onQuery
1438
+ });
1439
+ }
1096
1440
  };
1097
1441
  // Annotate the CommonJS export names for ESM import in node:
1098
1442
  0 && (module.exports = {
1443
+ DataMapperError,
1099
1444
  QueryInterpreter,
1100
1445
  TransactionManager,
1101
1446
  TransactionManagerError,
1102
1447
  UserFacingError,
1448
+ doKeysMatch,
1449
+ isDeepStrictEqual,
1450
+ isPrismaValueBigInt,
1103
1451
  isPrismaValueBytes,
1104
1452
  isPrismaValueGenerator,
1105
1453
  isPrismaValuePlaceholder,
1106
- noopTracingHelper
1454
+ noopTracingHelper,
1455
+ safeJsonStringify
1107
1456
  });