@lokalise/prisma-utils 5.0.2 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +129 -12
  2. package/dist/{errors → src/errors}/cockroachdbError.d.ts +1 -1
  3. package/dist/src/errors/cockroachdbError.js.map +1 -0
  4. package/dist/src/errors/index.js.map +1 -0
  5. package/dist/{errors → src/errors}/prismaError.d.ts +1 -1
  6. package/dist/src/errors/prismaError.js.map +1 -0
  7. package/dist/src/factory/extendPrismaClientWithMetrics.d.ts +3 -0
  8. package/dist/src/factory/extendPrismaClientWithMetrics.js +90 -0
  9. package/dist/src/factory/extendPrismaClientWithMetrics.js.map +1 -0
  10. package/dist/src/factory/index.d.ts +1 -0
  11. package/dist/src/factory/index.js +2 -0
  12. package/dist/src/factory/index.js.map +1 -0
  13. package/dist/src/factory/prismaClientFactory.d.ts +20 -0
  14. package/dist/src/factory/prismaClientFactory.js +26 -0
  15. package/dist/src/factory/prismaClientFactory.js.map +1 -0
  16. package/dist/src/index.d.ts +4 -0
  17. package/dist/src/index.js +4 -0
  18. package/dist/src/index.js.map +1 -0
  19. package/dist/src/transaction/index.d.ts +2 -0
  20. package/dist/src/transaction/index.js +2 -0
  21. package/dist/src/transaction/index.js.map +1 -0
  22. package/dist/src/transaction/prismaTransaction.d.ts +17 -0
  23. package/dist/{prismaTransaction.js → src/transaction/prismaTransaction.js} +5 -6
  24. package/dist/src/transaction/prismaTransaction.js.map +1 -0
  25. package/dist/src/transaction/types.d.ts +15 -0
  26. package/dist/src/transaction/types.js +2 -0
  27. package/dist/src/transaction/types.js.map +1 -0
  28. package/dist/src/types.d.ts +7 -0
  29. package/dist/src/types.js +5 -0
  30. package/dist/src/types.js.map +1 -0
  31. package/dist/test/db-client/client.d.ts +31 -0
  32. package/dist/test/db-client/client.js +36 -0
  33. package/dist/test/db-client/client.js.map +1 -0
  34. package/dist/test/db-client/commonInputTypes.d.ts +119 -0
  35. package/dist/test/db-client/commonInputTypes.js +11 -0
  36. package/dist/test/db-client/commonInputTypes.js.map +1 -0
  37. package/dist/test/db-client/enums.d.ts +1 -0
  38. package/dist/test/db-client/enums.js +12 -0
  39. package/dist/test/db-client/enums.js.map +1 -0
  40. package/dist/test/db-client/internal/class.d.ts +137 -0
  41. package/dist/test/db-client/internal/class.js +50 -0
  42. package/dist/test/db-client/internal/class.js.map +1 -0
  43. package/dist/test/db-client/internal/prismaNamespace.d.ts +592 -0
  44. package/dist/test/db-client/internal/prismaNamespace.js +98 -0
  45. package/dist/test/db-client/internal/prismaNamespace.js.map +1 -0
  46. package/dist/test/db-client/models/Item1.d.ts +912 -0
  47. package/dist/test/db-client/models/Item1.js +2 -0
  48. package/dist/test/db-client/models/Item1.js.map +1 -0
  49. package/dist/test/db-client/models/Item2.d.ts +909 -0
  50. package/dist/test/db-client/models/Item2.js +2 -0
  51. package/dist/test/db-client/models/Item2.js.map +1 -0
  52. package/dist/test/db-client/models.d.ts +3 -0
  53. package/dist/test/db-client/models.js +2 -0
  54. package/dist/test/db-client/models.js.map +1 -0
  55. package/package.json +13 -18
  56. package/dist/errors/cockroachdbError.js.map +0 -1
  57. package/dist/errors/index.js.map +0 -1
  58. package/dist/errors/prismaError.js.map +0 -1
  59. package/dist/index.d.ts +0 -5
  60. package/dist/index.js +0 -5
  61. package/dist/index.js.map +0 -1
  62. package/dist/isolation_level/isolationLevel.d.ts +0 -3
  63. package/dist/isolation_level/isolationLevel.js +0 -2
  64. package/dist/isolation_level/isolationLevel.js.map +0 -1
  65. package/dist/plugins/CollectionScheduler.d.ts +0 -12
  66. package/dist/plugins/CollectionScheduler.js +0 -20
  67. package/dist/plugins/CollectionScheduler.js.map +0 -1
  68. package/dist/plugins/MetricsCollector.d.ts +0 -36
  69. package/dist/plugins/MetricsCollector.js +0 -188
  70. package/dist/plugins/MetricsCollector.js.map +0 -1
  71. package/dist/plugins/prismaMetricsPlugin.d.ts +0 -21
  72. package/dist/plugins/prismaMetricsPlugin.js +0 -49
  73. package/dist/plugins/prismaMetricsPlugin.js.map +0 -1
  74. package/dist/prismaClientFactory.d.ts +0 -5
  75. package/dist/prismaClientFactory.js +0 -11
  76. package/dist/prismaClientFactory.js.map +0 -1
  77. package/dist/prismaTransaction.d.ts +0 -17
  78. package/dist/prismaTransaction.js.map +0 -1
  79. package/dist/types.d.ts +0 -44
  80. package/dist/types.js +0 -4
  81. package/dist/types.js.map +0 -1
  82. /package/dist/{errors → src/errors}/cockroachdbError.js +0 -0
  83. /package/dist/{errors → src/errors}/index.d.ts +0 -0
  84. /package/dist/{errors → src/errors}/index.js +0 -0
  85. /package/dist/{errors → src/errors}/prismaError.js +0 -0
package/README.md CHANGED
@@ -2,26 +2,143 @@
2
2
 
3
3
  This package provides reusable helpers for Prisma query builder.
4
4
 
5
- # Usage
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @lokalise/prisma-utils
9
+ ```
10
+
11
+ ## Features
12
+
13
+ ### Prisma Client Factory
14
+
15
+ Factory function to create a Prisma client instance with default configuration and optional Prometheus metrics integration.
16
+
17
+ **Without metrics:**
18
+
19
+ ```typescript
20
+ import { prismaClientFactory } from '@lokalise/prisma-utils'
21
+ import { PrismaClient } from '@prisma/client'
22
+
23
+ const prisma = prismaClientFactory(PrismaClient, {
24
+ // Your Prisma client options
25
+ })
26
+ ```
27
+
28
+ **With Prometheus metrics:**
29
+
30
+ ```typescript
31
+ import { prismaClientFactory } from '@lokalise/prisma-utils'
32
+ import { PrismaClient } from '@prisma/client'
33
+ import * as promClient from 'prom-client'
34
+
35
+ const prisma = prismaClientFactory(
36
+ PrismaClient,
37
+ {
38
+ // Your Prisma client options
39
+ },
40
+ { promClient }
41
+ )
42
+ ```
43
+
44
+ The factory automatically:
45
+ - Sets default transaction isolation level to `ReadCommitted`
46
+ - Extends the client with Prometheus metrics when `promClient` is provided
47
+
48
+ #### Prisma Metrics Collection
49
+
50
+ When you provide `promClient` to the factory, the following metrics are automatically collected:
51
+
52
+ - **`prisma_queries_total`**: Total number of Prisma queries executed
53
+ - Labels: `model`, `operation`, `status` (success/error)
54
+ - **`prisma_query_duration_seconds`**: Duration of Prisma queries in seconds
55
+ - Labels: `model`, `operation`
56
+ - Buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5]
57
+ - **`prisma_errors_total`**: Total number of Prisma query errors
58
+ - Labels: `model`, `operation`, `error_code`
59
+
60
+ These metrics are automatically exposed through the Prometheus registry and can be scraped by your monitoring system.
61
+
62
+ ### Prisma Transactions
63
+
64
+ Helper for executing Prisma transactions with intelligent automatic retry mechanism, optimized for distributed databases like CockroachDB.
65
+
66
+ #### Basic Usage
67
+
68
+ **With multiple operations:**
6
69
 
7
70
  ```typescript
8
71
  import { prismaTransaction } from '@lokalise/prisma-utils'
72
+ import type { Either } from '@lokalise/node-core'
9
73
 
10
- const result: Either<unknown, [Item, Segment]> = await prismaTransaction(prisma, [
11
- prisma.item.create({ data: TEST_ITEM_1 }),
12
- prisma.segment.create({ data: TEST_SEGMENT_1 }),
13
- ])
74
+ const result: Either<unknown, [Item, Segment]> = await prismaTransaction(
75
+ prisma,
76
+ { dbDriver: 'CockroachDb' },
77
+ [
78
+ prisma.item.create({ data: { value: 'item-1' } }),
79
+ prisma.segment.create({ data: { name: 'segment-1' } }),
80
+ ]
81
+ )
14
82
  ```
15
83
 
16
- This implementation will retry the transaction on P2034 error, which satisfies Prisma recommendations for distributed databases such as CockroachDB.
84
+ **With transaction callback:**
85
+
86
+ ```typescript
87
+ const result: Either<unknown, User> = await prismaTransaction(
88
+ prisma,
89
+ { dbDriver: 'CockroachDb' },
90
+ async (tx) => {
91
+ const user = await tx.user.create({ data: { name: 'John' } })
92
+ await tx.profile.create({ data: { userId: user.id, bio: 'Developer' } })
93
+ return user
94
+ }
95
+ )
96
+ ```
17
97
 
18
- ### Prisma metrics plugin
98
+ #### Automatic Retry Mechanism
19
99
 
20
- Plugin to collect and send metrics to prometheus. Prisma metrics will be added to our app metrics.
100
+ The transaction helper automatically retries on the following error conditions:
21
101
 
22
- Add the plugin to your Fastify instance by registering it with the following options:
102
+ **Prisma Error Codes:**
103
+ - **P2034**: Write conflict / Serialization failure (common in distributed databases)
104
+ - **P2028**: Transaction API error
105
+ - **P1017**: Server closed the connection
106
+
107
+ **CockroachDB-specific:**
108
+ - Automatically detects and retries CockroachDB retry transaction errors.
109
+
110
+ #### Retry Strategy
111
+
112
+ 1. **Exponential Backoff**: Delay between retries increases exponentially
113
+ - Formula: `2^(retry-1) × baseRetryDelayMs`
114
+ - Example: 100ms → 200ms → 400ms → 800ms...
115
+
116
+ 2. **Smart Timeout Adjustment**: If transaction times out (P2028 with "transaction is closed"), the timeout is
117
+ automatically doubled on retry (up to `maxTimeout`)
118
+
119
+ 3. **Configurable Options**:
120
+
121
+ ```typescript
122
+ const result = await prismaTransaction(
123
+ prisma,
124
+ {
125
+ dbDriver: 'CockroachDb', // Enable CockroachDB-specific retries
126
+ retriesAllowed: 2, // Default: 2 (total 3 attempts)
127
+ baseRetryDelayMs: 100, // Default: 100ms
128
+ maxRetryDelayMs: 30000, // Default: 30s (max delay between retries)
129
+ timeout: 5000, // Default: 5s (transaction timeout)
130
+ maxTimeout: 30000, // Default: 30s (max transaction timeout)
131
+ },
132
+ [
133
+ // Your transaction operations
134
+ ]
135
+ )
136
+ ```
23
137
 
24
- - `isEnabled`;
25
- - `collectionOptions` (by default we collect metrics `every 5 seconds`) to override default collector behaviour
138
+ #### Why This Matters
26
139
 
27
- Once the plugin has been added to your Fastify instance and loaded, we will start collection prisma metrics.
140
+ Distributed databases like CockroachDB use optimistic concurrency control, which can result in serialization errors
141
+ when multiple transactions compete for the same resources. This helper abstracts away the complexity of handling these
142
+ errors, providing a robust and efficient way to ensure your transactions succeed even under high contention.
143
+ By automatically retrying failed transactions with an intelligent backoff strategy, you can significantly improve the
144
+ reliability and performance of your application when using distributed databases.
@@ -1,4 +1,4 @@
1
- import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
1
+ import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/client';
2
2
  /**
3
3
  * Check if the error is a CockroachDB transaction retry error
4
4
  *
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cockroachdbError.js","sourceRoot":"","sources":["../../../src/errors/cockroachdbError.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,kCAAkC,GAAG,OAAO,CAAA;AAElD;;;;GAIG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,KAAoC,EAAW,EAAE;IAC7F,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAA;IACvB,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAEvB,OAAO,IAAI,CAAC,IAAI,KAAK,kCAAkC,CAAA;AACzD,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/errors/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAA"}
@@ -1,4 +1,4 @@
1
- import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
1
+ import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/client';
2
2
  /**
3
3
  * What is checked?
4
4
  * 1. error is defined and not null
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prismaError.js","sourceRoot":"","sources":["../../../src/errors/prismaError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAG7C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,CAC7C,KAAc,EAC0B,EAAE,CAC1C,CAAC,CAAC,KAAK;IACP,OAAO,CAAC,KAAK,CAAC;IACd,MAAM,IAAI,KAAK;IACf,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;IAC9B,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;AAE5B,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,KAAoC,EAAW,EAAE,CAC9F,KAAK,CAAC,IAAI,KAAK,wBAAwB;IACvC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAA;AAEpE;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAA;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,OAAO,CAAA;AAEjD;;;GAGG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,OAAO,CAAA;AAErD;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAA;AAE/C;;GAEG;AACH,MAAM,CAAC,MAAM,qCAAqC,GAAG,OAAO,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type Prometheus from 'prom-client';
2
+ import type { PrismaClient } from '../../test/db-client/client.ts';
3
+ export declare const extendPrismaClientWithMetrics: <Client extends PrismaClient>(prisma: Client, promClient: typeof Prometheus) => Client;
@@ -0,0 +1,90 @@
1
+ export const extendPrismaClientWithMetrics = (prisma, promClient) => createExtendedClient(prisma, registerMetricsIfNeeded(promClient));
2
+ const METRICS_PREFIX = 'prisma';
3
+ const createExtendedClient = (prisma, metrics) => {
4
+ return prisma.$extends({
5
+ name: 'metrics-collector',
6
+ query: {
7
+ $allModels: {
8
+ async $allOperations({ model, operation, args, query }) {
9
+ const startTime = Date.now();
10
+ const resolvedModel = model ?? 'unknown';
11
+ let result = { result: undefined };
12
+ try {
13
+ const response = await query(args);
14
+ result = { result: response };
15
+ }
16
+ catch (error) {
17
+ result = { error };
18
+ }
19
+ finally {
20
+ const duration = (Date.now() - startTime) / 1000;
21
+ // Always record metrics (success or error)
22
+ metrics.queriesTotal.inc({
23
+ model: resolvedModel,
24
+ operation,
25
+ status: result.error ? 'error' : 'success',
26
+ });
27
+ metrics.queryDuration.observe({ model: resolvedModel, operation }, duration);
28
+ if (result.error) {
29
+ const errorCode = result.error?.code || 'unknown';
30
+ metrics.errorsTotal.inc({
31
+ model: resolvedModel,
32
+ operation,
33
+ error_code: errorCode,
34
+ });
35
+ }
36
+ }
37
+ if (result.error)
38
+ throw result.error;
39
+ return result.result;
40
+ },
41
+ },
42
+ },
43
+ });
44
+ };
45
+ /**
46
+ * Register Prometheus metrics if they don't already exist
47
+ */
48
+ const registerMetricsIfNeeded = (promClient) => {
49
+ const existingMetrics = getExistingMetrics(promClient);
50
+ if (existingMetrics)
51
+ return existingMetrics;
52
+ const queriesTotal = new promClient.Counter({
53
+ name: `${METRICS_PREFIX}_queries_total`,
54
+ help: 'Total number of Prisma queries executed',
55
+ labelNames: ['model', 'operation', 'status'],
56
+ registers: [promClient.register],
57
+ });
58
+ const queryDuration = new promClient.Histogram({
59
+ name: `${METRICS_PREFIX}_query_duration_seconds`,
60
+ help: 'Duration of Prisma queries in seconds',
61
+ labelNames: ['model', 'operation'],
62
+ buckets: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5],
63
+ registers: [promClient.register],
64
+ });
65
+ const errorsTotal = new promClient.Counter({
66
+ name: `${METRICS_PREFIX}_errors_total`,
67
+ help: 'Total number of Prisma query errors',
68
+ labelNames: ['model', 'operation', 'error_code'],
69
+ registers: [promClient.register],
70
+ });
71
+ return { queriesTotal, queryDuration, errorsTotal };
72
+ };
73
+ /**
74
+ * Check if metrics already exist in the registry
75
+ */
76
+ const getExistingMetrics = (promClient) => {
77
+ try {
78
+ const queriesTotal = promClient.register.getSingleMetric(`${METRICS_PREFIX}_queries_total`);
79
+ const queryDuration = promClient.register.getSingleMetric(`${METRICS_PREFIX}_query_duration_seconds`);
80
+ const errorsTotal = promClient.register.getSingleMetric(`${METRICS_PREFIX}_errors_total`);
81
+ if (queriesTotal && queryDuration && errorsTotal) {
82
+ return { queriesTotal, queryDuration, errorsTotal };
83
+ }
84
+ }
85
+ catch {
86
+ // If any error occurs (e.g. metrics not found), return null to create new metrics
87
+ }
88
+ return null;
89
+ };
90
+ //# sourceMappingURL=extendPrismaClientWithMetrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extendPrismaClientWithMetrics.js","sourceRoot":"","sources":["../../../src/factory/extendPrismaClientWithMetrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export { prismaClientFactory } from './prismaClientFactory.ts';
@@ -0,0 +1,2 @@
1
+ export { prismaClientFactory } from "./prismaClientFactory.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/factory/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA"}
@@ -0,0 +1,20 @@
1
+ import type * as RuntimePrisma from '@prisma/client/runtime/client';
2
+ import type Prometheus from 'prom-client';
3
+ import type { PrismaClient } from '../../test/db-client/client.ts';
4
+ import type { PrismaClientConstructor } from '../../test/db-client/internal/class.ts';
5
+ export type prismaClientFactoryOptions = {
6
+ promClient?: typeof Prometheus;
7
+ };
8
+ /**
9
+ * Factory function to create a Prisma client instance with default configuration
10
+ * and optional Prometheus metrics integration.
11
+ *
12
+ * @template Client - The Prisma client type to instantiate
13
+ * @param builder - The Prisma client constructor
14
+ * @param options - Prisma client initialization options
15
+ * @param factoryOptions - Optional factory configuration
16
+ * @param factoryOptions.promClient - Prometheus client instance to enable metrics collection
17
+ * @returns A configured Prisma client instance, optionally extended with metrics
18
+ *
19
+ */
20
+ export declare const prismaClientFactory: <Client extends PrismaClient>(builder: PrismaClientConstructor, options: RuntimePrisma.PrismaClientOptions, factoryOptions?: prismaClientFactoryOptions) => Client;
@@ -0,0 +1,26 @@
1
+ import { extendPrismaClientWithMetrics } from "./extendPrismaClientWithMetrics.js";
2
+ /**
3
+ * Factory function to create a Prisma client instance with default configuration
4
+ * and optional Prometheus metrics integration.
5
+ *
6
+ * @template Client - The Prisma client type to instantiate
7
+ * @param builder - The Prisma client constructor
8
+ * @param options - Prisma client initialization options
9
+ * @param factoryOptions - Optional factory configuration
10
+ * @param factoryOptions.promClient - Prometheus client instance to enable metrics collection
11
+ * @returns A configured Prisma client instance, optionally extended with metrics
12
+ *
13
+ */
14
+ export const prismaClientFactory = (builder, options, factoryOptions) => {
15
+ options.transactionOptions = {
16
+ isolationLevel: 'ReadCommitted',
17
+ ...options.transactionOptions,
18
+ };
19
+ // @ts-expect-error
20
+ let prismaClient = new builder(options);
21
+ if (factoryOptions?.promClient) {
22
+ prismaClient = extendPrismaClientWithMetrics(prismaClient, factoryOptions.promClient);
23
+ }
24
+ return prismaClient;
25
+ };
26
+ //# sourceMappingURL=prismaClientFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prismaClientFactory.js","sourceRoot":"","sources":["../../../src/factory/prismaClientFactory.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAA;AAMlF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAAgC,EAChC,OAA0C,EAC1C,cAA2C,EACnC,EAAE;IACV,OAAO,CAAC,kBAAkB,GAAG;QAC3B,cAAc,EAAE,eAAe;QAC/B,GAAG,OAAO,CAAC,kBAAkB;KAC9B,CAAA;IAED,mBAAmB;IACnB,IAAI,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IACvC,IAAI,cAAc,EAAE,UAAU,EAAE,CAAC;QAC/B,YAAY,GAAG,6BAA6B,CAAC,YAAY,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;IACvF,CAAC;IAED,OAAO,YAAsB,CAAA;AAC/B,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './errors/index.ts';
2
+ export * from './factory/index.ts';
3
+ export * from './transaction/index.ts';
4
+ export type * from './types.ts';
@@ -0,0 +1,4 @@
1
+ export * from "./errors/index.js";
2
+ export * from "./factory/index.js";
3
+ export * from "./transaction/index.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,oBAAoB,CAAA;AAClC,cAAc,wBAAwB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { prismaTransaction } from './prismaTransaction.ts';
2
+ export type * from './types.ts';
@@ -0,0 +1,2 @@
1
+ export { prismaTransaction } from "./prismaTransaction.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/transaction/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA"}
@@ -0,0 +1,17 @@
1
+ import type { Either } from '@lokalise/node-core';
2
+ import type * as RuntimePrisma from '@prisma/client/runtime/client';
3
+ import type { PrismaClient } from '../../test/db-client/client.ts';
4
+ import type { PrismaTransactionFn, PrismaTransactionOptions } from './types.ts';
5
+ /**
6
+ * Perform a Prisma DB transaction with automatic retries if needed.
7
+ *
8
+ * @template T | T extends Prisma.PrismaPromise<unknown>[]
9
+ * @param {PrismaClient} prisma
10
+ * @param {PrismaTransactionFn<T> | RuntimePrisma.PrismaPromise<unknown>[]} arg operation to perform into the transaction
11
+ * @param {PrismaTransactionOptions} options transaction configuration
12
+ * @return {Promise<PrismaTransactionReturnType<T>>}
13
+ */
14
+ export declare const prismaTransaction: {
15
+ <T, P extends PrismaClient>(prisma: P, options: PrismaTransactionOptions, fn: PrismaTransactionFn<T, P>): Promise<Either<unknown, T>>;
16
+ <T extends RuntimePrisma.PrismaPromise<unknown>[], P extends PrismaClient>(prisma: P, options: PrismaTransactionOptions, args: [...T]): Promise<Either<unknown, RuntimePrisma.Types.Utils.UnwrapTuple<T>>>;
17
+ };
@@ -1,10 +1,9 @@
1
1
  import { setTimeout } from 'node:timers/promises';
2
2
  import { deepClone } from '@lokalise/node-core';
3
- import { isCockroachDBRetryTransaction } from "./errors/cockroachdbError.js";
4
- import { isPrismaClientKnownRequestError, isPrismaTransactionClosedError, PRISMA_SERIALIZATION_ERROR, PRISMA_SERVER_CLOSED_CONNECTION_ERROR, PRISMA_TRANSACTION_ERROR, } from "./errors/prismaError.js";
3
+ import { isCockroachDBRetryTransaction } from "../errors/cockroachdbError.js";
4
+ import { isPrismaClientKnownRequestError, isPrismaTransactionClosedError, PRISMA_SERIALIZATION_ERROR, PRISMA_SERVER_CLOSED_CONNECTION_ERROR, PRISMA_TRANSACTION_ERROR, } from "../errors/index.js";
5
5
  const DEFAULT_OPTIONS = {
6
6
  retriesAllowed: 2, // first try + 2 retries = 3 tries
7
- dbDriver: 'CockroachDb',
8
7
  baseRetryDelayMs: 100,
9
8
  maxRetryDelayMs: 30000, // 30s
10
9
  timeout: 5000, // 5s
@@ -15,11 +14,11 @@ const DEFAULT_OPTIONS = {
15
14
  *
16
15
  * @template T | T extends Prisma.PrismaPromise<unknown>[]
17
16
  * @param {PrismaClient} prisma
18
- * @param {PrismaTransactionFn<T> | Prisma.PrismaPromise<unknown>[]} arg operation to perform into the transaction
19
- * @param {PrismaTransactionOptions | PrismaTransactionBasicOptions} options transaction configuration
17
+ * @param {PrismaTransactionFn<T> | RuntimePrisma.PrismaPromise<unknown>[]} arg operation to perform into the transaction
18
+ * @param {PrismaTransactionOptions} options transaction configuration
20
19
  * @return {Promise<PrismaTransactionReturnType<T>>}
21
20
  */
22
- export const prismaTransaction = (async (prisma, arg, options) => {
21
+ export const prismaTransaction = (async (prisma, options, arg) => {
23
22
  let optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options };
24
23
  let result;
25
24
  let retries = 0;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prismaTransaction.js","sourceRoot":"","sources":["../../../src/transaction/prismaTransaction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { Either } from '@lokalise/node-core';
2
+ import type * as RuntimePrisma from '@prisma/client/runtime/client';
3
+ import type { DbDriver } from '../types.ts';
4
+ type InternalTransactionOptions = RuntimePrisma.PrismaClientOptions['transactionOptions'];
5
+ export type PrismaTransactionOptions = {
6
+ dbDriver: DbDriver;
7
+ retriesAllowed?: number;
8
+ baseRetryDelayMs?: number;
9
+ maxRetryDelayMs?: number;
10
+ maxTimeout?: number;
11
+ } & InternalTransactionOptions;
12
+ export type PrismaTransactionClient<P> = Omit<P, RuntimePrisma.ITXClientDenyList>;
13
+ export type PrismaTransactionFn<T, P> = (prisma: PrismaTransactionClient<P>) => Promise<T>;
14
+ export type PrismaTransactionReturnType<T> = Either<unknown, T | RuntimePrisma.Types.Utils.UnwrapTuple<RuntimePrisma.PrismaPromise<unknown>[]>>;
15
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/transaction/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ type ObjectValues<T> = T[keyof T];
2
+ export declare const DbDriverEnum: {
3
+ readonly COCKROACH_DB: "CockroachDb";
4
+ readonly POSTGRES: "Postgres";
5
+ };
6
+ export type DbDriver = ObjectValues<typeof DbDriverEnum>;
7
+ export {};
@@ -0,0 +1,5 @@
1
+ export const DbDriverEnum = {
2
+ COCKROACH_DB: 'CockroachDb',
3
+ POSTGRES: 'Postgres',
4
+ };
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,YAAY,EAAE,aAAa;IAC3B,QAAQ,EAAE,UAAU;CACZ,CAAA"}
@@ -0,0 +1,31 @@
1
+ import * as runtime from "@prisma/client/runtime/client";
2
+ import * as $Class from "./internal/class.ts";
3
+ import * as Prisma from "./internal/prismaNamespace.ts";
4
+ export * as $Enums from './enums.ts';
5
+ export * from "./enums.ts";
6
+ /**
7
+ * ## Prisma Client
8
+ *
9
+ * Type-safe database client for TypeScript
10
+ * @example
11
+ * ```
12
+ * const prisma = new PrismaClient()
13
+ * // Fetch zero or more Item1s
14
+ * const item1s = await prisma.item1.findMany()
15
+ * ```
16
+ *
17
+ * Read more in our [docs](https://pris.ly/d/client).
18
+ */
19
+ export declare const PrismaClient: $Class.PrismaClientConstructor;
20
+ export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>;
21
+ export { Prisma };
22
+ /**
23
+ * Model Item1
24
+ *
25
+ */
26
+ export type Item1 = Prisma.Item1Model;
27
+ /**
28
+ * Model Item2
29
+ *
30
+ */
31
+ export type Item2 = Prisma.Item2Model;
@@ -0,0 +1,36 @@
1
+ /* !!! This is code generated by Prisma. Do not edit directly. !!! */
2
+ /* eslint-disable */
3
+ // biome-ignore-all lint: generated file
4
+ // @ts-nocheck
5
+ /*
6
+ * This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
7
+ * If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
8
+ *
9
+ * 🟢 You can import this file directly.
10
+ */
11
+ import * as process from 'node:process';
12
+ import * as path from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
14
+ globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url));
15
+ import * as runtime from "@prisma/client/runtime/client";
16
+ import * as $Enums from "./enums.js";
17
+ import * as $Class from "./internal/class.js";
18
+ import * as Prisma from "./internal/prismaNamespace.js";
19
+ export * as $Enums from "./enums.js";
20
+ export * from "./enums.js";
21
+ /**
22
+ * ## Prisma Client
23
+ *
24
+ * Type-safe database client for TypeScript
25
+ * @example
26
+ * ```
27
+ * const prisma = new PrismaClient()
28
+ * // Fetch zero or more Item1s
29
+ * const item1s = await prisma.item1.findMany()
30
+ * ```
31
+ *
32
+ * Read more in our [docs](https://pris.ly/d/client).
33
+ */
34
+ export const PrismaClient = $Class.getPrismaClientClass();
35
+ export { Prisma };
36
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../test/db-client/client.ts"],"names":[],"mappings":"AACA,qEAAqE;AACrE,oBAAoB;AACpB,wCAAwC;AACxC,eAAe;AACf;;;;;GAKG;AAEH,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,UAAU,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAEtE,OAAO,KAAK,OAAO,MAAM,+BAA+B,CAAA;AACxD,OAAO,KAAK,MAAM,MAAM,YAAY,CAAA;AACpC,OAAO,KAAK,MAAM,MAAM,qBAAqB,CAAA;AAC7C,OAAO,KAAK,MAAM,MAAM,+BAA+B,CAAA;AAEvD,OAAO,KAAK,MAAM,MAAM,YAAY,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,oBAAoB,EAAE,CAAA;AAEzD,OAAO,EAAE,MAAM,EAAE,CAAA"}