@prisma/client-engine-runtime 6.7.0-dev.5 → 6.7.0-dev.50

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
@@ -35,10 +35,38 @@ __export(index_exports, {
35
35
  TransactionManagerError: () => TransactionManagerError,
36
36
  UserFacingError: () => UserFacingError,
37
37
  isPrismaValueGenerator: () => isPrismaValueGenerator,
38
- isPrismaValuePlaceholder: () => isPrismaValuePlaceholder
38
+ isPrismaValuePlaceholder: () => isPrismaValuePlaceholder,
39
+ noopTracingHelper: () => noopTracingHelper
39
40
  });
40
41
  module.exports = __toCommonJS(index_exports);
41
42
 
43
+ // src/interpreter/QueryInterpreter.ts
44
+ var import_api = require("@opentelemetry/api");
45
+
46
+ // src/utils.ts
47
+ function assertNever(_, message) {
48
+ throw new Error(message);
49
+ }
50
+
51
+ // src/tracing.ts
52
+ var noopTracingHelper = {
53
+ runInChildSpan(_, callback) {
54
+ return callback();
55
+ }
56
+ };
57
+ function providerToOtelSystem(provider) {
58
+ switch (provider) {
59
+ case "postgres":
60
+ return "postgresql";
61
+ case "mysql":
62
+ return "mysql";
63
+ case "sqlite":
64
+ return "sqlite";
65
+ default:
66
+ assertNever(provider, `Unknown provider: ${provider}`);
67
+ }
68
+ }
69
+
42
70
  // src/UserFacingError.ts
43
71
  var import_driver_adapter_utils = require("@prisma/driver-adapter-utils");
44
72
  var UserFacingError = class extends Error {
@@ -111,10 +139,8 @@ function getErrorCode(err) {
111
139
  case "sqlite":
112
140
  case "mysql":
113
141
  return;
114
- default: {
115
- const cause = err.cause;
116
- throw new Error(`Unknown error: ${cause}`);
117
- }
142
+ default:
143
+ assertNever(err.cause, `Unknown error: ${err.cause}`);
118
144
  }
119
145
  }
120
146
  function renderErrorMessage(err) {
@@ -169,10 +195,8 @@ function renderErrorMessage(err) {
169
195
  case "postgres":
170
196
  case "mysql":
171
197
  return;
172
- default: {
173
- const cause = err.cause;
174
- throw new Error(`Unknown error: ${cause}`);
175
- }
198
+ default:
199
+ assertNever(err.cause, `Unknown error: ${err.cause}`);
176
200
  }
177
201
  }
178
202
  function renderConstraint(constraint) {
@@ -186,6 +210,100 @@ function renderConstraint(constraint) {
186
210
  return "(not available)";
187
211
  }
188
212
 
213
+ // src/interpreter/DataMapper.ts
214
+ function applyDataMap(data, structure) {
215
+ switch (structure.type) {
216
+ case "Object":
217
+ return mapArrayOrObject(data, structure.fields);
218
+ case "Value":
219
+ return mapValue(data, structure.resultType);
220
+ default:
221
+ assertNever(structure, `Invalid data mapping type: '${structure.type}'`);
222
+ }
223
+ }
224
+ function mapArrayOrObject(data, fields) {
225
+ if (data === null) return null;
226
+ if (Array.isArray(data)) {
227
+ const rows = data;
228
+ return rows.map((row) => mapObject(row, fields));
229
+ }
230
+ if (typeof data === "object") {
231
+ const row = data;
232
+ return mapObject(row, fields);
233
+ }
234
+ throw new Error(`DataMapper: Expected an array or an object, got: ${typeof data}`);
235
+ }
236
+ function mapObject(data, fields) {
237
+ if (typeof data !== "object") {
238
+ throw new Error(`DataMapper: Expected an object, but got '${typeof data}'`);
239
+ }
240
+ const result = {};
241
+ for (const [name, node] of Object.entries(fields)) {
242
+ switch (node.type) {
243
+ case "Object":
244
+ if (Object.hasOwn(data, name)) {
245
+ result[name] = mapArrayOrObject(data[name], node.fields);
246
+ } else {
247
+ throw new Error(
248
+ `DataMapper: Missing data field (Object): '${name}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
249
+ );
250
+ }
251
+ break;
252
+ case "Value":
253
+ {
254
+ const dbName = node.dbName;
255
+ if (Object.hasOwn(data, dbName)) {
256
+ result[name] = mapValue(data[dbName], node.resultType);
257
+ } else {
258
+ throw new Error(
259
+ `DataMapper: Missing data field (Value): '${dbName}'; node: ${JSON.stringify(node)}; data: ${JSON.stringify(data)}`
260
+ );
261
+ }
262
+ }
263
+ break;
264
+ default:
265
+ assertNever(node, `DataMapper: Invalid data mapping node type: '${node.type}'`);
266
+ }
267
+ }
268
+ return result;
269
+ }
270
+ function mapValue(value, resultType) {
271
+ if (value === null) return null;
272
+ switch (resultType.type) {
273
+ case "Any":
274
+ return value;
275
+ case "String":
276
+ return typeof value === "string" ? value : `${value}`;
277
+ case "Int":
278
+ return typeof value === "number" ? value : parseInt(`${value}`, 10);
279
+ case "BigInt":
280
+ return typeof value === "bigint" ? value : BigInt(`${value}`);
281
+ case "Float":
282
+ return typeof value === "number" ? value : parseFloat(`${value}`);
283
+ case "Boolean":
284
+ return typeof value === "boolean" ? value : value !== "0";
285
+ case "Decimal":
286
+ return typeof value === "number" ? value : parseFloat(`${value}`);
287
+ case "Date":
288
+ return value instanceof Date ? value : /* @__PURE__ */ new Date(`${value}`);
289
+ case "Array": {
290
+ const values = value;
291
+ return values.map((v) => {
292
+ mapValue(v, resultType.inner);
293
+ });
294
+ }
295
+ case "Object":
296
+ return typeof value === "object" ? value : { value };
297
+ case "Bytes":
298
+ if (typeof value !== "string") {
299
+ throw new Error(`DataMapper: Bytes data is not a string, got: ${typeof value}`);
300
+ }
301
+ return value;
302
+ default:
303
+ assertNever(resultType, `DataMapper: Unknown result type: ${resultType.type}`);
304
+ }
305
+ }
306
+
189
307
  // src/interpreter/generators.ts
190
308
  var import_cuid = __toESM(require("@bugsnag/cuid"));
191
309
  var import_cuid2 = require("@paralleldrive/cuid2");
@@ -200,6 +318,7 @@ var GeneratorRegistry = class {
200
318
  this.register("cuid", new CuidGenerator());
201
319
  this.register("ulid", new UlidGenerator());
202
320
  this.register("nanoid", new NanoIdGenerator());
321
+ this.register("product", new ProductGenerator());
203
322
  }
204
323
  /**
205
324
  * Returns a snapshot of the generator registry. It's 'frozen' in time at the moment of this
@@ -262,6 +381,22 @@ var NanoIdGenerator = class {
262
381
  }
263
382
  }
264
383
  };
384
+ var ProductGenerator = class {
385
+ generate(lhs, rhs) {
386
+ if (lhs === void 0 || rhs === void 0) {
387
+ throw new Error("Invalid Product generator arguments");
388
+ }
389
+ if (Array.isArray(lhs) && Array.isArray(rhs)) {
390
+ return lhs.flatMap((l) => rhs.map((r) => [l, r]));
391
+ } else if (Array.isArray(lhs)) {
392
+ return lhs.map((l) => [l, rhs]);
393
+ } else if (Array.isArray(rhs)) {
394
+ return rhs.map((r) => [lhs, r]);
395
+ } else {
396
+ return [[lhs, rhs]];
397
+ }
398
+ }
399
+ };
265
400
 
266
401
  // src/QueryPlan.ts
267
402
  function isPrismaValuePlaceholder(value) {
@@ -271,46 +406,49 @@ function isPrismaValueGenerator(value) {
271
406
  return typeof value === "object" && value !== null && value["prisma__type"] === "generatorCall";
272
407
  }
273
408
 
274
- // src/utils.ts
275
- function assertNever(_, message) {
276
- throw new Error(message);
277
- }
278
-
279
409
  // src/interpreter/renderQuery.ts
280
410
  function renderQuery(dbQuery, scope, generators) {
281
411
  const queryType = dbQuery.type;
282
412
  switch (queryType) {
283
413
  case "rawSql":
284
- return renderRawSql(dbQuery.sql, substituteParams(dbQuery.params, scope, generators));
414
+ return renderRawSql(dbQuery.sql, evaluateParams(dbQuery.params, scope, generators));
285
415
  case "templateSql":
286
416
  return renderTemplateSql(
287
417
  dbQuery.fragments,
288
418
  dbQuery.placeholderFormat,
289
- substituteParams(dbQuery.params, scope, generators)
419
+ evaluateParams(dbQuery.params, scope, generators)
290
420
  );
291
421
  default:
292
422
  assertNever(queryType, `Invalid query type`);
293
423
  }
294
424
  }
295
- function substituteParams(params, scope, generators) {
296
- return params.map((param) => {
297
- if (isPrismaValueGenerator(param)) {
298
- const { name, args } = param.prisma__value;
425
+ function evaluateParams(params, scope, generators) {
426
+ return params.map((param) => evaluateParam(param, scope, generators));
427
+ }
428
+ function evaluateParam(param, scope, generators) {
429
+ let value = param;
430
+ while (doesRequireEvaluation(value)) {
431
+ if (isPrismaValuePlaceholder(value)) {
432
+ const found = scope[value.prisma__value.name];
433
+ if (found === void 0) {
434
+ throw new Error(`Missing value for query variable ${value.prisma__value.name}`);
435
+ }
436
+ value = found;
437
+ } else if (isPrismaValueGenerator(value)) {
438
+ const { name, args } = value.prisma__value;
299
439
  const generator = generators[name];
300
440
  if (!generator) {
301
441
  throw new Error(`Encountered an unknown generator '${name}'`);
302
442
  }
303
- return generator.generate(...args);
304
- }
305
- if (!isPrismaValuePlaceholder(param)) {
306
- return param;
307
- }
308
- const value = scope[param.prisma__value.name];
309
- if (value === void 0) {
310
- throw new Error(`Missing value for query variable ${param.prisma__value.name}`);
443
+ value = generator.generate(...args.map((arg) => evaluateParam(arg, scope, generators)));
444
+ } else {
445
+ assertNever(value, `Unexpected unevaluated value type: ${value}`);
311
446
  }
312
- return value;
313
- });
447
+ }
448
+ if (Array.isArray(value)) {
449
+ value = value.map((el) => evaluateParam(el, scope, generators));
450
+ }
451
+ return value;
314
452
  }
315
453
  function renderTemplateSql(fragments, placeholderFormat, params) {
316
454
  let paramIndex = 0;
@@ -339,6 +477,29 @@ function renderTemplateSql(fragments, placeholderFormat, params) {
339
477
  }).join(",");
340
478
  return `(${placeholders})`;
341
479
  }
480
+ case "parameterTupleList": {
481
+ if (paramIndex >= params.length) {
482
+ throw new Error(`Malformed query template. Fragments attempt to read over ${params.length} parameters.`);
483
+ }
484
+ const paramValue = params[paramIndex++];
485
+ if (!Array.isArray(paramValue)) {
486
+ throw new Error(`Malformed query template. Tuple list expected.`);
487
+ }
488
+ if (paramValue.length === 0) {
489
+ throw new Error(`Malformed query template. Tuple list cannot be empty.`);
490
+ }
491
+ const tupleList = paramValue.map((tuple) => {
492
+ if (!Array.isArray(tuple)) {
493
+ throw new Error(`Malformed query template. Tuple expected.`);
494
+ }
495
+ const elements = tuple.map((value) => {
496
+ flattenedParams.push(value);
497
+ return formatPlaceholder(placeholderFormat, placeholderNumber++);
498
+ }).join(",");
499
+ return `(${elements})`;
500
+ }).join(",");
501
+ return tupleList;
502
+ }
342
503
  default:
343
504
  assertNever(fragmentType, "Invalid fragment type");
344
505
  }
@@ -397,9 +558,12 @@ function placeholderTypeToArgType(type) {
397
558
  }
398
559
  return mappedType;
399
560
  }
561
+ function doesRequireEvaluation(param) {
562
+ return isPrismaValuePlaceholder(param) || isPrismaValueGenerator(param);
563
+ }
400
564
 
401
- // src/interpreter/serialize.ts
402
- function serialize(resultSet) {
565
+ // src/interpreter/serializeSql.ts
566
+ function serializeSql(resultSet) {
403
567
  return resultSet.rows.map(
404
568
  (row) => row.reduce((acc, value, index) => {
405
569
  const splitByDot = resultSet.columnNames[index].split(".");
@@ -421,15 +585,28 @@ function serialize(resultSet) {
421
585
  }
422
586
 
423
587
  // src/interpreter/QueryInterpreter.ts
424
- var QueryInterpreter = class {
588
+ var QueryInterpreter = class _QueryInterpreter {
425
589
  #transactionManager;
426
590
  #placeholderValues;
427
591
  #onQuery;
428
592
  #generators = new GeneratorRegistry();
429
- constructor({ transactionManager, placeholderValues, onQuery }) {
593
+ #tracingHelper;
594
+ #serializer;
595
+ constructor({ transactionManager, placeholderValues, onQuery, tracingHelper, serializer }) {
430
596
  this.#transactionManager = transactionManager;
431
597
  this.#placeholderValues = placeholderValues;
432
598
  this.#onQuery = onQuery;
599
+ this.#tracingHelper = tracingHelper;
600
+ this.#serializer = serializer;
601
+ }
602
+ static forSql(options) {
603
+ return new _QueryInterpreter({
604
+ transactionManager: options.transactionManager,
605
+ placeholderValues: options.placeholderValues,
606
+ onQuery: options.onQuery,
607
+ tracingHelper: options.tracingHelper,
608
+ serializer: serializeSql
609
+ });
433
610
  }
434
611
  async run(queryPlan, queryable) {
435
612
  return this.interpretNode(queryPlan, queryable, this.#placeholderValues, this.#generators.snapshot()).catch(
@@ -473,14 +650,14 @@ var QueryInterpreter = class {
473
650
  }
474
651
  case "execute": {
475
652
  const query = renderQuery(node.args, scope, generators);
476
- return this.#withQueryEvent(query, async () => {
653
+ return this.#withQueryEvent(query, queryable, async () => {
477
654
  return await queryable.executeRaw(query);
478
655
  });
479
656
  }
480
657
  case "query": {
481
658
  const query = renderQuery(node.args, scope, generators);
482
- return this.#withQueryEvent(query, async () => {
483
- return serialize(await queryable.queryRaw(query));
659
+ return this.#withQueryEvent(query, queryable, async () => {
660
+ return this.#serializer(await queryable.queryRaw(query));
484
661
  });
485
662
  }
486
663
  case "reverse": {
@@ -530,7 +707,7 @@ var QueryInterpreter = class {
530
707
  }
531
708
  const transactionManager = this.#transactionManager.manager;
532
709
  const transactionInfo = await transactionManager.startTransaction();
533
- const transaction = transactionManager.getTransaction(transactionInfo, "new");
710
+ const transaction = transactionManager.getTransaction(transactionInfo, "query");
534
711
  try {
535
712
  const value = await this.interpretNode(node.args, transaction, scope, generators);
536
713
  await transactionManager.commitTransaction(transactionInfo.id);
@@ -540,24 +717,38 @@ var QueryInterpreter = class {
540
717
  throw e;
541
718
  }
542
719
  }
543
- default: {
544
- node;
545
- throw new Error(`Unexpected node type: ${node.type}`);
720
+ case "dataMap": {
721
+ const data = await this.interpretNode(node.args.expr, queryable, scope, generators);
722
+ return applyDataMap(data, node.args.structure);
546
723
  }
724
+ default:
725
+ assertNever(node, `Unexpected node type: ${node.type}`);
547
726
  }
548
727
  }
549
- async #withQueryEvent(query, execute) {
550
- const timestamp = /* @__PURE__ */ new Date();
551
- const startInstant = performance.now();
552
- const result = await execute();
553
- const endInstant = performance.now();
554
- this.#onQuery?.({
555
- timestamp,
556
- duration: endInstant - startInstant,
557
- query: query.sql,
558
- params: query.args
559
- });
560
- return result;
728
+ #withQueryEvent(query, queryable, execute) {
729
+ return this.#tracingHelper.runInChildSpan(
730
+ {
731
+ name: "db_query",
732
+ kind: import_api.SpanKind.CLIENT,
733
+ attributes: {
734
+ "db.query.text": query.sql,
735
+ "db.system.name": providerToOtelSystem(queryable.provider)
736
+ }
737
+ },
738
+ async () => {
739
+ const timestamp = /* @__PURE__ */ new Date();
740
+ const startInstant = performance.now();
741
+ const result = await execute();
742
+ const endInstant = performance.now();
743
+ this.#onQuery?.({
744
+ timestamp,
745
+ duration: endInstant - startInstant,
746
+ query: query.sql,
747
+ params: query.args
748
+ });
749
+ return result;
750
+ }
751
+ );
561
752
  }
562
753
  };
563
754
  function isEmpty(value) {
@@ -602,6 +793,8 @@ function attachChildrenToParent(parentRecord, children) {
602
793
  function filterChildRecords(records, parentRecord, joinExpr) {
603
794
  if (Array.isArray(records)) {
604
795
  return records.filter((record) => childRecordMatchesParent(asRecord(record), parentRecord, joinExpr));
796
+ } else if (records === null) {
797
+ return null;
605
798
  } else {
606
799
  const record = asRecord(records);
607
800
  return childRecordMatchesParent(record, parentRecord, joinExpr) ? record : null;
@@ -650,7 +843,7 @@ var TransactionClosedError = class extends TransactionManagerError {
650
843
  };
651
844
  var TransactionRolledBackError = class extends TransactionManagerError {
652
845
  constructor(operation) {
653
- super(`Transaction already closed: A ${operation} cannot be executed on a committed transaction`);
846
+ super(`Transaction already closed: A ${operation} cannot be executed on a transaction that was rolled back`);
654
847
  }
655
848
  };
656
849
  var TransactionStartTimeoutError = class extends TransactionManagerError {
@@ -690,11 +883,20 @@ var TransactionManager = class {
690
883
  closedTransactions = [];
691
884
  driverAdapter;
692
885
  transactionOptions;
693
- constructor({ driverAdapter, transactionOptions }) {
886
+ tracingHelper;
887
+ constructor({
888
+ driverAdapter,
889
+ transactionOptions,
890
+ tracingHelper
891
+ }) {
694
892
  this.driverAdapter = driverAdapter;
695
893
  this.transactionOptions = transactionOptions;
894
+ this.tracingHelper = tracingHelper;
696
895
  }
697
896
  async startTransaction(options) {
897
+ return await this.tracingHelper.runInChildSpan("start_transaction", () => this.#startTransactionImpl(options));
898
+ }
899
+ async #startTransactionImpl(options) {
698
900
  const validatedOptions = options !== void 0 ? this.validateOptions(options) : this.transactionOptions;
699
901
  const transaction = {
700
902
  id: await randomUUID(),
@@ -728,12 +930,16 @@ var TransactionManager = class {
728
930
  }
729
931
  }
730
932
  async commitTransaction(transactionId) {
731
- const txw = this.getActiveTransaction(transactionId, "commit");
732
- await this.closeTransaction(txw, "committed");
933
+ return await this.tracingHelper.runInChildSpan("commit_transaction", async () => {
934
+ const txw = this.getActiveTransaction(transactionId, "commit");
935
+ await this.closeTransaction(txw, "committed");
936
+ });
733
937
  }
734
938
  async rollbackTransaction(transactionId) {
735
- const txw = this.getActiveTransaction(transactionId, "rollback");
736
- await this.closeTransaction(txw, "rolled_back");
939
+ return await this.tracingHelper.runInChildSpan("rollback_transaction", async () => {
940
+ const txw = this.getActiveTransaction(transactionId, "rollback");
941
+ await this.closeTransaction(txw, "rolled_back");
942
+ });
737
943
  }
738
944
  getTransaction(txInfo, operation) {
739
945
  const tx = this.getActiveTransaction(txInfo.id, operation);
@@ -825,5 +1031,6 @@ var TransactionManager = class {
825
1031
  TransactionManagerError,
826
1032
  UserFacingError,
827
1033
  isPrismaValueGenerator,
828
- isPrismaValuePlaceholder
1034
+ isPrismaValuePlaceholder,
1035
+ noopTracingHelper
829
1036
  });