@prisma/client-engine-runtime 6.9.0-dev.1 → 6.9.0-dev.11

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.
@@ -47,6 +47,7 @@ export type PrismaValueType = {
47
47
  export type ResultNode = {
48
48
  type: 'Object';
49
49
  fields: Record<string, ResultNode>;
50
+ flattened: boolean;
50
51
  } | {
51
52
  type: 'Value';
52
53
  dbName: string;
@@ -84,6 +85,7 @@ export type JoinExpression = {
84
85
  child: QueryPlanNode;
85
86
  on: [left: string, right: string][];
86
87
  parentField: string;
88
+ isRelationUnique: boolean;
87
89
  };
88
90
  export type QueryPlanNode = {
89
91
  type: 'seq';
@@ -168,6 +170,24 @@ export type QueryPlanNode = {
168
170
  from: QueryPlanNode;
169
171
  to: QueryPlanNode;
170
172
  };
173
+ } | {
174
+ type: 'distinctBy';
175
+ args: {
176
+ expr: QueryPlanNode;
177
+ fields: string[];
178
+ };
179
+ } | {
180
+ type: 'paginate';
181
+ args: {
182
+ expr: QueryPlanNode;
183
+ pagination: Pagination;
184
+ };
185
+ };
186
+ export type Pagination = {
187
+ cursor: Record<string, PrismaValue> | null;
188
+ take: number | null;
189
+ skip: number | null;
190
+ linkingFields: string[] | null;
171
191
  };
172
192
  export type DataRule = {
173
193
  type: 'rowCountEq';
package/dist/index.d.mts CHANGED
@@ -7,6 +7,10 @@ import { SqlQueryable } from '@prisma/driver-adapter-utils';
7
7
  import { SqlResultSet } from '@prisma/driver-adapter-utils';
8
8
  import { Transaction } from '@prisma/driver-adapter-utils';
9
9
 
10
+ export declare class DataMapperError extends Error {
11
+ name: string;
12
+ }
13
+
10
14
  export declare type DataRule = {
11
15
  type: 'rowCountEq';
12
16
  args: number;
@@ -32,6 +36,11 @@ export declare type Fragment = {
32
36
  type: 'parameterTupleList';
33
37
  };
34
38
 
39
+ /**
40
+ * Checks if two objects are deeply equal, recursively checking all properties for strict equality.
41
+ */
42
+ export declare function isDeepStrictEqual(a: unknown, b: unknown): boolean;
43
+
35
44
  export declare function isPrismaValueBytes(value: unknown): value is PrismaValueBytes;
36
45
 
37
46
  export declare function isPrismaValueGenerator(value: unknown): value is PrismaValueGenerator;
@@ -42,10 +51,18 @@ export declare type JoinExpression = {
42
51
  child: QueryPlanNode;
43
52
  on: [left: string, right: string][];
44
53
  parentField: string;
54
+ isRelationUnique: boolean;
45
55
  };
46
56
 
47
57
  export declare const noopTracingHelper: TracingHelper;
48
58
 
59
+ export declare type Pagination = {
60
+ cursor: Record<string, PrismaValue> | null;
61
+ take: number | null;
62
+ skip: number | null;
63
+ linkingFields: string[] | null;
64
+ };
65
+
49
66
  export declare interface PlaceholderFormat {
50
67
  prefix: string;
51
68
  hasNumbering: boolean;
@@ -233,11 +250,24 @@ export declare type QueryPlanNode = {
233
250
  from: QueryPlanNode;
234
251
  to: QueryPlanNode;
235
252
  };
253
+ } | {
254
+ type: 'distinctBy';
255
+ args: {
256
+ expr: QueryPlanNode;
257
+ fields: string[];
258
+ };
259
+ } | {
260
+ type: 'paginate';
261
+ args: {
262
+ expr: QueryPlanNode;
263
+ pagination: Pagination;
264
+ };
236
265
  };
237
266
 
238
267
  export declare type ResultNode = {
239
268
  type: 'Object';
240
269
  fields: Record<string, ResultNode>;
270
+ flattened: boolean;
241
271
  } | {
242
272
  type: 'Value';
243
273
  dbName: string;
package/dist/index.d.ts CHANGED
@@ -7,6 +7,10 @@ import { SqlQueryable } from '@prisma/driver-adapter-utils';
7
7
  import { SqlResultSet } from '@prisma/driver-adapter-utils';
8
8
  import { Transaction } from '@prisma/driver-adapter-utils';
9
9
 
10
+ export declare class DataMapperError extends Error {
11
+ name: string;
12
+ }
13
+
10
14
  export declare type DataRule = {
11
15
  type: 'rowCountEq';
12
16
  args: number;
@@ -32,6 +36,11 @@ export declare type Fragment = {
32
36
  type: 'parameterTupleList';
33
37
  };
34
38
 
39
+ /**
40
+ * Checks if two objects are deeply equal, recursively checking all properties for strict equality.
41
+ */
42
+ export declare function isDeepStrictEqual(a: unknown, b: unknown): boolean;
43
+
35
44
  export declare function isPrismaValueBytes(value: unknown): value is PrismaValueBytes;
36
45
 
37
46
  export declare function isPrismaValueGenerator(value: unknown): value is PrismaValueGenerator;
@@ -42,10 +51,18 @@ export declare type JoinExpression = {
42
51
  child: QueryPlanNode;
43
52
  on: [left: string, right: string][];
44
53
  parentField: string;
54
+ isRelationUnique: boolean;
45
55
  };
46
56
 
47
57
  export declare const noopTracingHelper: TracingHelper;
48
58
 
59
+ export declare type Pagination = {
60
+ cursor: Record<string, PrismaValue> | null;
61
+ take: number | null;
62
+ skip: number | null;
63
+ linkingFields: string[] | null;
64
+ };
65
+
49
66
  export declare interface PlaceholderFormat {
50
67
  prefix: string;
51
68
  hasNumbering: boolean;
@@ -233,11 +250,24 @@ export declare type QueryPlanNode = {
233
250
  from: QueryPlanNode;
234
251
  to: QueryPlanNode;
235
252
  };
253
+ } | {
254
+ type: 'distinctBy';
255
+ args: {
256
+ expr: QueryPlanNode;
257
+ fields: string[];
258
+ };
259
+ } | {
260
+ type: 'paginate';
261
+ args: {
262
+ expr: QueryPlanNode;
263
+ pagination: Pagination;
264
+ };
236
265
  };
237
266
 
238
267
  export declare type ResultNode = {
239
268
  type: 'Object';
240
269
  fields: Record<string, ResultNode>;
270
+ flattened: boolean;
241
271
  } | {
242
272
  type: 'Value';
243
273
  dbName: string;
package/dist/index.js CHANGED
@@ -30,10 +30,12 @@ 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
+ isDeepStrictEqual: () => isDeepStrictEqual,
37
39
  isPrismaValueBytes: () => isPrismaValueBytes,
38
40
  isPrismaValueGenerator: () => isPrismaValueGenerator,
39
41
  isPrismaValuePlaceholder: () => isPrismaValuePlaceholder,
@@ -41,13 +43,127 @@ __export(index_exports, {
41
43
  });
42
44
  module.exports = __toCommonJS(index_exports);
43
45
 
44
- // src/interpreter/QueryInterpreter.ts
45
- var import_api = require("@opentelemetry/api");
46
+ // src/interpreter/DataMapper.ts
47
+ var import_decimal = __toESM(require("decimal.js"));
46
48
 
47
49
  // src/utils.ts
48
50
  function assertNever(_, message) {
49
51
  throw new Error(message);
50
52
  }
53
+ function isDeepStrictEqual(a, b) {
54
+ 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]));
55
+ }
56
+
57
+ // src/interpreter/DataMapper.ts
58
+ var DataMapperError = class extends Error {
59
+ name = "DataMapperError";
60
+ };
61
+ function applyDataMap(data, structure) {
62
+ switch (structure.type) {
63
+ case "Object":
64
+ return mapArrayOrObject(data, structure.fields);
65
+ case "Value":
66
+ return mapValue(data, structure.resultType);
67
+ default:
68
+ assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
69
+ }
70
+ }
71
+ function mapArrayOrObject(data, fields) {
72
+ if (data === null) return null;
73
+ if (Array.isArray(data)) {
74
+ const rows = data;
75
+ return rows.map((row) => mapObject(row, fields));
76
+ }
77
+ if (typeof data === "object") {
78
+ const row = data;
79
+ return mapObject(row, fields);
80
+ }
81
+ if (typeof data === "string") {
82
+ let decodedData;
83
+ try {
84
+ decodedData = JSON.parse(data);
85
+ } catch (error) {
86
+ throw new DataMapperError(`Expected an array or object, got a string that is not valid JSON`, {
87
+ cause: error
88
+ });
89
+ }
90
+ return mapArrayOrObject(decodedData, fields);
91
+ }
92
+ throw new DataMapperError(`Expected an array or an object, got: ${typeof data}`);
93
+ }
94
+ function mapObject(data, fields) {
95
+ if (typeof data !== "object") {
96
+ throw new DataMapperError(`Expected an object, but got '${typeof data}'`);
97
+ }
98
+ const result = {};
99
+ for (const [name, node] of Object.entries(fields)) {
100
+ switch (node.type) {
101
+ case "Object": {
102
+ if (!node.flattened && !Object.hasOwn(data, name)) {
103
+ throw new DataMapperError(
104
+ `Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
105
+ );
106
+ }
107
+ const target = node.flattened ? data : data[name];
108
+ result[name] = mapArrayOrObject(target, node.fields);
109
+ break;
110
+ }
111
+ case "Value":
112
+ {
113
+ const dbName = node.dbName;
114
+ if (Object.hasOwn(data, dbName)) {
115
+ result[name] = mapValue(data[dbName], node.resultType);
116
+ } else {
117
+ throw new DataMapperError(
118
+ `Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
119
+ );
120
+ }
121
+ }
122
+ break;
123
+ default:
124
+ assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
125
+ }
126
+ }
127
+ return result;
128
+ }
129
+ function mapValue(value, resultType) {
130
+ if (value === null) return null;
131
+ switch (resultType.type) {
132
+ case "Any":
133
+ return value;
134
+ case "String":
135
+ return typeof value === "string" ? value : `${value}`;
136
+ case "Int":
137
+ return typeof value === "number" ? value : parseInt(`${value}`, 10);
138
+ case "BigInt":
139
+ return typeof value === "bigint" ? value : BigInt(`${value}`);
140
+ case "Float":
141
+ return typeof value === "number" ? value : parseFloat(`${value}`);
142
+ case "Boolean":
143
+ return typeof value === "boolean" ? value : value !== "0";
144
+ case "Decimal":
145
+ return typeof value === "number" ? new import_decimal.default(value) : new import_decimal.default(`${value}`);
146
+ case "Date":
147
+ return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
148
+ case "Array": {
149
+ const values = value;
150
+ return values.map((v) => mapValue(v, resultType.inner));
151
+ }
152
+ case "Object":
153
+ return typeof value === "string" ? value : JSON.stringify(value);
154
+ case "Bytes": {
155
+ if (!Array.isArray(value)) {
156
+ throw new DataMapperError(`Bytes data is invalid, got: ${typeof value}`);
157
+ }
158
+ return new Uint8Array(value);
159
+ }
160
+ default:
161
+ assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
162
+ }
163
+ }
164
+
165
+ // src/interpreter/QueryInterpreter.ts
166
+ var import_api = require("@opentelemetry/api");
51
167
 
52
168
  // src/tracing.ts
53
169
  var noopTracingHelper = {
@@ -211,100 +327,6 @@ function renderConstraint(constraint) {
211
327
  return "(not available)";
212
328
  }
213
329
 
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 (Object.hasOwn(data, name)) {
247
- result[name] = mapArrayOrObject(data[name], node.fields);
248
- } else {
249
- throw new Error(
250
- `DataMapper: Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
251
- );
252
- }
253
- break;
254
- case "Value":
255
- {
256
- const dbName = node.dbName;
257
- if (Object.hasOwn(data, dbName)) {
258
- result[name] = mapValue(data[dbName], node.resultType);
259
- } else {
260
- throw new Error(
261
- `DataMapper: Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
262
- );
263
- }
264
- }
265
- break;
266
- default:
267
- assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
268
- }
269
- }
270
- return result;
271
- }
272
- function mapValue(value, resultType) {
273
- if (value === null) return null;
274
- switch (resultType.type) {
275
- case "Any":
276
- return value;
277
- case "String":
278
- return typeof value === "string" ? value : `${value}`;
279
- case "Int":
280
- return typeof value === "number" ? value : parseInt(`${value}`, 10);
281
- case "BigInt":
282
- return typeof value === "bigint" ? value : BigInt(`${value}`);
283
- case "Float":
284
- return typeof value === "number" ? value : parseFloat(`${value}`);
285
- case "Boolean":
286
- return typeof value === "boolean" ? value : value !== "0";
287
- case "Decimal":
288
- return typeof value === "number" ? new import_decimal.default(value) : new import_decimal.default(`${value}`);
289
- case "Date":
290
- return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
291
- case "Array": {
292
- const values = value;
293
- return values.map((v) => mapValue(v, resultType.inner));
294
- }
295
- case "Object":
296
- return typeof value === "string" ? value : JSON.stringify(value);
297
- case "Bytes": {
298
- if (!Array.isArray(value)) {
299
- throw new Error(`DataMapper: Bytes data is invalid, got: ${typeof value}`);
300
- }
301
- return new Uint8Array(value);
302
- }
303
- default:
304
- assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
305
- }
306
- }
307
-
308
330
  // src/interpreter/generators.ts
309
331
  var import_cuid = __toESM(require("@bugsnag/cuid"));
310
332
  var import_cuid2 = require("@paralleldrive/cuid2");
@@ -665,8 +687,11 @@ var QueryInterpreter = class _QueryInterpreter {
665
687
  async interpretNode(node, queryable, scope, generators) {
666
688
  switch (node.type) {
667
689
  case "seq": {
668
- const results = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
669
- return results[results.length - 1];
690
+ let result;
691
+ for (const arg of node.args) {
692
+ result = await this.interpretNode(arg, queryable, scope, generators);
693
+ }
694
+ return result;
670
695
  }
671
696
  case "get": {
672
697
  return scope[node.args.name];
@@ -790,6 +815,38 @@ var QueryInterpreter = class _QueryInterpreter {
790
815
  const toSet = new Set(asList(to));
791
816
  return asList(from).filter((item) => !toSet.has(item));
792
817
  }
818
+ case "distinctBy": {
819
+ const value = await this.interpretNode(node.args.expr, queryable, scope, generators);
820
+ const seen = /* @__PURE__ */ new Set();
821
+ const result = [];
822
+ for (const item of asList(value)) {
823
+ const key = getRecordKey(item, node.args.fields);
824
+ if (!seen.has(key)) {
825
+ seen.add(key);
826
+ result.push(item);
827
+ }
828
+ }
829
+ return result;
830
+ }
831
+ case "paginate": {
832
+ const value = await this.interpretNode(node.args.expr, queryable, scope, generators);
833
+ const list = asList(value);
834
+ const linkingFields = node.args.pagination.linkingFields;
835
+ if (linkingFields !== null) {
836
+ const groupedByParent = /* @__PURE__ */ new Map();
837
+ for (const item of list) {
838
+ const parentKey = getRecordKey(item, linkingFields);
839
+ if (!groupedByParent.has(parentKey)) {
840
+ groupedByParent.set(parentKey, []);
841
+ }
842
+ groupedByParent.get(parentKey).push(item);
843
+ }
844
+ const groupList = Array.from(groupedByParent.entries());
845
+ groupList.sort(([aId], [bId]) => aId < bId ? -1 : aId > bId ? 1 : 0);
846
+ return groupList.flatMap(([, elems]) => paginate(elems, node.args.pagination));
847
+ }
848
+ return paginate(list, node.args.pagination);
849
+ }
793
850
  default:
794
851
  assertNever(node, `Unexpected node type: ${node.type}`);
795
852
  }
@@ -861,7 +918,12 @@ function attachChildrenToParent(parentRecord, children) {
861
918
  }
862
919
  function filterChildRecords(records, parentRecord, joinExpr) {
863
920
  if (Array.isArray(records)) {
864
- return records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
921
+ const filtered = records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
922
+ if (joinExpr.isRelationUnique) {
923
+ return filtered.length > 0 ? filtered[0] : null;
924
+ } else {
925
+ return filtered;
926
+ }
865
927
  } else if (records === null) {
866
928
  return null;
867
929
  } else {
@@ -877,6 +939,26 @@ function childRecordMatchesParent(childRecord, parentRecord, joinExpr) {
877
939
  }
878
940
  return true;
879
941
  }
942
+ function paginate(list, { cursor, skip, take }) {
943
+ const cursorIndex = cursor !== null ? list.findIndex((item) => doesMatchCursor(item, cursor)) : 0;
944
+ if (cursorIndex === -1) {
945
+ return [];
946
+ }
947
+ const start = cursorIndex + (skip ?? 0);
948
+ const end = take !== null ? start + take : list.length;
949
+ return list.slice(start, end);
950
+ }
951
+ function getRecordKey(record, fields) {
952
+ return JSON.stringify(fields.map((field) => record[field]));
953
+ }
954
+ function doesMatchCursor(item, cursor) {
955
+ return Object.keys(cursor).every((key) => {
956
+ if (typeof item[key] !== typeof cursor[key] && (typeof item[key] === "number" || typeof cursor[key] === "number")) {
957
+ return `${item[key]}` === `${cursor[key]}`;
958
+ }
959
+ return isDeepStrictEqual(cursor[key], item[key]);
960
+ });
961
+ }
880
962
 
881
963
  // src/transactionManager/TransactionManager.ts
882
964
  var import_debug = require("@prisma/debug");
@@ -1095,10 +1177,12 @@ var TransactionManager = class {
1095
1177
  };
1096
1178
  // Annotate the CommonJS export names for ESM import in node:
1097
1179
  0 && (module.exports = {
1180
+ DataMapperError,
1098
1181
  QueryInterpreter,
1099
1182
  TransactionManager,
1100
1183
  TransactionManagerError,
1101
1184
  UserFacingError,
1185
+ isDeepStrictEqual,
1102
1186
  isPrismaValueBytes,
1103
1187
  isPrismaValueGenerator,
1104
1188
  isPrismaValuePlaceholder,
package/dist/index.mjs CHANGED
@@ -1,10 +1,124 @@
1
- // src/interpreter/QueryInterpreter.ts
2
- import { SpanKind } from "@opentelemetry/api";
1
+ // src/interpreter/DataMapper.ts
2
+ import Decimal from "decimal.js";
3
3
 
4
4
  // src/utils.ts
5
5
  function assertNever(_, message) {
6
6
  throw new Error(message);
7
7
  }
8
+ function isDeepStrictEqual(a, b) {
9
+ 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]));
10
+ }
11
+
12
+ // src/interpreter/DataMapper.ts
13
+ var DataMapperError = class extends Error {
14
+ name = "DataMapperError";
15
+ };
16
+ function applyDataMap(data, structure) {
17
+ switch (structure.type) {
18
+ case "Object":
19
+ return mapArrayOrObject(data, structure.fields);
20
+ case "Value":
21
+ return mapValue(data, structure.resultType);
22
+ default:
23
+ assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
24
+ }
25
+ }
26
+ function mapArrayOrObject(data, fields) {
27
+ if (data === null) return null;
28
+ if (Array.isArray(data)) {
29
+ const rows = data;
30
+ return rows.map((row) => mapObject(row, fields));
31
+ }
32
+ if (typeof data === "object") {
33
+ const row = data;
34
+ return mapObject(row, fields);
35
+ }
36
+ if (typeof data === "string") {
37
+ let decodedData;
38
+ try {
39
+ decodedData = JSON.parse(data);
40
+ } catch (error) {
41
+ throw new DataMapperError(`Expected an array or object, got a string that is not valid JSON`, {
42
+ cause: error
43
+ });
44
+ }
45
+ return mapArrayOrObject(decodedData, fields);
46
+ }
47
+ throw new DataMapperError(`Expected an array or an object, got: ${typeof data}`);
48
+ }
49
+ function mapObject(data, fields) {
50
+ if (typeof data !== "object") {
51
+ throw new DataMapperError(`Expected an object, but got '${typeof data}'`);
52
+ }
53
+ const result = {};
54
+ for (const [name, node] of Object.entries(fields)) {
55
+ switch (node.type) {
56
+ case "Object": {
57
+ if (!node.flattened && !Object.hasOwn(data, name)) {
58
+ throw new DataMapperError(
59
+ `Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
60
+ );
61
+ }
62
+ const target = node.flattened ? data : data[name];
63
+ result[name] = mapArrayOrObject(target, node.fields);
64
+ break;
65
+ }
66
+ case "Value":
67
+ {
68
+ const dbName = node.dbName;
69
+ if (Object.hasOwn(data, dbName)) {
70
+ result[name] = mapValue(data[dbName], node.resultType);
71
+ } else {
72
+ throw new DataMapperError(
73
+ `Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
74
+ );
75
+ }
76
+ }
77
+ break;
78
+ default:
79
+ assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
80
+ }
81
+ }
82
+ return result;
83
+ }
84
+ function mapValue(value, resultType) {
85
+ if (value === null) return null;
86
+ switch (resultType.type) {
87
+ case "Any":
88
+ return value;
89
+ case "String":
90
+ return typeof value === "string" ? value : `${value}`;
91
+ case "Int":
92
+ return typeof value === "number" ? value : parseInt(`${value}`, 10);
93
+ case "BigInt":
94
+ return typeof value === "bigint" ? value : BigInt(`${value}`);
95
+ case "Float":
96
+ return typeof value === "number" ? value : parseFloat(`${value}`);
97
+ case "Boolean":
98
+ return typeof value === "boolean" ? value : value !== "0";
99
+ case "Decimal":
100
+ return typeof value === "number" ? new Decimal(value) : new Decimal(`${value}`);
101
+ case "Date":
102
+ return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
103
+ case "Array": {
104
+ const values = value;
105
+ return values.map((v) => mapValue(v, resultType.inner));
106
+ }
107
+ case "Object":
108
+ return typeof value === "string" ? value : JSON.stringify(value);
109
+ case "Bytes": {
110
+ if (!Array.isArray(value)) {
111
+ throw new DataMapperError(`Bytes data is invalid, got: ${typeof value}`);
112
+ }
113
+ return new Uint8Array(value);
114
+ }
115
+ default:
116
+ assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
117
+ }
118
+ }
119
+
120
+ // src/interpreter/QueryInterpreter.ts
121
+ import { SpanKind } from "@opentelemetry/api";
8
122
 
9
123
  // src/tracing.ts
10
124
  var noopTracingHelper = {
@@ -168,100 +282,6 @@ function renderConstraint(constraint) {
168
282
  return "(not available)";
169
283
  }
170
284
 
171
- // src/interpreter/DataMapper.ts
172
- import Decimal from "decimal.js";
173
- function applyDataMap(data, structure) {
174
- switch (structure.type) {
175
- case "Object":
176
- return mapArrayOrObject(data, structure.fields);
177
- case "Value":
178
- return mapValue(data, structure.resultType);
179
- default:
180
- assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
181
- }
182
- }
183
- function mapArrayOrObject(data, fields) {
184
- if (data === null) return null;
185
- if (Array.isArray(data)) {
186
- const rows = data;
187
- return rows.map((row) => mapObject(row, fields));
188
- }
189
- if (typeof data === "object") {
190
- const row = data;
191
- return mapObject(row, fields);
192
- }
193
- throw new Error(`DataMapper: Expected an array or an object, got: ${typeof data}`);
194
- }
195
- function mapObject(data, fields) {
196
- if (typeof data !== "object") {
197
- throw new Error(`DataMapper: Expected an object, but got '${typeof data}'`);
198
- }
199
- const result = {};
200
- for (const [name, node] of Object.entries(fields)) {
201
- switch (node.type) {
202
- case "Object":
203
- if (Object.hasOwn(data, name)) {
204
- result[name] = mapArrayOrObject(data[name], node.fields);
205
- } else {
206
- throw new Error(
207
- `DataMapper: Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
208
- );
209
- }
210
- break;
211
- case "Value":
212
- {
213
- const dbName = node.dbName;
214
- if (Object.hasOwn(data, dbName)) {
215
- result[name] = mapValue(data[dbName], node.resultType);
216
- } else {
217
- throw new Error(
218
- `DataMapper: Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
219
- );
220
- }
221
- }
222
- break;
223
- default:
224
- assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
225
- }
226
- }
227
- return result;
228
- }
229
- function mapValue(value, resultType) {
230
- if (value === null) return null;
231
- switch (resultType.type) {
232
- case "Any":
233
- return value;
234
- case "String":
235
- return typeof value === "string" ? value : `${value}`;
236
- case "Int":
237
- return typeof value === "number" ? value : parseInt(`${value}`, 10);
238
- case "BigInt":
239
- return typeof value === "bigint" ? value : BigInt(`${value}`);
240
- case "Float":
241
- return typeof value === "number" ? value : parseFloat(`${value}`);
242
- case "Boolean":
243
- return typeof value === "boolean" ? value : value !== "0";
244
- case "Decimal":
245
- return typeof value === "number" ? new Decimal(value) : new Decimal(`${value}`);
246
- case "Date":
247
- return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
248
- case "Array": {
249
- const values = value;
250
- return values.map((v) => mapValue(v, resultType.inner));
251
- }
252
- case "Object":
253
- return typeof value === "string" ? value : JSON.stringify(value);
254
- case "Bytes": {
255
- if (!Array.isArray(value)) {
256
- throw new Error(`DataMapper: Bytes data is invalid, got: ${typeof value}`);
257
- }
258
- return new Uint8Array(value);
259
- }
260
- default:
261
- assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
262
- }
263
- }
264
-
265
285
  // src/interpreter/generators.ts
266
286
  import cuid1 from "@bugsnag/cuid";
267
287
  import { createId as cuid2 } from "@paralleldrive/cuid2";
@@ -622,8 +642,11 @@ var QueryInterpreter = class _QueryInterpreter {
622
642
  async interpretNode(node, queryable, scope, generators) {
623
643
  switch (node.type) {
624
644
  case "seq": {
625
- const results = await Promise.all(node.args.map((arg) => this.interpretNode(arg, queryable, scope, generators)));
626
- return results[results.length - 1];
645
+ let result;
646
+ for (const arg of node.args) {
647
+ result = await this.interpretNode(arg, queryable, scope, generators);
648
+ }
649
+ return result;
627
650
  }
628
651
  case "get": {
629
652
  return scope[node.args.name];
@@ -747,6 +770,38 @@ var QueryInterpreter = class _QueryInterpreter {
747
770
  const toSet = new Set(asList(to));
748
771
  return asList(from).filter((item) => !toSet.has(item));
749
772
  }
773
+ case "distinctBy": {
774
+ const value = await this.interpretNode(node.args.expr, queryable, scope, generators);
775
+ const seen = /* @__PURE__ */ new Set();
776
+ const result = [];
777
+ for (const item of asList(value)) {
778
+ const key = getRecordKey(item, node.args.fields);
779
+ if (!seen.has(key)) {
780
+ seen.add(key);
781
+ result.push(item);
782
+ }
783
+ }
784
+ return result;
785
+ }
786
+ case "paginate": {
787
+ const value = await this.interpretNode(node.args.expr, queryable, scope, generators);
788
+ const list = asList(value);
789
+ const linkingFields = node.args.pagination.linkingFields;
790
+ if (linkingFields !== null) {
791
+ const groupedByParent = /* @__PURE__ */ new Map();
792
+ for (const item of list) {
793
+ const parentKey = getRecordKey(item, linkingFields);
794
+ if (!groupedByParent.has(parentKey)) {
795
+ groupedByParent.set(parentKey, []);
796
+ }
797
+ groupedByParent.get(parentKey).push(item);
798
+ }
799
+ const groupList = Array.from(groupedByParent.entries());
800
+ groupList.sort(([aId], [bId]) => aId < bId ? -1 : aId > bId ? 1 : 0);
801
+ return groupList.flatMap(([, elems]) => paginate(elems, node.args.pagination));
802
+ }
803
+ return paginate(list, node.args.pagination);
804
+ }
750
805
  default:
751
806
  assertNever(node, `Unexpected node type: ${node.type}`);
752
807
  }
@@ -818,7 +873,12 @@ function attachChildrenToParent(parentRecord, children) {
818
873
  }
819
874
  function filterChildRecords(records, parentRecord, joinExpr) {
820
875
  if (Array.isArray(records)) {
821
- return records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
876
+ const filtered = records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
877
+ if (joinExpr.isRelationUnique) {
878
+ return filtered.length > 0 ? filtered[0] : null;
879
+ } else {
880
+ return filtered;
881
+ }
822
882
  } else if (records === null) {
823
883
  return null;
824
884
  } else {
@@ -834,6 +894,26 @@ function childRecordMatchesParent(childRecord, parentRecord, joinExpr) {
834
894
  }
835
895
  return true;
836
896
  }
897
+ function paginate(list, { cursor, skip, take }) {
898
+ const cursorIndex = cursor !== null ? list.findIndex((item) => doesMatchCursor(item, cursor)) : 0;
899
+ if (cursorIndex === -1) {
900
+ return [];
901
+ }
902
+ const start = cursorIndex + (skip ?? 0);
903
+ const end = take !== null ? start + take : list.length;
904
+ return list.slice(start, end);
905
+ }
906
+ function getRecordKey(record, fields) {
907
+ return JSON.stringify(fields.map((field) => record[field]));
908
+ }
909
+ function doesMatchCursor(item, cursor) {
910
+ return Object.keys(cursor).every((key) => {
911
+ if (typeof item[key] !== typeof cursor[key] && (typeof item[key] === "number" || typeof cursor[key] === "number")) {
912
+ return `${item[key]}` === `${cursor[key]}`;
913
+ }
914
+ return isDeepStrictEqual(cursor[key], item[key]);
915
+ });
916
+ }
837
917
 
838
918
  // src/transactionManager/TransactionManager.ts
839
919
  import { Debug } from "@prisma/debug";
@@ -1051,10 +1131,12 @@ var TransactionManager = class {
1051
1131
  }
1052
1132
  };
1053
1133
  export {
1134
+ DataMapperError,
1054
1135
  QueryInterpreter,
1055
1136
  TransactionManager,
1056
1137
  TransactionManagerError,
1057
1138
  UserFacingError,
1139
+ isDeepStrictEqual,
1058
1140
  isPrismaValueBytes,
1059
1141
  isPrismaValueGenerator,
1060
1142
  isPrismaValuePlaceholder,
@@ -1,3 +1,6 @@
1
1
  import { ResultNode } from '../QueryPlan';
2
2
  import { Value } from './scope';
3
+ export declare class DataMapperError extends Error {
4
+ name: string;
5
+ }
3
6
  export declare function applyDataMap(data: Value, structure: ResultNode): Value;
package/dist/utils.d.ts CHANGED
@@ -1 +1,5 @@
1
1
  export declare function assertNever(_: never, message: string): never;
2
+ /**
3
+ * Checks if two objects are deeply equal, recursively checking all properties for strict equality.
4
+ */
5
+ export declare function isDeepStrictEqual(a: unknown, b: unknown): boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/client-engine-runtime",
3
- "version": "6.9.0-dev.1",
3
+ "version": "6.9.0-dev.11",
4
4
  "description": "This package is intended for Prisma's internal use",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -31,8 +31,8 @@
31
31
  "nanoid": "5.1.5",
32
32
  "ulid": "3.0.0",
33
33
  "uuid": "11.1.0",
34
- "@prisma/debug": "6.9.0-dev.1",
35
- "@prisma/driver-adapter-utils": "6.9.0-dev.1"
34
+ "@prisma/debug": "6.9.0-dev.11",
35
+ "@prisma/driver-adapter-utils": "6.9.0-dev.11"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/jest": "29.5.14",