prisma-generator-effect 1.0.0 → 1.1.0-beta.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 (2) hide show
  1. package/dist/index.js +998 -463
  2. package/package.json +11 -8
package/dist/index.js CHANGED
@@ -4,9 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- const generator_helper_1 = require("@prisma/generator-helper");
7
+ const node_child_process_1 = require("node:child_process");
8
8
  const promises_1 = __importDefault(require("node:fs/promises"));
9
9
  const node_path_1 = __importDefault(require("node:path"));
10
+ const generator_helper_1 = require("@prisma/generator-helper");
10
11
  const header = `// This file was generated by prisma-generator-effect, do not edit manually.\n`;
11
12
  // Utility function to convert PascalCase to camelCase
12
13
  function toCamelCase(str) {
@@ -28,10 +29,7 @@ function toCamelCase(str) {
28
29
  const configPath = options.generator.config.clientImportPath;
29
30
  const clientImportPath = Array.isArray(configPath)
30
31
  ? configPath[0]
31
- : configPath ?? "@prisma/client";
32
- // Get datasource provider (e.g., "sqlite", "postgresql", "mysql", etc.)
33
- const datasources = options.datasources;
34
- const provider = datasources[0]?.provider; // Usually there's one datasource, but could be multiple
32
+ : (configPath ?? "@prisma/client");
35
33
  // Custom error configuration: "path/to/module#ErrorClassName"
36
34
  // Path is relative to schema.prisma, e.g., "./errors#PrismaError"
37
35
  // The module must export:
@@ -47,10 +45,26 @@ function toCamelCase(str) {
47
45
  const importExtConfigRaw = options.generator.config.importFileExtension;
48
46
  const importFileExtension = Array.isArray(importExtConfigRaw)
49
47
  ? importExtConfigRaw[0]
50
- : importExtConfigRaw ?? "";
48
+ : (importExtConfigRaw ?? "");
49
+ // Telemetry configuration: enable Effect.fn tracing for all operations
50
+ // Set to "true" to wrap operations with Effect.fn (adds operation names to traces)
51
+ // Set to "false" to use Effect.fnUntraced (no telemetry overhead)
52
+ const telemetryConfigRaw = options.generator.config.enableTelemetry;
53
+ const enableTelemetry = Array.isArray(telemetryConfigRaw)
54
+ ? telemetryConfigRaw[0] === "true"
55
+ : telemetryConfigRaw === "true";
51
56
  if (!outputDir) {
52
57
  throw new Error("No output directory specified");
53
58
  }
59
+ // Get datasource provider (e.g., "sqlite", "postgresql", "mysql", etc.)
60
+ // Used to avoid generating createManyAndReturn/updateManyAndReturn when the DB doesn't support them
61
+ const datasources = options.datasources;
62
+ const provider = datasources?.[0]?.provider;
63
+ const supportsManyAndReturn = provider === "postgresql" ||
64
+ provider === "postgres" ||
65
+ provider === "prisma+postgres" ||
66
+ provider === "cockroachdb" ||
67
+ provider === "sqlite";
54
68
  // Helper to add file extension to a path if configured
55
69
  const addExtension = (filePath) => {
56
70
  if (!importFileExtension)
@@ -89,254 +103,425 @@ function toCamelCase(str) {
89
103
  await promises_1.default.rm(outputDir, { recursive: true, force: true });
90
104
  await promises_1.default.mkdir(outputDir, { recursive: true });
91
105
  // Generate unified index file with PrismaService
92
- await generateUnifiedService([...models], outputDir, clientImportPath, errorImportPath, provider);
106
+ await generateUnifiedService([...models], outputDir, clientImportPath, errorImportPath, enableTelemetry, supportsManyAndReturn);
93
107
  },
94
108
  });
95
- function generateRawSqlOperations(customError) {
109
+ function generateRawSqlOperations(customError, enableTelemetry) {
96
110
  // With custom error, use mapError which maps to user's error type
97
111
  // Without custom error, use mapError which maps to PrismaError union
98
112
  const errorType = customError ? customError.className : "PrismaError";
113
+ const wrapTelemetry = (name, generatorFn) => enableTelemetry
114
+ ? `Effect.fn("${name}")(${generatorFn})`
115
+ : `Effect.fnUntraced(${generatorFn})`;
99
116
  return `
100
- $executeRaw: (args: PrismaNamespace.Sql | [PrismaNamespace.Sql, ...any[]]): Effect.Effect<number, ${errorType}, PrismaClient> =>
101
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
102
- Effect.tryPromise({
103
- try: () => (Array.isArray(args) ? client.$executeRaw(args[0], ...args.slice(1)) : client.$executeRaw(args)),
104
- catch: (error) => mapError(error, "$executeRaw", "Prisma")
105
- })
106
- ),
107
-
108
- $executeRawUnsafe: (query: string, ...values: any[]): Effect.Effect<number, ${errorType}, PrismaClient> =>
109
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
110
- Effect.tryPromise({
111
- try: () => client.$executeRawUnsafe(query, ...values),
112
- catch: (error) => mapError(error, "$executeRawUnsafe", "Prisma")
113
- })
114
- ),
115
-
116
- $queryRaw: <T = unknown>(args: PrismaNamespace.Sql | [PrismaNamespace.Sql, ...any[]]): Effect.Effect<T, ${errorType}, PrismaClient> =>
117
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
118
- Effect.tryPromise({
119
- try: () => (Array.isArray(args) ? client.$queryRaw(args[0], ...args.slice(1)) : client.$queryRaw(args)) as Promise<T>,
120
- catch: (error) => mapError(error, "$queryRaw", "Prisma")
121
- })
122
- ),
123
-
124
- $queryRawUnsafe: <T = unknown>(query: string, ...values: any[]): Effect.Effect<T, ${errorType}, PrismaClient> =>
125
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
126
- Effect.tryPromise({
127
- try: () => client.$queryRawUnsafe(query, ...values) as Promise<T>,
128
- catch: (error) => mapError(error, "$queryRawUnsafe", "Prisma")
129
- })
130
- ),`;
117
+ $executeRaw: ${wrapTelemetry("Prisma.$executeRaw", `function* (args) {
118
+ const actualClient = yield* clientOrTx(client);
119
+ return yield* Effect.tryPromise<any, ${errorType}>({
120
+ try: () => (Array.isArray(args) ? actualClient.$executeRaw(args[0], ...args.slice(1)) : actualClient.$executeRaw(args)) as any,
121
+ catch: (error) => mapError(error, "$executeRaw", "Prisma")
122
+ });
123
+ }`)},
124
+
125
+ $executeRawUnsafe: ${wrapTelemetry("Prisma.$executeRawUnsafe", `function* (query, ...values) {
126
+ const actualClient = yield* clientOrTx(client);
127
+ return yield* Effect.tryPromise<any, ${errorType}>({
128
+ try: () => actualClient.$executeRawUnsafe(query, ...values) as any,
129
+ catch: (error) => mapError(error, "$executeRawUnsafe", "Prisma")
130
+ });
131
+ }`)},
132
+
133
+ $queryRaw: ${wrapTelemetry("Prisma.$queryRaw", `function* (args) {
134
+ const actualClient = yield* clientOrTx(client);
135
+ return yield* Effect.tryPromise<any, ${errorType}>({
136
+ try: () => (Array.isArray(args) ? actualClient.$queryRaw(args[0], ...args.slice(1)) : actualClient.$queryRaw(args)) as any,
137
+ catch: (error) => mapError(error, "$queryRaw", "Prisma")
138
+ });
139
+ }`)},
140
+
141
+ $queryRawUnsafe: ${wrapTelemetry("Prisma.$queryRawUnsafe", `function* (query, ...values) {
142
+ const actualClient = yield* clientOrTx(client);
143
+ return yield* Effect.tryPromise<any, ${errorType}>({
144
+ try: () => actualClient.$queryRawUnsafe(query, ...values) as any,
145
+ catch: (error) => mapError(error, "$queryRawUnsafe", "Prisma")
146
+ });
147
+ }`)},`;
131
148
  }
132
- function generateModelOperations(models, customError, provider) {
133
- // Check if provider supports createManyAndReturn and updateManyAndReturn
134
- const supportsManyAndReturn = provider === "postgresql" ||
135
- provider === "postgres" ||
136
- provider === "prisma+postgres" ||
137
- provider === "cockroachdb" ||
138
- provider === "sqlite";
149
+ /**
150
+ * Generate type aliases for a model to reduce redundant type computation.
151
+ * TypeScript performance is significantly improved when complex types are
152
+ * computed once and reused via aliases rather than inline.
153
+ */
154
+ function generateModelTypeAliases(models, supportsManyAndReturn) {
155
+ const operations = [
156
+ "findUnique",
157
+ "findUniqueOrThrow",
158
+ "findFirst",
159
+ "findFirstOrThrow",
160
+ "findMany",
161
+ "create",
162
+ "createMany",
163
+ ...(supportsManyAndReturn ? ["createManyAndReturn"] : []),
164
+ "delete",
165
+ "update",
166
+ "deleteMany",
167
+ "updateMany",
168
+ ...(supportsManyAndReturn ? ["updateManyAndReturn"] : []),
169
+ "upsert",
170
+ "count",
171
+ "aggregate",
172
+ "groupBy",
173
+ ];
139
174
  return models
140
175
  .map((model) => {
141
176
  const modelName = model.name;
142
177
  const modelNameCamel = toCamelCase(modelName);
143
- // Type alias for the model delegate (e.g., BasePrismaClient['user'])
178
+ const argsAliases = operations
179
+ .map((op) => `type ${modelName}${capitalize(op)}Args = PrismaNamespace.Args<BasePrismaClient['${modelNameCamel}'], '${op}'>`)
180
+ .join("\n");
181
+ return argsAliases;
182
+ })
183
+ .join("\n\n");
184
+ }
185
+ /**
186
+ * Generate the IPrismaService interface that defines the contract for all Prisma operations.
187
+ * This interface is used to type-check the service implementation and provides explicit types.
188
+ */
189
+ function generatePrismaInterface(models, customError, supportsManyAndReturn) {
190
+ const errorType = customError ? customError.className : "PrismaError";
191
+ // Generate model operation interfaces
192
+ const modelInterfaces = models
193
+ .map((model) => {
194
+ const modelName = model.name;
195
+ const modelNameCamel = toCamelCase(modelName);
144
196
  const delegate = `BasePrismaClient['${modelNameCamel}']`;
197
+ // Helper to get Args type alias
198
+ const argsType = (op) => `${modelName}${capitalize(op)}Args`;
199
+ // Helper for error types based on operation
200
+ const errorTypeFor = (op) => {
201
+ if (customError)
202
+ return customError.className;
203
+ // Map operations to their specific error types
204
+ if (["findUniqueOrThrow", "findFirstOrThrow"].includes(op)) {
205
+ return "PrismaFindOrThrowError";
206
+ }
207
+ else if ([
208
+ "findUnique",
209
+ "findFirst",
210
+ "findMany",
211
+ "count",
212
+ "aggregate",
213
+ "groupBy",
214
+ ].includes(op)) {
215
+ return "PrismaFindError";
216
+ }
217
+ else if (["create", "createMany", "createManyAndReturn"].includes(op)) {
218
+ return "PrismaCreateError";
219
+ }
220
+ else if (["update", "updateMany", "updateManyAndReturn", "upsert"].includes(op)) {
221
+ return "PrismaUpdateError";
222
+ }
223
+ else if (["delete", "deleteMany"].includes(op)) {
224
+ return "PrismaDeleteError";
225
+ }
226
+ return "PrismaError";
227
+ };
228
+ return ` ${modelNameCamel}: {
229
+ findUnique: <A extends ${argsType("findUnique")}>(
230
+ args: A
231
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'findUnique'> | null, ${errorTypeFor("findUnique")}>
232
+
233
+ findUniqueOrThrow: <A extends ${argsType("findUniqueOrThrow")}>(
234
+ args: A
235
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'findUniqueOrThrow'>, ${errorTypeFor("findUniqueOrThrow")}>
236
+
237
+ findFirst: <A extends ${argsType("findFirst")}>(
238
+ args: A
239
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'findFirst'> | null, ${errorTypeFor("findFirst")}>
240
+
241
+ findFirstOrThrow: <A extends ${argsType("findFirstOrThrow")}>(
242
+ args: A
243
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'findFirstOrThrow'>, ${errorTypeFor("findFirstOrThrow")}>
244
+
245
+ findMany: <A extends ${argsType("findMany")}>(
246
+ args?: A
247
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'findMany'>, ${errorTypeFor("findMany")}>
248
+
249
+ create: <A extends ${argsType("create")}>(
250
+ args: A
251
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'create'>, ${errorTypeFor("create")}>
252
+
253
+ createMany: <A extends ${argsType("createMany")}>(
254
+ args: A
255
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'createMany'>, ${errorTypeFor("createMany")}>${supportsManyAndReturn
256
+ ? `
257
+
258
+ createManyAndReturn: <A extends ${argsType("createManyAndReturn")}>(
259
+ args: A
260
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'createManyAndReturn'>, ${errorTypeFor("createManyAndReturn")}>`
261
+ : ""}
262
+
263
+ delete: <A extends ${argsType("delete")}>(
264
+ args: A
265
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'delete'>, ${errorTypeFor("delete")}>
266
+
267
+ update: <A extends ${argsType("update")}>(
268
+ args: A
269
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'update'>, ${errorTypeFor("update")}>
270
+
271
+ deleteMany: <A extends ${argsType("deleteMany")}>(
272
+ args?: A
273
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'deleteMany'>, ${errorTypeFor("deleteMany")}>
274
+
275
+ updateMany: <A extends ${argsType("updateMany")}>(
276
+ args: A
277
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'updateMany'>, ${errorTypeFor("updateMany")}>${supportsManyAndReturn
278
+ ? `
279
+
280
+ updateManyAndReturn: <A extends ${argsType("updateManyAndReturn")}>(
281
+ args: A
282
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'updateManyAndReturn'>, ${errorTypeFor("updateManyAndReturn")}>`
283
+ : ""}
284
+
285
+ upsert: <A extends ${argsType("upsert")}>(
286
+ args: A
287
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'upsert'>, ${errorTypeFor("upsert")}>
288
+
289
+ count: <A extends ${argsType("count")}>(
290
+ args?: A
291
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'count'>, ${errorTypeFor("count")}>
292
+
293
+ aggregate: <A extends ${argsType("aggregate")}>(
294
+ args: A
295
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'aggregate'>, ${errorTypeFor("aggregate")}>
296
+
297
+ groupBy: <A extends ${argsType("groupBy")}>(
298
+ args: A
299
+ ) => EffectType<PrismaNamespace.Result<${delegate}, A, 'groupBy'>, ${errorTypeFor("groupBy")}>
300
+ }`;
301
+ })
302
+ .join("\n\n");
303
+ return `/**
304
+ * Interface defining all Prisma operations with explicit types.
305
+ * This provides a type contract that the service implementation must satisfy.
306
+ */
307
+ export interface IPrismaService {
308
+ client: BasePrismaClient
309
+ // Transaction operations
310
+ $transaction: <R, E, A>(
311
+ effect: EffectType<A, E, R>
312
+ ) => EffectType<A, E | ${errorType}, Exclude<R, PrismaTransactionClientService>>
313
+
314
+ $transactionWith: <R, E, A>(
315
+ effect: EffectType<A, E, R>,
316
+ options: TransactionOptions
317
+ ) => EffectType<A, E | ${errorType}, R>
318
+
319
+ $isolatedTransaction: <R, E, A>(
320
+ effect: EffectType<A, E, R>
321
+ ) => EffectType<A, E | ${errorType}, R>
322
+
323
+ $isolatedTransactionWith: <R, E, A>(
324
+ effect: EffectType<A, E, R>,
325
+ options: TransactionOptions
326
+ ) => EffectType<A, E | ${errorType}, R>
327
+
328
+ // Raw SQL operations
329
+ $executeRaw: (
330
+ args: PrismaNamespace.Sql | [PrismaNamespace.Sql, ...any[]]
331
+ ) => EffectType<number, ${errorType}>
332
+
333
+ $executeRawUnsafe: (
334
+ query: string,
335
+ ...values: any[]
336
+ ) => EffectType<number, ${errorType}>
337
+
338
+ $queryRaw: <T = unknown>(
339
+ args: PrismaNamespace.Sql | [PrismaNamespace.Sql, ...any[]]
340
+ ) => EffectType<T, ${errorType}>
341
+
342
+ $queryRawUnsafe: <T = unknown>(
343
+ query: string,
344
+ ...values: any[]
345
+ ) => EffectType<T, ${errorType}>
346
+
347
+ // Model operations
348
+ ${modelInterfaces}
349
+ }
350
+ `;
351
+ }
352
+ function capitalize(str) {
353
+ return str.charAt(0).toUpperCase() + str.slice(1);
354
+ }
355
+ function generateModelOperations(models, customError, enableTelemetry, supportsManyAndReturn) {
356
+ return models
357
+ .map((model) => {
358
+ const modelName = model.name;
359
+ const modelNameCamel = toCamelCase(modelName);
360
+ // Use pre-computed type alias for delegate
361
+ const _delegate = `BasePrismaClient['${modelNameCamel}']`;
362
+ // Helper to get pre-computed Args type alias
363
+ const _argsType = (op) => `${modelName}${capitalize(op)}Args`;
145
364
  // Cast Promise results to ensure consistent typing across Prisma versions
146
- // This handles Prisma 7's GlobalOmitConfig and works fine with Prisma 6 too
147
- const promiseCast = (op, nullable = false) => {
148
- const resultType = `PrismaNamespace.Result<${delegate}, A, '${op}'>`;
149
- const fullType = nullable ? `${resultType} | null` : resultType;
150
- return ` as Promise<${fullType}>`;
365
+ // This handles Prisma 7's GlobalOmitConfig
366
+ // @deprecated just cast as any now
367
+ const promiseCast = () => {
368
+ return " as any";
151
369
  };
152
370
  // Aggregate/groupBy use complex internal types that need stronger casts
153
- const strongPromiseCast = (op) => {
154
- const resultType = `PrismaNamespace.Result<${delegate}, A, '${op}'>`;
155
- return ` as unknown as Promise<${resultType}>`;
156
- };
157
- const resultType = (op, nullable = false) => {
158
- const baseType = `PrismaNamespace.Result<${delegate}, A, '${op}'>`;
159
- return nullable ? `${baseType} | null` : baseType;
371
+ // @deprecated just cast as any now
372
+ const strongPromiseCast = () => {
373
+ return " as any";
160
374
  };
161
375
  // With custom error: all operations use single error type and mapError
162
376
  // Without custom error: use per-operation error types and mappers
163
377
  const errorType = (opErrorType) => customError ? customError.className : opErrorType;
164
378
  const mapperFn = (defaultMapper) => customError ? "mapError" : defaultMapper;
379
+ // Telemetry: wrap operations with Effect.fn or Effect.fnUntraced
380
+ const wrapTelemetry = (name, generatorFn) => enableTelemetry
381
+ ? `Effect.fn("${name}")(${generatorFn})`
382
+ : `Effect.fnUntraced(${generatorFn})`;
383
+ // Implementation without explicit types - the IPrismaService interface provides all type checking
165
384
  return ` ${modelNameCamel}: {
166
- findUnique: <A extends PrismaNamespace.Args<${delegate}, 'findUnique'>>(
167
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'findUnique'>>
168
- ): Effect.Effect<${resultType("findUnique", true)}, ${errorType("PrismaFindError")}, PrismaClient> =>
169
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
170
- Effect.tryPromise({
171
- try: () => client.${modelNameCamel}.findUnique(args as any)${promiseCast("findUnique", true)},
172
- catch: (error) => ${mapperFn("mapFindError")}(error, "findUnique", "${modelName}")
173
- })
174
- ),
175
-
176
- findUniqueOrThrow: <A extends PrismaNamespace.Args<${delegate}, 'findUniqueOrThrow'>>(
177
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'findUniqueOrThrow'>>
178
- ): Effect.Effect<${resultType("findUniqueOrThrow")}, ${errorType("PrismaFindOrThrowError")}, PrismaClient> =>
179
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
180
- Effect.tryPromise({
181
- try: () => client.${modelNameCamel}.findUniqueOrThrow(args as any)${promiseCast("findUniqueOrThrow")},
182
- catch: (error) => ${mapperFn("mapFindOrThrowError")}(error, "findUniqueOrThrow", "${modelName}")
183
- })
184
- ),
185
-
186
- findFirst: <A extends PrismaNamespace.Args<${delegate}, 'findFirst'> = {}>(
187
- args?: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'findFirst'>>
188
- ): Effect.Effect<${resultType("findFirst", true)}, ${errorType("PrismaFindError")}, PrismaClient> =>
189
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
190
- Effect.tryPromise({
191
- try: () => client.${modelNameCamel}.findFirst(args as any)${promiseCast("findFirst", true)},
192
- catch: (error) => ${mapperFn("mapFindError")}(error, "findFirst", "${modelName}")
193
- })
194
- ),
195
-
196
- findFirstOrThrow: <A extends PrismaNamespace.Args<${delegate}, 'findFirstOrThrow'> = {}>(
197
- args?: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'findFirstOrThrow'>>
198
- ): Effect.Effect<${resultType("findFirstOrThrow")}, ${errorType("PrismaFindOrThrowError")}, PrismaClient> =>
199
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
200
- Effect.tryPromise({
201
- try: () => client.${modelNameCamel}.findFirstOrThrow(args as any)${promiseCast("findFirstOrThrow")},
202
- catch: (error) => ${mapperFn("mapFindOrThrowError")}(error, "findFirstOrThrow", "${modelName}")
203
- })
204
- ),
205
-
206
- findMany: <A extends PrismaNamespace.Args<${delegate}, 'findMany'> = {}>(
207
- args?: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'findMany'>>
208
- ): Effect.Effect<${resultType("findMany")}, ${errorType("PrismaFindError")}, PrismaClient> =>
209
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
210
- Effect.tryPromise({
211
- try: () => client.${modelNameCamel}.findMany(args as any)${promiseCast("findMany")},
212
- catch: (error) => ${mapperFn("mapFindError")}(error, "findMany", "${modelName}")
213
- })
214
- ),
215
-
216
- create: <A extends PrismaNamespace.Args<${delegate}, 'create'>>(
217
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'create'>>
218
- ): Effect.Effect<${resultType("create")}, ${errorType("PrismaCreateError")}, PrismaClient> =>
219
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
220
- Effect.tryPromise({
221
- try: () => client.${modelNameCamel}.create(args as any)${promiseCast("create")},
222
- catch: (error) => ${mapperFn("mapCreateError")}(error, "create", "${modelName}")
223
- })
224
- ),
225
-
226
- createMany: (
227
- args?: PrismaNamespace.Args<${delegate}, 'createMany'>
228
- ): Effect.Effect<PrismaNamespace.BatchPayload, ${errorType("PrismaCreateError")}, PrismaClient> =>
229
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
230
- Effect.tryPromise({
231
- try: () => client.${modelNameCamel}.createMany(args as any),
232
- catch: (error) => ${mapperFn("mapCreateError")}(error, "createMany", "${modelName}")
233
- })
234
- ),${supportsManyAndReturn
385
+ findUnique: ${wrapTelemetry(`Prisma.${modelNameCamel}.findUnique`, `function* (args) {
386
+ const actualClient = yield* clientOrTx(client);
387
+ return yield* Effect.tryPromise<any, ${errorType("PrismaFindError")}>({
388
+ try: () => actualClient.${modelNameCamel}.findUnique(args as any)${promiseCast()},
389
+ catch: (error) => ${mapperFn("mapFindError")}(error, "findUnique", "${modelName}")
390
+ });
391
+ }`)},
392
+
393
+ findUniqueOrThrow: ${wrapTelemetry(`Prisma.${modelNameCamel}.findUniqueOrThrow`, `function* (args) {
394
+ const actualClient = yield* clientOrTx(client);
395
+ return yield* Effect.tryPromise<any, ${errorType("PrismaFindOrThrowError")}>({
396
+ try: () => actualClient.${modelNameCamel}.findUniqueOrThrow(args as any)${promiseCast()},
397
+ catch: (error) => ${mapperFn("mapFindOrThrowError")}(error, "findUniqueOrThrow", "${modelName}")
398
+ });
399
+ }`)},
400
+
401
+ findFirst: ${wrapTelemetry(`Prisma.${modelNameCamel}.findFirst`, `function* (args) {
402
+ const actualClient = yield* clientOrTx(client);
403
+ return yield* Effect.tryPromise<any, ${errorType("PrismaFindError")}>({
404
+ try: () => actualClient.${modelNameCamel}.findFirst(args as any)${promiseCast()},
405
+ catch: (error) => ${mapperFn("mapFindError")}(error, "findFirst", "${modelName}")
406
+ });
407
+ }`)},
408
+
409
+ findFirstOrThrow: ${wrapTelemetry(`Prisma.${modelNameCamel}.findFirstOrThrow`, `function* (args) {
410
+ const actualClient = yield* clientOrTx(client);
411
+ return yield* Effect.tryPromise<any, ${errorType("PrismaFindOrThrowError")}>({
412
+ try: () => actualClient.${modelNameCamel}.findFirstOrThrow(args as any)${promiseCast()},
413
+ catch: (error) => ${mapperFn("mapFindOrThrowError")}(error, "findFirstOrThrow", "${modelName}")
414
+ });
415
+ }`)},
416
+
417
+ findMany: ${wrapTelemetry(`Prisma.${modelNameCamel}.findMany`, `function* (args) {
418
+ const actualClient = yield* clientOrTx(client);
419
+ return yield* Effect.tryPromise<any, ${errorType("PrismaFindError")}>({
420
+ try: () => actualClient.${modelNameCamel}.findMany(args as any)${promiseCast()},
421
+ catch: (error) => ${mapperFn("mapFindError")}(error, "findMany", "${modelName}")
422
+ });
423
+ }`)},
424
+
425
+ create: ${wrapTelemetry(`Prisma.${modelNameCamel}.create`, `function* (args) {
426
+ const actualClient = yield* clientOrTx(client);
427
+ return yield* Effect.tryPromise<any, ${errorType("PrismaCreateError")}>({
428
+ try: () => actualClient.${modelNameCamel}.create(args as any)${promiseCast()},
429
+ catch: (error) => ${mapperFn("mapCreateError")}(error, "create", "${modelName}")
430
+ });
431
+ }`)},
432
+
433
+ createMany: ${wrapTelemetry(`Prisma.${modelNameCamel}.createMany`, `function* (args) {
434
+ const actualClient = yield* clientOrTx(client);
435
+ return yield* Effect.tryPromise<any, ${errorType("PrismaCreateError")}>({
436
+ try: () => actualClient.${modelNameCamel}.createMany(args as any),
437
+ catch: (error) => ${mapperFn("mapCreateError")}(error, "createMany", "${modelName}")
438
+ });
439
+ }`)},${supportsManyAndReturn
235
440
  ? `
236
441
 
237
- createManyAndReturn: <A extends PrismaNamespace.Args<${delegate}, 'createManyAndReturn'>>(
238
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'createManyAndReturn'>>
239
- ): Effect.Effect<${resultType("createManyAndReturn")}, ${errorType("PrismaCreateError")}, PrismaClient> =>
240
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
241
- Effect.tryPromise({
242
- try: () => client.${modelNameCamel}.createManyAndReturn(args as any)${promiseCast("createManyAndReturn")},
243
- catch: (error) => ${mapperFn("mapCreateError")}(error, "createManyAndReturn", "${modelName}")
244
- })
245
- ),`
442
+ createManyAndReturn: ${wrapTelemetry(`Prisma.${modelNameCamel}.createManyAndReturn`, `function* (args) {
443
+ const actualClient = yield* clientOrTx(client);
444
+ return yield* Effect.tryPromise<any, ${errorType("PrismaCreateError")}>({
445
+ try: () => actualClient.${modelNameCamel}.createManyAndReturn(args as any)${promiseCast()},
446
+ catch: (error) => ${mapperFn("mapCreateError")}(error, "createManyAndReturn", "${modelName}")
447
+ });
448
+ }`)},`
246
449
  : ""}
247
450
 
248
- delete: <A extends PrismaNamespace.Args<${delegate}, 'delete'>>(
249
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'delete'>>
250
- ): Effect.Effect<${resultType("delete")}, ${errorType("PrismaDeleteError")}, PrismaClient> =>
251
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
252
- Effect.tryPromise({
253
- try: () => client.${modelNameCamel}.delete(args as any)${promiseCast("delete")},
254
- catch: (error) => ${mapperFn("mapDeleteError")}(error, "delete", "${modelName}")
255
- })
256
- ),
257
-
258
- update: <A extends PrismaNamespace.Args<${delegate}, 'update'>>(
259
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'update'>>
260
- ): Effect.Effect<${resultType("update")}, ${errorType("PrismaUpdateError")}, PrismaClient> =>
261
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
262
- Effect.tryPromise({
263
- try: () => client.${modelNameCamel}.update(args as any)${promiseCast("update")},
264
- catch: (error) => ${mapperFn("mapUpdateError")}(error, "update", "${modelName}")
265
- })
266
- ),
267
-
268
- deleteMany: (
269
- args?: PrismaNamespace.Args<${delegate}, 'deleteMany'>
270
- ): Effect.Effect<PrismaNamespace.BatchPayload, ${errorType("PrismaDeleteManyError")}, PrismaClient> =>
271
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
272
- Effect.tryPromise({
273
- try: () => client.${modelNameCamel}.deleteMany(args as any),
274
- catch: (error) => ${mapperFn("mapDeleteManyError")}(error, "deleteMany", "${modelName}")
275
- })
276
- ),
277
-
278
- updateMany: (
279
- args: PrismaNamespace.Args<${delegate}, 'updateMany'>
280
- ): Effect.Effect<PrismaNamespace.BatchPayload, ${errorType("PrismaUpdateManyError")}, PrismaClient> =>
281
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
282
- Effect.tryPromise({
283
- try: () => client.${modelNameCamel}.updateMany(args as any),
284
- catch: (error) => ${mapperFn("mapUpdateManyError")}(error, "updateMany", "${modelName}")
285
- })
286
- ),${supportsManyAndReturn
451
+ delete: ${wrapTelemetry(`Prisma.${modelNameCamel}.delete`, `function* (args) {
452
+ const actualClient = yield* clientOrTx(client);
453
+ return yield* Effect.tryPromise<any, ${errorType("PrismaDeleteError")}>({
454
+ try: () => actualClient.${modelNameCamel}.delete(args as any)${promiseCast()},
455
+ catch: (error) => ${mapperFn("mapDeleteError")}(error, "delete", "${modelName}")
456
+ });
457
+ }`)},
458
+
459
+ update: ${wrapTelemetry(`Prisma.${modelNameCamel}.update`, `function* (args) {
460
+ const actualClient = yield* clientOrTx(client);
461
+ return yield* Effect.tryPromise<any, ${errorType("PrismaUpdateError")}>({
462
+ try: () => actualClient.${modelNameCamel}.update(args as any)${promiseCast()},
463
+ catch: (error) => ${mapperFn("mapUpdateError")}(error, "update", "${modelName}")
464
+ });
465
+ }`)},
466
+
467
+ deleteMany: ${wrapTelemetry(`Prisma.${modelNameCamel}.deleteMany`, `function* (args) {
468
+ const actualClient = yield* clientOrTx(client);
469
+ return yield* Effect.tryPromise<any, ${errorType("PrismaDeleteManyError")}>({
470
+ try: () => actualClient.${modelNameCamel}.deleteMany(args as any),
471
+ catch: (error) => ${mapperFn("mapDeleteManyError")}(error, "deleteMany", "${modelName}")
472
+ });
473
+ }`)},
474
+
475
+ updateMany: ${wrapTelemetry(`Prisma.${modelNameCamel}.updateMany`, `function* (args) {
476
+ const actualClient = yield* clientOrTx(client);
477
+ return yield* Effect.tryPromise<any, ${errorType("PrismaUpdateManyError")}>({
478
+ try: () => actualClient.${modelNameCamel}.updateMany(args as any),
479
+ catch: (error) => ${mapperFn("mapUpdateManyError")}(error, "updateMany", "${modelName}")
480
+ });
481
+ }`)},${supportsManyAndReturn
287
482
  ? `
288
483
 
289
- updateManyAndReturn: <A extends PrismaNamespace.Args<${delegate}, 'updateManyAndReturn'>>(
290
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'updateManyAndReturn'>>
291
- ): Effect.Effect<${resultType("updateManyAndReturn")}, ${errorType("PrismaUpdateManyError")}, PrismaClient> =>
292
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
293
- Effect.tryPromise({
294
- try: () => client.${modelNameCamel}.updateManyAndReturn(args as any)${promiseCast("updateManyAndReturn")},
295
- catch: (error) => ${mapperFn("mapUpdateManyError")}(error, "updateManyAndReturn", "${modelName}")
296
- })
297
- ),`
484
+ updateManyAndReturn: ${wrapTelemetry(`Prisma.${modelNameCamel}.updateManyAndReturn`, `function* (args) {
485
+ const actualClient = yield* clientOrTx(client);
486
+ return yield* Effect.tryPromise<any, ${errorType("PrismaUpdateManyError")}>({
487
+ try: () => actualClient.${modelNameCamel}.updateManyAndReturn(args as any)${promiseCast()},
488
+ catch: (error) => ${mapperFn("mapUpdateManyError")}(error, "updateManyAndReturn", "${modelName}")
489
+ });
490
+ }`)},`
298
491
  : ""}
299
492
 
300
- upsert: <A extends PrismaNamespace.Args<${delegate}, 'upsert'>>(
301
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'upsert'>>
302
- ): Effect.Effect<${resultType("upsert")}, ${errorType("PrismaCreateError")}, PrismaClient> =>
303
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
304
- Effect.tryPromise({
305
- try: () => client.${modelNameCamel}.upsert(args as any)${promiseCast("upsert")},
306
- catch: (error) => ${mapperFn("mapCreateError")}(error, "upsert", "${modelName}")
307
- })
308
- ),
493
+ upsert: ${wrapTelemetry(`Prisma.${modelNameCamel}.upsert`, `function* (args) {
494
+ const actualClient = yield* clientOrTx(client);
495
+ return yield* Effect.tryPromise<any, ${errorType("PrismaCreateError")}>({
496
+ try: () => actualClient.${modelNameCamel}.upsert(args as any)${promiseCast()},
497
+ catch: (error) => ${mapperFn("mapCreateError")}(error, "upsert", "${modelName}")
498
+ });
499
+ }`)},
309
500
 
310
501
  // Aggregation operations
311
- count: <A extends PrismaNamespace.Args<${delegate}, 'count'> = {}>(
312
- args?: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'count'>>
313
- ): Effect.Effect<${resultType("count")}, ${errorType("PrismaFindError")}, PrismaClient> =>
314
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
315
- Effect.tryPromise({
316
- try: () => client.${modelNameCamel}.count(args as any)${promiseCast("count")},
317
- catch: (error) => ${mapperFn("mapFindError")}(error, "count", "${modelName}")
318
- })
319
- ),
320
-
321
- aggregate: <A extends PrismaNamespace.Args<${delegate}, 'aggregate'>>(
322
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'aggregate'>>
323
- ): Effect.Effect<${resultType("aggregate")}, ${errorType("PrismaFindError")}, PrismaClient> =>
324
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
325
- Effect.tryPromise({
326
- try: () => client.${modelNameCamel}.aggregate(args as any)${strongPromiseCast("aggregate")},
327
- catch: (error) => ${mapperFn("mapFindError")}(error, "aggregate", "${modelName}")
328
- })
329
- ),
330
-
331
- groupBy: <A extends PrismaNamespace.Args<${delegate}, 'groupBy'>>(
332
- args: PrismaNamespace.Exact<A, PrismaNamespace.Args<${delegate}, 'groupBy'>>
333
- ): Effect.Effect<${resultType("groupBy")}, ${errorType("PrismaFindError")}, PrismaClient> =>
334
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
335
- Effect.tryPromise({
336
- try: () => client.${modelNameCamel}.groupBy(args as any)${strongPromiseCast("groupBy")},
337
- catch: (error) => ${mapperFn("mapFindError")}(error, "groupBy", "${modelName}")
338
- })
339
- )
502
+ count: ${wrapTelemetry(`Prisma.${modelNameCamel}.count`, `function* (args) {
503
+ const actualClient = yield* clientOrTx(client);
504
+ return yield* Effect.tryPromise<any, ${errorType("PrismaFindError")}>({
505
+ try: () => actualClient.${modelNameCamel}.count(args as any)${promiseCast()},
506
+ catch: (error) => ${mapperFn("mapFindError")}(error, "count", "${modelName}")
507
+ });
508
+ }`)},
509
+
510
+ aggregate: ${wrapTelemetry(`Prisma.${modelNameCamel}.aggregate`, `function* (args) {
511
+ const actualClient = yield* clientOrTx(client);
512
+ return yield* Effect.tryPromise<any, ${errorType("PrismaFindError")}>({
513
+ try: () => actualClient.${modelNameCamel}.aggregate(args as any)${strongPromiseCast()},
514
+ catch: (error) => ${mapperFn("mapFindError")}(error, "aggregate", "${modelName}")
515
+ });
516
+ }`)},
517
+
518
+ groupBy: ${wrapTelemetry(`Prisma.${modelNameCamel}.groupBy`, `function* (args) {
519
+ const actualClient = yield* clientOrTx(client);
520
+ return yield* Effect.tryPromise<any, ${errorType("PrismaFindError")}>({
521
+ try: () => actualClient.${modelNameCamel}.groupBy(args as any)${strongPromiseCast()},
522
+ catch: (error) => ${mapperFn("mapFindError")}(error, "groupBy", "${modelName}")
523
+ });
524
+ }`)}
340
525
  }`;
341
526
  })
342
527
  .join(",\n\n");
@@ -351,27 +536,48 @@ function parseErrorImportPath(errorImportPath) {
351
536
  }
352
537
  return { path, className };
353
538
  }
354
- async function generateUnifiedService(models, outputDir, clientImportPath, errorImportPath, provider) {
539
+ async function generateUnifiedService(models, outputDir, clientImportPath, errorImportPath, enableTelemetry, supportsManyAndReturn) {
355
540
  const customError = parseErrorImportPath(errorImportPath);
356
- const rawSqlOperations = generateRawSqlOperations(customError);
357
- const modelOperations = generateModelOperations(models, customError, provider);
541
+ const rawSqlOperations = generateRawSqlOperations(customError, enableTelemetry);
542
+ const modelTypeAliases = generateModelTypeAliases(models, supportsManyAndReturn);
543
+ const prismaInterface = generatePrismaInterface(models, customError, supportsManyAndReturn);
544
+ const modelOperations = generateModelOperations(models, customError, enableTelemetry, supportsManyAndReturn);
358
545
  // Generate different content based on whether custom error is configured
359
546
  const serviceContent = customError
360
- ? generateCustomErrorService(customError, clientImportPath, rawSqlOperations, modelOperations)
361
- : generateDefaultErrorService(clientImportPath, rawSqlOperations, modelOperations);
362
- await promises_1.default.writeFile(node_path_1.default.join(outputDir, "index.ts"), serviceContent);
547
+ ? generateCustomErrorService(customError, clientImportPath, rawSqlOperations, modelTypeAliases, prismaInterface, modelOperations, enableTelemetry)
548
+ : generateDefaultErrorService(clientImportPath, rawSqlOperations, modelTypeAliases, prismaInterface, modelOperations, enableTelemetry);
549
+ const outputPath = node_path_1.default.join(outputDir, "index.ts");
550
+ await promises_1.default.writeFile(outputPath, serviceContent);
551
+ // Format the generated file with Biome
552
+ try {
553
+ (0, node_child_process_1.execSync)(`npx @biomejs/biome format --write "${outputPath}"`, {
554
+ stdio: "inherit",
555
+ cwd: process.cwd(),
556
+ });
557
+ }
558
+ catch (error) {
559
+ console.warn("Warning: Failed to format generated code with Biome:", error);
560
+ }
363
561
  }
364
562
  /**
365
563
  * Generate service with custom user-provided error class.
366
564
  * All operations use a single error type and a simple mapError function.
367
565
  */
368
- function generateCustomErrorService(customError, clientImportPath, rawSqlOperations, modelOperations) {
566
+ function generateCustomErrorService(customError, clientImportPath, rawSqlOperations, modelTypeAliases, prismaInterface, modelOperations, enableTelemetry) {
567
+ const _errorType = customError.className;
369
568
  return `${header}
370
- import { Context, Effect, Exit, Layer } from "effect"
371
- import { Service } from "effect/Effect"
569
+ import { Effect, Exit, Layer, Option, Scope, ServiceMap } from "effect"
570
+ import type { Effect as EffectType, Error as EffectError, Services as EffectServices, Success as EffectSuccess } from "effect/Effect"
372
571
  import { Prisma as PrismaNamespace, PrismaClient as BasePrismaClient } from "${clientImportPath}"
373
572
  import { ${customError.className}, mapPrismaError } from "${customError.path}"
374
573
 
574
+ // ============================================================================
575
+ // Type aliases for model operations (performance optimization)
576
+ // These are computed once and reused, reducing TypeScript's type-checking workload
577
+ // ============================================================================
578
+
579
+ ${modelTypeAliases}
580
+
375
581
  // Symbol used to identify intentional rollbacks vs actual errors
376
582
  const ROLLBACK = Symbol.for("prisma.effect.rollback")
377
583
 
@@ -390,59 +596,44 @@ type TransactionOptions = {
390
596
 
391
597
  /**
392
598
  * Context tag for the Prisma client instance.
393
- * Holds the transaction client (tx) and root client.
394
599
  *
395
600
  * Use \`PrismaClient.layer()\` or \`PrismaClient.layerEffect()\` to create a layer.
396
601
  *
397
602
  * @example
398
- * // Prisma 6 - all options are optional
399
- * const layer = PrismaClient.layer({ datasourceUrl: "..." })
400
- *
401
603
  * // Prisma 7 - adapter or accelerateUrl is required
402
604
  * const layer = PrismaClient.layer({ adapter: myAdapter })
403
605
  *
404
606
  * // With transaction options (Prisma uses these as defaults for $transaction)
405
607
  * const layer = PrismaClient.layer({
406
- * adapter: myAdapter, // or datasourceUrl for Prisma 6
608
+ * adapter: myAdapter,
407
609
  * transactionOptions: { isolationLevel: "Serializable", timeout: 10000 }
408
610
  * })
409
611
  */
410
- export class PrismaClient extends Context.Tag("PrismaClient")<
411
- PrismaClient,
412
- {
413
- tx: BasePrismaClient | PrismaNamespace.TransactionClient
414
- client: BasePrismaClient
415
- }
416
- >() {
612
+ export class PrismaClient extends ServiceMap.Service<PrismaClient, BasePrismaClient>()("PrismaClient") {
417
613
  /**
418
614
  * Create a PrismaClient layer with the given options.
419
615
  * The client will be automatically disconnected when the layer scope ends.
420
616
  *
421
617
  * Pass options directly - the signature matches PrismaClient's constructor.
422
- * Prisma 6: all options are optional
423
- * Prisma 7: requires either \`adapter\` or \`accelerateUrl\`
618
+ * Prisma 7: requires either \`adapter\`
424
619
  *
425
620
  * @example
426
- * // Prisma 6
427
- * const layer = PrismaClient.layer({ datasourceUrl: process.env.DATABASE_URL })
428
- *
429
621
  * // Prisma 7 with adapter
430
622
  * const layer = PrismaClient.layer({ adapter: myAdapter })
431
- *
432
623
  * // With transaction options
433
624
  * const layer = PrismaClient.layer({
434
625
  * adapter: myAdapter,
435
626
  * transactionOptions: { isolationLevel: "Serializable" }
436
627
  * })
437
628
  */
438
- static layer = (
629
+ static layer: (
439
630
  ...args: ConstructorParameters<typeof BasePrismaClient>
440
- ) => Layer.scoped(
631
+ ) => Layer.Layer<PrismaClient, never, never> = (...args) => Layer.effect(
441
632
  PrismaClient,
442
633
  Effect.gen(function* () {
443
- const prisma = new BasePrismaClient(...args)
634
+ const prisma: BasePrismaClient = new BasePrismaClient(...args)
444
635
  yield* Effect.addFinalizer(() => Effect.promise(() => prisma.$disconnect()))
445
- return { tx: prisma, client: prisma }
636
+ return prisma
446
637
  })
447
638
  )
448
639
 
@@ -470,25 +661,78 @@ export class PrismaClient extends Context.Tag("PrismaClient")<
470
661
  * })
471
662
  * )
472
663
  */
473
- static layerEffect = <R, E>(
474
- optionsEffect: Effect.Effect<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
475
- ) => Layer.scoped(
664
+ static layerEffect: <R, E>(
665
+ optionsEffect: EffectType<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
666
+ ) => Layer.Layer<PrismaClient, E, Exclude<R, Scope.Scope>> = (optionsEffect) => Layer.effect(
476
667
  PrismaClient,
477
668
  Effect.gen(function* () {
478
- const options = yield* optionsEffect
479
- const prisma = new BasePrismaClient(options)
669
+ const options: ConstructorParameters<typeof BasePrismaClient>[0] = yield* optionsEffect
670
+ const prisma: BasePrismaClient = new BasePrismaClient(options)
480
671
  yield* Effect.addFinalizer(() => Effect.promise(() => prisma.$disconnect()))
481
- return { tx: prisma, client: prisma }
672
+ return prisma
482
673
  })
483
674
  )
484
675
  }
485
676
 
677
+ /**
678
+ * Context tag for the transaction client within a transaction scope.
679
+ * This service is only available inside \`$transaction\` calls.
680
+ * Use \`Effect.serviceOption(PrismaTransactionClientService)\` to check if you're in a transaction.
681
+ */
682
+ export class PrismaTransactionClientService extends ServiceMap.Service<PrismaTransactionClientService, PrismaNamespace.TransactionClient>()("PrismaTransactionClientService") {}
683
+
486
684
  // Re-export the custom error type for convenience
487
685
  export { ${customError.className} }
488
686
 
489
687
  // Use the user-provided error mapper
490
688
  const mapError = mapPrismaError
491
689
 
690
+ /**
691
+ * Helper to get the current client - either the transaction client if in a transaction,
692
+ * or the root client if not. Uses Effect.serviceOption to detect transaction context.
693
+ */
694
+ const clientOrTx = (client: BasePrismaClient) => Effect.map(
695
+ Effect.serviceOption(PrismaTransactionClientService),
696
+ Option.getOrElse(() => client),
697
+ );
698
+
699
+ /**
700
+ * Like Effect.acquireUseRelease, but allows the release function to fail.
701
+ * Release errors are surfaced in the error channel instead of becoming defects.
702
+ *
703
+ * Key properties:
704
+ * - The release function is always called, even if use fails
705
+ * - The release function is uninterruptible to ensure cleanup completes
706
+ * - Release errors are surfaced in the error channel, not as defects
707
+ */
708
+ const acquireUseReleaseWithErrors = <A, E, R, A2, E2, R2, X, E3, R3>(
709
+ acquire: EffectType<A, E, R>,
710
+ use: (a: A) => EffectType<A2, E2, R2>,
711
+ release: (a: A, exit: Exit.Exit<A2, E2>) => EffectType<X, E3, R3>
712
+ ): EffectType<A2, E | E2 | E3, R | R2 | R3> =>
713
+ Effect.uninterruptibleMask((restore) =>
714
+ Effect.flatMap(acquire, (a) =>
715
+ Effect.flatMap(
716
+ Effect.exit(restore(use(a))),
717
+ (exit) =>
718
+ Effect.flatMap(
719
+ // Make release uninterruptible to ensure cleanup always completes
720
+ Effect.exit(release(a, exit)),
721
+ (releaseExit) => {
722
+ if (Exit.isFailure(releaseExit)) {
723
+ // Release failed - surface the release error
724
+ return releaseExit as any;
725
+ }
726
+ // Release succeeded - return the original use result
727
+ return exit as any;
728
+ }
729
+ )
730
+ )
731
+ )
732
+ );
733
+
734
+ ${prismaInterface}
735
+
492
736
  /**
493
737
  * Internal helper to begin a callback-free interactive transaction.
494
738
  * Returns a transaction client with $commit and $rollback methods.
@@ -501,8 +745,8 @@ const $begin = (
501
745
  timeout?: number
502
746
  isolationLevel?: PrismaNamespace.TransactionIsolationLevel
503
747
  }
504
- ): Effect.Effect<FlatTransactionClient, ${customError.className}> =>
505
- Effect.async<FlatTransactionClient, ${customError.className}>((resume) => {
748
+ ): EffectType<FlatTransactionClient, ${customError.className}> =>
749
+ Effect.callback<FlatTransactionClient, ${customError.className}>((resume) => {
506
750
  let setTxClient: (txClient: PrismaNamespace.TransactionClient) => void
507
751
  let commit: () => void
508
752
  let rollback: () => void
@@ -554,15 +798,14 @@ const $begin = (
554
798
  * return user
555
799
  * })
556
800
  *
557
- * // Run with default layer (Prisma 6)
558
- * Effect.runPromise(program.pipe(Effect.provide(Prisma.Live)))
559
- *
560
- * // Or with custom options
801
+ * // Run with layer
561
802
  * Effect.runPromise(program.pipe(Effect.provide(Prisma.layer({ datasourceUrl: "..." }))))
562
803
  */
563
- export class Prisma extends Service<Prisma>()("Prisma", {
564
- effect: Effect.gen(function* () {
565
- return {
804
+ const makePrismaService = Effect.gen(function* () {
805
+ const client = yield* PrismaClient;
806
+
807
+ const prismaService: IPrismaService = {
808
+ client,
566
809
  /**
567
810
  * Execute an effect within a database transaction.
568
811
  * All operations within the effect will be atomic - they either all succeed or all fail.
@@ -570,7 +813,8 @@ export class Prisma extends Service<Prisma>()("Prisma", {
570
813
  * This implementation uses a callback-free transaction pattern that keeps the effect
571
814
  * running in the same fiber as the parent, preserving Ref, FiberRef, and Context access.
572
815
  *
573
- * Options passed here override any defaults set via TransactionConfig layer.
816
+ * Uses default transaction options from PrismaClient constructor.
817
+ * For custom options, use \`$transactionWith\`.
574
818
  *
575
819
  * @example
576
820
  * const result = yield* prisma.$transaction(
@@ -580,47 +824,101 @@ export class Prisma extends Service<Prisma>()("Prisma", {
580
824
  * return user
581
825
  * })
582
826
  * )
827
+ */
828
+ $transaction: ${enableTelemetry ? 'Effect.fn("Prisma.$transaction")' : "Effect.fnUntraced"}(function* (effect) {
829
+ const currentTx = yield* Effect.serviceOption(PrismaTransactionClientService);
830
+
831
+ // If already in a transaction, just run the effect
832
+ if (Option.isSome(currentTx)) {
833
+ // have to get this to pass the Exclude type
834
+ return yield* (effect as EffectType<
835
+ EffectSuccess<typeof effect>,
836
+ EffectError<typeof effect>,
837
+ Exclude<
838
+ EffectServices<typeof effect>,
839
+ PrismaTransactionClientService
840
+ >
841
+ >);
842
+ }
843
+
844
+ // Otherwise, start a new transaction
845
+ return yield* acquireUseReleaseWithErrors(
846
+ // Acquire: begin a new transaction with default options
847
+ $begin(client),
848
+
849
+ // Use: run the effect with the transaction client injected
850
+ (txClient) =>
851
+ effect.pipe(
852
+ Effect.provideService(PrismaTransactionClientService, txClient)
853
+ ),
854
+
855
+ // Release: commit on success, rollback on failure/interruption
856
+ (txClient, exit) =>
857
+ Exit.isSuccess(exit)
858
+ ? Effect.tryPromise({
859
+ try: () => txClient.$commit(),
860
+ catch: (error) => mapError(error, "$commit", "Prisma")
861
+ }).pipe(Effect.withSpan("txClient.$commit"))
862
+ : Effect.tryPromise({
863
+ try: () => txClient.$rollback(),
864
+ catch: (error) => mapError(error, "$rollback", "Prisma")
865
+ }).pipe(Effect.withSpan("txClient.$rollback"))
866
+ );
867
+ }),
868
+
869
+ /**
870
+ * Execute an effect within a database transaction with custom options.
871
+ * All operations within the effect will be atomic - they either all succeed or all fail.
872
+ *
873
+ * This implementation uses a callback-free transaction pattern that keeps the effect
874
+ * running in the same fiber as the parent, preserving Ref, FiberRef, and Context access.
875
+ *
876
+ * Options passed here override any defaults set in PrismaClient constructor.
583
877
  *
584
878
  * @example
585
879
  * // Override default isolation level for this transaction
586
- * const result = yield* prisma.$transaction(myEffect, {
587
- * isolationLevel: "ReadCommitted"
588
- * })
880
+ * const result = yield* prisma.$transactionWith(
881
+ * Effect.gen(function* () {
882
+ * const user = yield* prisma.user.create({ data: { name: "Alice" } })
883
+ * yield* prisma.post.create({ data: { title: "Hello", authorId: user.id } })
884
+ * return user
885
+ * }),
886
+ * { isolationLevel: "ReadCommitted", timeout: 10000 }
887
+ * )
589
888
  */
590
- $transaction: <R, E, A>(
591
- effect: Effect.Effect<A, E, R>,
592
- options?: TransactionOptions
593
- ) =>
594
- Effect.flatMap(
595
- PrismaClient,
596
- ({ client, tx }): Effect.Effect<A, E | ${customError.className}, R> => {
597
- // If we're already in a transaction, just run the effect directly (no nesting)
598
- const isRootClient = "$transaction" in tx
599
- if (!isRootClient) {
600
- return effect
601
- }
889
+ $transactionWith: ${enableTelemetry ? 'Effect.fn("Prisma.$transactionWith")' : "Effect.fnUntraced"}(function* (effect, options) {
890
+ const currentTx = yield* Effect.serviceOption(PrismaTransactionClientService);
602
891
 
603
- // Use acquireUseRelease to manage the transaction lifecycle
604
- // This keeps everything in the same fiber, preserving Ref/FiberRef/Context
605
- return Effect.acquireUseRelease(
606
- // Acquire: begin a new transaction
607
- // Prisma merges per-call options with constructor defaults internally
608
- $begin(client, options),
609
-
610
- // Use: run the effect with the transaction client injected
611
- (txClient) =>
612
- effect.pipe(
613
- Effect.provideService(PrismaClient, { tx: txClient, client })
614
- ),
615
-
616
- // Release: commit on success, rollback on failure/interruption
617
- (txClient, exit) =>
618
- Exit.isSuccess(exit)
619
- ? Effect.promise(() => txClient.$commit())
620
- : Effect.promise(() => txClient.$rollback())
621
- )
892
+ // If already in a transaction, just run the effect
893
+ if (Option.isSome(currentTx)) {
894
+ return yield* effect;
622
895
  }
623
- ),
896
+
897
+ // Otherwise, start a new transaction
898
+ return yield* acquireUseReleaseWithErrors(
899
+ // Acquire: begin a new transaction
900
+ // Prisma merges per-call options with constructor defaults internally
901
+ $begin(client, options),
902
+
903
+ // Use: run the effect with the transaction client injected
904
+ (txClient) =>
905
+ effect.pipe(
906
+ Effect.provideService(PrismaTransactionClientService, txClient)
907
+ ),
908
+
909
+ // Release: commit on success, rollback on failure/interruption
910
+ (txClient, exit) =>
911
+ Exit.isSuccess(exit)
912
+ ? Effect.tryPromise({
913
+ try: () => txClient.$commit(),
914
+ catch: (error) => mapError(error, "$commit", "Prisma")
915
+ }).pipe(Effect.withSpan("txClient.$rollback"))
916
+ : Effect.tryPromise({
917
+ try: () => txClient.$rollback(),
918
+ catch: (error) => mapError(error, "$rollback", "Prisma")
919
+ }).pipe(Effect.withSpan("txClient.$rollback"))
920
+ );
921
+ }),
624
922
 
625
923
  /**
626
924
  * Execute an effect in a NEW transaction, even if already inside a transaction.
@@ -634,6 +932,9 @@ export class Prisma extends Service<Prisma>()("Prisma", {
634
932
  * ⚠️ WARNING: The isolated transaction can commit while the parent rolls back,
635
933
  * or vice versa. Use carefully to avoid data inconsistencies.
636
934
  *
935
+ * Uses default transaction options from PrismaClient constructor.
936
+ * For custom options, use \`$isolatedTransactionWith\`.
937
+ *
637
938
  * @example
638
939
  * yield* prisma.$transaction(
639
940
  * Effect.gen(function* () {
@@ -646,46 +947,99 @@ export class Prisma extends Service<Prisma>()("Prisma", {
646
947
  * })
647
948
  * )
648
949
  */
649
- $isolatedTransaction: <R, E, A>(
650
- effect: Effect.Effect<A, E, R>,
651
- options?: TransactionOptions
652
- ) =>
653
- Effect.flatMap(
654
- PrismaClient,
655
- ({ client }): Effect.Effect<A, E | ${customError.className}, R> => {
656
- // Always use the root client to create a fresh transaction
657
- return Effect.acquireUseRelease(
658
- $begin(client, options),
659
- (txClient) =>
660
- effect.pipe(
661
- Effect.provideService(PrismaClient, { tx: txClient, client })
662
- ),
663
- (txClient, exit) =>
664
- Exit.isSuccess(exit)
665
- ? Effect.promise(() => txClient.$commit())
666
- : Effect.promise(() => txClient.$rollback())
667
- )
668
- }
669
- ),
950
+ $isolatedTransaction: ${enableTelemetry ? 'Effect.fn("Prisma.$isolatedTransaction")' : "Effect.fnUntraced"}(function* (effect) {
951
+ // Always create a fresh transaction
952
+ return yield* acquireUseReleaseWithErrors(
953
+ $begin(client),
954
+ (txClient) =>
955
+ effect.pipe(
956
+ Effect.provideService(PrismaTransactionClientService, txClient)
957
+ ),
958
+ (txClient, exit) =>
959
+ Exit.isSuccess(exit)
960
+ ? Effect.tryPromise({
961
+ try: () => txClient.$commit(),
962
+ catch: (error) => mapError(error, "$commit", "Prisma")
963
+ }).pipe(Effect.withSpan("txClient.$rollback"))
964
+ : Effect.tryPromise({
965
+ try: () => txClient.$rollback(),
966
+ catch: (error) => mapError(error, "$rollback", "Prisma")
967
+ }).pipe(Effect.withSpan("txClient.$rollback"))
968
+ );
969
+ }),
970
+
971
+ /**
972
+ * Execute an effect in a NEW transaction with custom options, even if already inside a transaction.
973
+ * Unlike \`$transaction\`, this always creates a fresh, independent transaction.
974
+ *
975
+ * Use this for operations that should NOT be rolled back with the parent:
976
+ * - Audit logging that must persist even if main operation fails
977
+ * - Saga pattern where each step has independent commit/rollback
978
+ * - Background job queuing that should commit immediately
979
+ *
980
+ * ⚠️ WARNING: The isolated transaction can commit while the parent rolls back,
981
+ * or vice versa. Use carefully to avoid data inconsistencies.
982
+ *
983
+ * Options passed here override any defaults set in PrismaClient constructor.
984
+ *
985
+ * @example
986
+ * yield* prisma.$transaction(
987
+ * Effect.gen(function* () {
988
+ * // This audit log commits independently with custom isolation level
989
+ * yield* prisma.$isolatedTransactionWith(
990
+ * prisma.auditLog.create({ data: { action: "attempt", userId } }),
991
+ * { isolationLevel: "Serializable" }
992
+ * )
993
+ * // Main operation - if this fails, audit log is still committed
994
+ * yield* prisma.user.delete({ where: { id: userId } })
995
+ * })
996
+ * )
997
+ */
998
+ $isolatedTransactionWith: ${enableTelemetry ? 'Effect.fn("Prisma.$isolatedTransactionWith")' : "Effect.fnUntraced"}(function* (effect, options) {
999
+ // Always create a fresh transaction
1000
+ return yield* acquireUseReleaseWithErrors(
1001
+ $begin(client, options),
1002
+ (txClient) =>
1003
+ effect.pipe(
1004
+ Effect.provideService(PrismaTransactionClientService, txClient)
1005
+ ),
1006
+ (txClient, exit) =>
1007
+ Exit.isSuccess(exit)
1008
+ ? Effect.tryPromise({
1009
+ try: () => txClient.$commit(),
1010
+ catch: (error) => mapError(error, "$commit", "Prisma")
1011
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1012
+ : Effect.tryPromise({
1013
+ try: () => txClient.$rollback(),
1014
+ catch: (error) => mapError(error, "$rollback", "Prisma")
1015
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1016
+ );
1017
+ }),
670
1018
  ${rawSqlOperations}
671
1019
 
672
1020
  ${modelOperations}
673
- }
674
- })
675
- }) {
1021
+ };
1022
+
1023
+ return prismaService;
1024
+ });
1025
+
1026
+ export class Prisma extends ServiceMap.Service<Prisma, IPrismaService>()("Prisma") {
1027
+ /**
1028
+ * Effect that constructs the Prisma service.
1029
+ * Used internally by layer constructors.
1030
+ */
1031
+ static make: EffectType<IPrismaService, never, PrismaClient> = makePrismaService;
1032
+ static Default: Layer.Layer<Prisma, never, PrismaClient> = Layer.effect(Prisma, this.make);
1033
+
676
1034
  /**
677
1035
  * Create a complete Prisma layer with the given PrismaClient options.
678
1036
  * This is the recommended way to create a Prisma layer - it bundles both
679
1037
  * PrismaClient and Prisma service together.
680
1038
  *
681
1039
  * Pass options directly - the signature matches PrismaClient's constructor.
682
- * Prisma 6: all options are optional
683
1040
  * Prisma 7: requires either \`adapter\` or \`accelerateUrl\`
684
1041
  *
685
1042
  * @example
686
- * // Prisma 6
687
- * const MainLayer = Prisma.layer({ datasourceUrl: process.env.DATABASE_URL })
688
- *
689
1043
  * // Prisma 7 with adapter
690
1044
  * const MainLayer = Prisma.layer({ adapter: myAdapter })
691
1045
  *
@@ -698,9 +1052,11 @@ export class Prisma extends Service<Prisma>()("Prisma", {
698
1052
  * // Use it
699
1053
  * Effect.runPromise(program.pipe(Effect.provide(MainLayer)))
700
1054
  */
701
- static layer = (
1055
+ static layer: (
702
1056
  ...args: ConstructorParameters<typeof BasePrismaClient>
703
- ) => Layer.merge(PrismaClient.layer(...args), Prisma.Default)
1057
+ ) => Layer.Layer<Prisma | PrismaClient, never, never> = (...args) => this.Default.pipe(
1058
+ Layer.provideMerge(PrismaClient.layer(...args))
1059
+ );
704
1060
 
705
1061
  /**
706
1062
  * Create a complete Prisma layer where PrismaClient options are computed via an Effect.
@@ -725,23 +1081,42 @@ export class Prisma extends Service<Prisma>()("Prisma", {
725
1081
  * })
726
1082
  * )
727
1083
  */
728
- static layerEffect = <R, E>(
729
- optionsEffect: Effect.Effect<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
730
- ) => Layer.merge(PrismaClient.layerEffect(optionsEffect), Prisma.Default)
731
-
1084
+ static layerEffect: <R, E>(
1085
+ optionsEffect: EffectType<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
1086
+ ) => Layer.Layer<Prisma | PrismaClient, E, Exclude<R, Scope.Scope>> = (optionsEffect) => this.Default.pipe(
1087
+ Layer.provideMerge(PrismaClient.layerEffect(optionsEffect))
1088
+ );
732
1089
  }
1090
+
733
1091
  `;
734
1092
  }
735
1093
  /**
736
1094
  * Generate service with default tagged error classes.
737
1095
  * Operations have per-operation error types for fine-grained error handling.
738
1096
  */
739
- function generateDefaultErrorService(clientImportPath, rawSqlOperations, modelOperations) {
1097
+ function generateDefaultErrorService(clientImportPath, rawSqlOperations, modelTypeAliases, prismaInterface, modelOperations, enableTelemetry) {
1098
+ const _errorType = "PrismaError";
740
1099
  return `${header}
741
- import { Context, Data, Effect, Exit, Layer } from "effect"
742
- import { Service } from "effect/Effect"
1100
+ import { Data, Effect, Exit, Layer, Option, Scope, ServiceMap } from "effect"
1101
+ import type { Effect as EffectType, Error as EffectError, Services as EffectServices, Success as EffectSuccess } from "effect/Effect"
743
1102
  import { Prisma as PrismaNamespace, PrismaClient as BasePrismaClient } from "${clientImportPath}"
744
1103
 
1104
+ // Create local reference to error class for proper type narrowing
1105
+ const PrismaClientKnownRequestError = PrismaNamespace.PrismaClientKnownRequestError
1106
+
1107
+ // Type guard function to help TypeScript narrow the error type
1108
+ // This is needed for Prisma 7 which re-exports error classes from external packages
1109
+ function isPrismaClientKnownRequestError(error: unknown): error is PrismaNamespace.PrismaClientKnownRequestError {
1110
+ return error instanceof PrismaClientKnownRequestError
1111
+ }
1112
+
1113
+ // ============================================================================
1114
+ // Type aliases for model operations (performance optimization)
1115
+ // These are computed once and reused, reducing TypeScript's type-checking workload
1116
+ // ============================================================================
1117
+
1118
+ ${modelTypeAliases}
1119
+
745
1120
  // Symbol used to identify intentional rollbacks vs actual errors
746
1121
  const ROLLBACK = Symbol.for("prisma.effect.rollback")
747
1122
 
@@ -760,42 +1135,28 @@ type TransactionOptions = {
760
1135
 
761
1136
  /**
762
1137
  * Context tag for the Prisma client instance.
763
- * Holds the transaction client (tx) and root client.
764
1138
  *
765
1139
  * Use \`PrismaClient.layer()\` or \`PrismaClient.layerEffect()\` to create a layer.
766
1140
  *
767
1141
  * @example
768
- * // Prisma 6 - all options are optional
769
- * const layer = PrismaClient.layer({ datasourceUrl: "..." })
770
- *
771
1142
  * // Prisma 7 - adapter or accelerateUrl is required
772
1143
  * const layer = PrismaClient.layer({ adapter: myAdapter })
773
1144
  *
774
1145
  * // With transaction options (Prisma uses these as defaults for $transaction)
775
1146
  * const layer = PrismaClient.layer({
776
- * adapter: myAdapter, // or datasourceUrl for Prisma 6
1147
+ * adapter: myAdapter,
777
1148
  * transactionOptions: { isolationLevel: "Serializable", timeout: 10000 }
778
1149
  * })
779
1150
  */
780
- export class PrismaClient extends Context.Tag("PrismaClient")<
781
- PrismaClient,
782
- {
783
- tx: BasePrismaClient | PrismaNamespace.TransactionClient
784
- client: BasePrismaClient
785
- }
786
- >() {
1151
+ export class PrismaClient extends ServiceMap.Service<PrismaClient, BasePrismaClient>()("PrismaClient") {
787
1152
  /**
788
1153
  * Create a PrismaClient layer with the given options.
789
1154
  * The client will be automatically disconnected when the layer scope ends.
790
1155
  *
791
1156
  * Pass options directly - the signature matches PrismaClient's constructor.
792
- * Prisma 6: all options are optional
793
1157
  * Prisma 7: requires either \`adapter\` or \`accelerateUrl\`
794
1158
  *
795
1159
  * @example
796
- * // Prisma 6
797
- * const layer = PrismaClient.layer({ datasourceUrl: process.env.DATABASE_URL })
798
- *
799
1160
  * // Prisma 7 with adapter
800
1161
  * const layer = PrismaClient.layer({ adapter: myAdapter })
801
1162
  *
@@ -805,14 +1166,14 @@ export class PrismaClient extends Context.Tag("PrismaClient")<
805
1166
  * transactionOptions: { isolationLevel: "Serializable" }
806
1167
  * })
807
1168
  */
808
- static layer = (
1169
+ static layer: (
809
1170
  ...args: ConstructorParameters<typeof BasePrismaClient>
810
- ) => Layer.scoped(
1171
+ ) => Layer.Layer<PrismaClient, never, never> = (...args) => Layer.effect(
811
1172
  PrismaClient,
812
1173
  Effect.gen(function* () {
813
- const prisma = new BasePrismaClient(...args)
1174
+ const prisma: BasePrismaClient = new BasePrismaClient(...args)
814
1175
  yield* Effect.addFinalizer(() => Effect.promise(() => prisma.$disconnect()))
815
- return { tx: prisma, client: prisma }
1176
+ return prisma
816
1177
  })
817
1178
  )
818
1179
 
@@ -840,19 +1201,26 @@ export class PrismaClient extends Context.Tag("PrismaClient")<
840
1201
  * })
841
1202
  * )
842
1203
  */
843
- static layerEffect = <R, E>(
844
- optionsEffect: Effect.Effect<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
845
- ) => Layer.scoped(
1204
+ static layerEffect: <R, E>(
1205
+ optionsEffect: EffectType<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
1206
+ ) => Layer.Layer<PrismaClient, E, Exclude<R, Scope.Scope>> = (optionsEffect) => Layer.effect(
846
1207
  PrismaClient,
847
1208
  Effect.gen(function* () {
848
- const options = yield* optionsEffect
849
- const prisma = new BasePrismaClient(options)
1209
+ const options: ConstructorParameters<typeof BasePrismaClient>[0] = yield* optionsEffect
1210
+ const prisma: BasePrismaClient = new BasePrismaClient(options)
850
1211
  yield* Effect.addFinalizer(() => Effect.promise(() => prisma.$disconnect()))
851
- return { tx: prisma, client: prisma }
1212
+ return prisma
852
1213
  })
853
1214
  )
854
1215
  }
855
1216
 
1217
+ /**
1218
+ * Context tag for the transaction client within a transaction scope.
1219
+ * This service is only available inside \`$transaction\` calls.
1220
+ * Use \`Effect.serviceOption(PrismaTransactionClientService)\` to check if you're in a transaction.
1221
+ */
1222
+ export class PrismaTransactionClientService extends ServiceMap.Service<PrismaTransactionClientService, PrismaNamespace.TransactionClient>()("PrismaTransactionClientService") {}
1223
+
856
1224
  export class PrismaUniqueConstraintError extends Data.TaggedError("PrismaUniqueConstraintError")<{
857
1225
  cause: PrismaNamespace.PrismaClientKnownRequestError
858
1226
  operation: string
@@ -998,8 +1366,9 @@ export type PrismaError =
998
1366
 
999
1367
  // Generic mapper for raw operations and fallback
1000
1368
  const mapError = (error: unknown, operation: string, model: string): PrismaError => {
1001
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1002
- switch (error.code) {
1369
+ if (isPrismaClientKnownRequestError(error)) {
1370
+ const knownError = error;
1371
+ switch (knownError.code) {
1003
1372
  case "P2000":
1004
1373
  return new PrismaValueTooLongError({ cause: error, operation, model });
1005
1374
  case "P2002":
@@ -1038,8 +1407,9 @@ const mapError = (error: unknown, operation: string, model: string): PrismaError
1038
1407
 
1039
1408
  // Create, Upsert
1040
1409
  const mapCreateError = (error: unknown, operation: string, model: string): PrismaCreateError => {
1041
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1042
- switch (error.code) {
1410
+ if (isPrismaClientKnownRequestError(error)) {
1411
+ const knownError = error;
1412
+ switch (knownError.code) {
1043
1413
  case "P2000":
1044
1414
  return new PrismaValueTooLongError({ cause: error, operation, model });
1045
1415
  case "P2002":
@@ -1071,8 +1441,9 @@ const mapCreateError = (error: unknown, operation: string, model: string): Prism
1071
1441
 
1072
1442
  // Update
1073
1443
  const mapUpdateError = (error: unknown, operation: string, model: string): PrismaUpdateError => {
1074
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1075
- switch (error.code) {
1444
+ if (isPrismaClientKnownRequestError(error)) {
1445
+ const knownError = error;
1446
+ switch (knownError.code) {
1076
1447
  case "P2000":
1077
1448
  return new PrismaValueTooLongError({ cause: error, operation, model });
1078
1449
  case "P2002":
@@ -1108,8 +1479,9 @@ const mapUpdateError = (error: unknown, operation: string, model: string): Prism
1108
1479
 
1109
1480
  // Delete
1110
1481
  const mapDeleteError = (error: unknown, operation: string, model: string): PrismaDeleteError => {
1111
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1112
- switch (error.code) {
1482
+ if (isPrismaClientKnownRequestError(error)) {
1483
+ const knownError = error;
1484
+ switch (knownError.code) {
1113
1485
  case "P2003":
1114
1486
  return new PrismaForeignKeyConstraintError({ cause: error, operation, model });
1115
1487
  case "P2014":
@@ -1127,8 +1499,9 @@ const mapDeleteError = (error: unknown, operation: string, model: string): Prism
1127
1499
 
1128
1500
  // FindOrThrow
1129
1501
  const mapFindOrThrowError = (error: unknown, operation: string, model: string): PrismaFindOrThrowError => {
1130
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1131
- switch (error.code) {
1502
+ if (isPrismaClientKnownRequestError(error)) {
1503
+ const knownError = error;
1504
+ switch (knownError.code) {
1132
1505
  case "P2024":
1133
1506
  return new PrismaConnectionError({ cause: error, operation, model });
1134
1507
  case "P2025":
@@ -1140,8 +1513,9 @@ const mapFindOrThrowError = (error: unknown, operation: string, model: string):
1140
1513
 
1141
1514
  // Find
1142
1515
  const mapFindError = (error: unknown, operation: string, model: string): PrismaFindError => {
1143
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1144
- switch (error.code) {
1516
+ if (isPrismaClientKnownRequestError(error)) {
1517
+ const knownError = error;
1518
+ switch (knownError.code) {
1145
1519
  case "P2024":
1146
1520
  return new PrismaConnectionError({ cause: error, operation, model });
1147
1521
  }
@@ -1151,8 +1525,9 @@ const mapFindError = (error: unknown, operation: string, model: string): PrismaF
1151
1525
 
1152
1526
  // DeleteMany
1153
1527
  const mapDeleteManyError = (error: unknown, operation: string, model: string): PrismaDeleteManyError => {
1154
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1155
- switch (error.code) {
1528
+ if (isPrismaClientKnownRequestError(error)) {
1529
+ const knownError = error;
1530
+ switch (knownError.code) {
1156
1531
  case "P2003":
1157
1532
  return new PrismaForeignKeyConstraintError({ cause: error, operation, model });
1158
1533
  case "P2014":
@@ -1168,8 +1543,9 @@ const mapDeleteManyError = (error: unknown, operation: string, model: string): P
1168
1543
 
1169
1544
  // UpdateMany
1170
1545
  const mapUpdateManyError = (error: unknown, operation: string, model: string): PrismaUpdateManyError => {
1171
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1172
- switch (error.code) {
1546
+ if (isPrismaClientKnownRequestError(error)) {
1547
+ const knownError = error;
1548
+ switch (knownError.code) {
1173
1549
  case "P2000":
1174
1550
  return new PrismaValueTooLongError({ cause: error, operation, model });
1175
1551
  case "P2002":
@@ -1196,6 +1572,52 @@ const mapUpdateManyError = (error: unknown, operation: string, model: string): P
1196
1572
  throw error;
1197
1573
  }
1198
1574
 
1575
+ /**
1576
+ * Helper to get the current client - either the transaction client if in a transaction,
1577
+ * or the root client if not. Uses Effect.serviceOption to detect transaction context.
1578
+ */
1579
+ const clientOrTx = (client: BasePrismaClient) => Effect.map(
1580
+ Effect.serviceOption(PrismaTransactionClientService),
1581
+ Option.getOrElse(() => client),
1582
+ );
1583
+
1584
+ /**
1585
+ * Like Effect.acquireUseRelease, but allows the release function to fail.
1586
+ * Release errors are surfaced in the error channel instead of becoming defects.
1587
+ *
1588
+ * Key properties:
1589
+ * - The release function is always called, even if use fails
1590
+ * - The release function is uninterruptible to ensure cleanup completes
1591
+ * - Release errors are surfaced in the error channel, not as defects
1592
+ */
1593
+ const acquireUseReleaseWithErrors = <A, E, R, A2, E2, R2, X, E3, R3>(
1594
+ acquire: EffectType<A, E, R>,
1595
+ use: (a: A) => EffectType<A2, E2, R2>,
1596
+ release: (a: A, exit: Exit.Exit<A2, E2>) => EffectType<X, E3, R3>
1597
+ ): EffectType<A2, E | E2 | E3, R | R2 | R3> =>
1598
+ Effect.uninterruptibleMask((restore) =>
1599
+ Effect.flatMap(acquire, (a) =>
1600
+ Effect.flatMap(
1601
+ Effect.exit(restore(use(a))),
1602
+ (exit) =>
1603
+ Effect.flatMap(
1604
+ // Make release uninterruptible to ensure cleanup always completes
1605
+ Effect.exit(Effect.uninterruptible(release(a, exit))),
1606
+ (releaseExit) => {
1607
+ if (Exit.isFailure(releaseExit)) {
1608
+ // Release failed - surface the release error
1609
+ return releaseExit as any;
1610
+ }
1611
+ // Release succeeded - return the original use result
1612
+ return exit as any;
1613
+ }
1614
+ )
1615
+ )
1616
+ )
1617
+ );
1618
+
1619
+ ${prismaInterface}
1620
+
1199
1621
  /**
1200
1622
  * Internal helper to begin a callback-free interactive transaction.
1201
1623
  * Returns a transaction client with $commit and $rollback methods.
@@ -1208,8 +1630,8 @@ const $begin = (
1208
1630
  timeout?: number
1209
1631
  isolationLevel?: PrismaNamespace.TransactionIsolationLevel
1210
1632
  }
1211
- ): Effect.Effect<FlatTransactionClient, PrismaError> =>
1212
- Effect.async<FlatTransactionClient, PrismaError>((resume) => {
1633
+ ): EffectType<FlatTransactionClient, PrismaError> =>
1634
+ Effect.callback<FlatTransactionClient, PrismaError>((resume) => {
1213
1635
  let setTxClient: (txClient: PrismaNamespace.TransactionClient) => void
1214
1636
  let commit: () => void
1215
1637
  let rollback: () => void
@@ -1261,15 +1683,14 @@ const $begin = (
1261
1683
  * return user
1262
1684
  * })
1263
1685
  *
1264
- * // Run with default layer (Prisma 6)
1265
- * Effect.runPromise(program.pipe(Effect.provide(Prisma.Live)))
1266
- *
1267
- * // Or with custom options
1686
+ * // Run with layer
1268
1687
  * Effect.runPromise(program.pipe(Effect.provide(Prisma.layer({ datasourceUrl: "..." }))))
1269
1688
  */
1270
- export class Prisma extends Service<Prisma>()("Prisma", {
1271
- effect: Effect.gen(function* () {
1272
- return {
1689
+ const makePrismaService = Effect.gen(function* () {
1690
+ const client = yield* PrismaClient;
1691
+
1692
+ const prismaService: IPrismaService = {
1693
+ client,
1273
1694
  /**
1274
1695
  * Execute an effect within a database transaction.
1275
1696
  * All operations within the effect will be atomic - they either all succeed or all fail.
@@ -1277,7 +1698,8 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1277
1698
  * This implementation uses a callback-free transaction pattern that keeps the effect
1278
1699
  * running in the same fiber as the parent, preserving Ref, FiberRef, and Context access.
1279
1700
  *
1280
- * Options passed here override any defaults set via transactionOptions in the layer.
1701
+ * Uses default transaction options from PrismaClient constructor.
1702
+ * For custom options, use \`$transactionWith\`.
1281
1703
  *
1282
1704
  * @example
1283
1705
  * const result = yield* prisma.$transaction(
@@ -1287,47 +1709,100 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1287
1709
  * return user
1288
1710
  * })
1289
1711
  * )
1712
+ */
1713
+ $transaction: ${enableTelemetry ? 'Effect.fn("Prisma.$transaction")' : "Effect.fnUntraced"}(function* (effect) {
1714
+ const currentTx = yield* Effect.serviceOption(PrismaTransactionClientService);
1715
+
1716
+ // If already in a transaction, just run the effect
1717
+ if (Option.isSome(currentTx)) {
1718
+ return yield* (effect as EffectType<
1719
+ EffectSuccess<typeof effect>,
1720
+ EffectError<typeof effect>,
1721
+ Exclude<
1722
+ EffectServices<typeof effect>,
1723
+ PrismaTransactionClientService
1724
+ >
1725
+ >);
1726
+ }
1727
+
1728
+ // Otherwise, start a new transaction
1729
+ return yield* acquireUseReleaseWithErrors(
1730
+ // Acquire: begin a new transaction with default options
1731
+ $begin(client),
1732
+
1733
+ // Use: run the effect with the transaction client injected
1734
+ (txClient) =>
1735
+ effect.pipe(
1736
+ Effect.provideService(PrismaTransactionClientService, txClient)
1737
+ ),
1738
+
1739
+ // Release: commit on success, rollback on failure/interruption
1740
+ (txClient, exit) =>
1741
+ Exit.isSuccess(exit)
1742
+ ? Effect.tryPromise({
1743
+ try: () => txClient.$commit(),
1744
+ catch: (error) => mapError(error, "$commit", "Prisma")
1745
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1746
+ : Effect.tryPromise({
1747
+ try: () => txClient.$rollback(),
1748
+ catch: (error) => mapError(error, "$rollback", "Prisma")
1749
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1750
+ );
1751
+ }),
1752
+
1753
+ /**
1754
+ * Execute an effect within a database transaction with custom options.
1755
+ * All operations within the effect will be atomic - they either all succeed or all fail.
1756
+ *
1757
+ * This implementation uses a callback-free transaction pattern that keeps the effect
1758
+ * running in the same fiber as the parent, preserving Ref, FiberRef, and Context access.
1759
+ *
1760
+ * Options passed here override any defaults set in PrismaClient constructor.
1290
1761
  *
1291
1762
  * @example
1292
1763
  * // Override default isolation level for this transaction
1293
- * const result = yield* prisma.$transaction(myEffect, {
1294
- * isolationLevel: "ReadCommitted"
1295
- * })
1764
+ * const result = yield* prisma.$transactionWith(
1765
+ * Effect.gen(function* () {
1766
+ * const user = yield* prisma.user.create({ data: { name: "Alice" } })
1767
+ * yield* prisma.post.create({ data: { title: "Hello", authorId: user.id } })
1768
+ * return user
1769
+ * }),
1770
+ * { isolationLevel: "ReadCommitted", timeout: 10000 }
1771
+ * )
1296
1772
  */
1297
- $transaction: <R, E, A>(
1298
- effect: Effect.Effect<A, E, R>,
1299
- options?: TransactionOptions
1300
- ) =>
1301
- Effect.flatMap(
1302
- PrismaClient,
1303
- ({ client, tx }): Effect.Effect<A, E | PrismaError, R> => {
1304
- // If we're already in a transaction, just run the effect directly (no nesting)
1305
- const isRootClient = "$transaction" in tx
1306
- if (!isRootClient) {
1307
- return effect
1308
- }
1773
+ $transactionWith: ${enableTelemetry ? 'Effect.fn("Prisma.$transactionWith")' : "Effect.fnUntraced"}(function* (effect, options) {
1774
+ const currentTx = yield* Effect.serviceOption(PrismaTransactionClientService);
1309
1775
 
1310
- // Use acquireUseRelease to manage the transaction lifecycle
1311
- // This keeps everything in the same fiber, preserving Ref/FiberRef/Context
1312
- return Effect.acquireUseRelease(
1313
- // Acquire: begin a new transaction
1314
- // Prisma merges per-call options with constructor defaults internally
1315
- $begin(client, options),
1316
-
1317
- // Use: run the effect with the transaction client injected
1318
- (txClient) =>
1319
- effect.pipe(
1320
- Effect.provideService(PrismaClient, { tx: txClient, client })
1321
- ),
1322
-
1323
- // Release: commit on success, rollback on failure/interruption
1324
- (txClient, exit) =>
1325
- Exit.isSuccess(exit)
1326
- ? Effect.promise(() => txClient.$commit())
1327
- : Effect.promise(() => txClient.$rollback())
1328
- )
1776
+ // If already in a transaction, just run the effect
1777
+ if (Option.isSome(currentTx)) {
1778
+ return yield* effect;
1329
1779
  }
1330
- ),
1780
+
1781
+ // Otherwise, start a new transaction
1782
+ return yield* acquireUseReleaseWithErrors(
1783
+ // Acquire: begin a new transaction
1784
+ // Prisma merges per-call options with constructor defaults internally
1785
+ $begin(client, options),
1786
+
1787
+ // Use: run the effect with the transaction client injected
1788
+ (txClient) =>
1789
+ effect.pipe(
1790
+ Effect.provideService(PrismaTransactionClientService, txClient)
1791
+ ),
1792
+
1793
+ // Release: commit on success, rollback on failure/interruption
1794
+ (txClient, exit) =>
1795
+ Exit.isSuccess(exit)
1796
+ ? Effect.tryPromise({
1797
+ try: () => txClient.$commit(),
1798
+ catch: (error) => mapError(error, "$commit", "Prisma")
1799
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1800
+ : Effect.tryPromise({
1801
+ try: () => txClient.$rollback(),
1802
+ catch: (error) => mapError(error, "$rollback", "Prisma")
1803
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1804
+ );
1805
+ }),
1331
1806
 
1332
1807
  /**
1333
1808
  * Execute an effect in a NEW transaction, even if already inside a transaction.
@@ -1341,6 +1816,9 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1341
1816
  * ⚠️ WARNING: The isolated transaction can commit while the parent rolls back,
1342
1817
  * or vice versa. Use carefully to avoid data inconsistencies.
1343
1818
  *
1819
+ * Uses default transaction options from PrismaClient constructor.
1820
+ * For custom options, use \`$isolatedTransactionWith\`.
1821
+ *
1344
1822
  * @example
1345
1823
  * yield* prisma.$transaction(
1346
1824
  * Effect.gen(function* () {
@@ -1353,46 +1831,99 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1353
1831
  * })
1354
1832
  * )
1355
1833
  */
1356
- $isolatedTransaction: <R, E, A>(
1357
- effect: Effect.Effect<A, E, R>,
1358
- options?: TransactionOptions
1359
- ) =>
1360
- Effect.flatMap(
1361
- PrismaClient,
1362
- ({ client }): Effect.Effect<A, E | PrismaError, R> => {
1363
- // Always use the root client to create a fresh transaction
1364
- return Effect.acquireUseRelease(
1365
- $begin(client, options),
1366
- (txClient) =>
1367
- effect.pipe(
1368
- Effect.provideService(PrismaClient, { tx: txClient, client })
1369
- ),
1370
- (txClient, exit) =>
1371
- Exit.isSuccess(exit)
1372
- ? Effect.promise(() => txClient.$commit())
1373
- : Effect.promise(() => txClient.$rollback())
1374
- )
1375
- }
1376
- ),
1834
+ $isolatedTransaction: ${enableTelemetry ? 'Effect.fn("Prisma.$isolatedTransaction")' : "Effect.fnUntraced"}(function* (effect) {
1835
+ // Always create a fresh transaction
1836
+ return yield* acquireUseReleaseWithErrors(
1837
+ $begin(client),
1838
+ (txClient) =>
1839
+ effect.pipe(
1840
+ Effect.provideService(PrismaTransactionClientService, txClient)
1841
+ ),
1842
+ (txClient, exit) =>
1843
+ Exit.isSuccess(exit)
1844
+ ? Effect.tryPromise({
1845
+ try: () => txClient.$commit(),
1846
+ catch: (error) => mapError(error, "$commit", "Prisma")
1847
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1848
+ : Effect.tryPromise({
1849
+ try: () => txClient.$rollback(),
1850
+ catch: (error) => mapError(error, "$rollback", "Prisma")
1851
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1852
+ );
1853
+ }),
1854
+
1855
+ /**
1856
+ * Execute an effect in a NEW transaction with custom options, even if already inside a transaction.
1857
+ * Unlike \`$transaction\`, this always creates a fresh, independent transaction.
1858
+ *
1859
+ * Use this for operations that should NOT be rolled back with the parent:
1860
+ * - Audit logging that must persist even if main operation fails
1861
+ * - Saga pattern where each step has independent commit/rollback
1862
+ * - Background job queuing that should commit immediately
1863
+ *
1864
+ * ⚠️ WARNING: The isolated transaction can commit while the parent rolls back,
1865
+ * or vice versa. Use carefully to avoid data inconsistencies.
1866
+ *
1867
+ * Options passed here override any defaults set in PrismaClient constructor.
1868
+ *
1869
+ * @example
1870
+ * yield* prisma.$transaction(
1871
+ * Effect.gen(function* () {
1872
+ * // This audit log commits independently with custom isolation level
1873
+ * yield* prisma.$isolatedTransactionWith(
1874
+ * prisma.auditLog.create({ data: { action: "attempt", userId } }),
1875
+ * { isolationLevel: "Serializable" }
1876
+ * )
1877
+ * // Main operation - if this fails, audit log is still committed
1878
+ * yield* prisma.user.delete({ where: { id: userId } })
1879
+ * })
1880
+ * )
1881
+ */
1882
+ $isolatedTransactionWith: ${enableTelemetry ? 'Effect.fn("Prisma.$isolatedTransactionWith")' : "Effect.fnUntraced"}(function* (effect, options) {
1883
+ // Always create a fresh transaction
1884
+ return yield* acquireUseReleaseWithErrors(
1885
+ $begin(client, options),
1886
+ (txClient) =>
1887
+ effect.pipe(
1888
+ Effect.provideService(PrismaTransactionClientService, txClient)
1889
+ ),
1890
+ (txClient, exit) =>
1891
+ Exit.isSuccess(exit)
1892
+ ? Effect.tryPromise({
1893
+ try: () => txClient.$commit(),
1894
+ catch: (error) => mapError(error, "$commit", "Prisma")
1895
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1896
+ : Effect.tryPromise({
1897
+ try: () => txClient.$rollback(),
1898
+ catch: (error) => mapError(error, "$rollback", "Prisma")
1899
+ }).pipe(Effect.withSpan("txClient.$rollback"))
1900
+ );
1901
+ }),
1377
1902
  ${rawSqlOperations}
1378
1903
 
1379
1904
  ${modelOperations}
1380
- }
1381
- })
1382
- }) {
1905
+ };
1906
+
1907
+ return prismaService;
1908
+ });
1909
+
1910
+ export class Prisma extends ServiceMap.Service<Prisma, IPrismaService>()("Prisma") {
1911
+ /**
1912
+ * Effect that constructs the Prisma service.
1913
+ * Used internally by layer constructors.
1914
+ */
1915
+ static make: EffectType<IPrismaService, never, PrismaClient> = makePrismaService;
1916
+ static Default: Layer.Layer<Prisma, never, PrismaClient> = Layer.effect(Prisma, this.make);
1917
+
1383
1918
  /**
1384
1919
  * Create a complete Prisma layer with the given PrismaClient options.
1385
1920
  * This is the recommended way to create a Prisma layer - it bundles both
1386
1921
  * PrismaClient and Prisma service together.
1387
1922
  *
1388
1923
  * Pass options directly - the signature matches PrismaClient's constructor.
1389
- * Prisma 6: all options are optional
1390
1924
  * Prisma 7: requires either \`adapter\` or \`accelerateUrl\`
1391
1925
  *
1392
1926
  * @example
1393
- * // Prisma 6
1394
- * const MainLayer = Prisma.layer({ datasourceUrl: process.env.DATABASE_URL })
1395
- *
1396
1927
  * // Prisma 7 with adapter
1397
1928
  * const MainLayer = Prisma.layer({ adapter: myAdapter })
1398
1929
  *
@@ -1405,9 +1936,11 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1405
1936
  * // Use it
1406
1937
  * Effect.runPromise(program.pipe(Effect.provide(MainLayer)))
1407
1938
  */
1408
- static layer = (
1939
+ static layer: (
1409
1940
  ...args: ConstructorParameters<typeof BasePrismaClient>
1410
- ) => Layer.merge(PrismaClient.layer(...args), Prisma.Default)
1941
+ ) => Layer.Layer<Prisma | PrismaClient, never, never> = (...args) => this.Default.pipe(
1942
+ Layer.provideMerge(PrismaClient.layer(...args))
1943
+ );
1411
1944
 
1412
1945
  /**
1413
1946
  * Create a complete Prisma layer where PrismaClient options are computed via an Effect.
@@ -1432,10 +1965,12 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1432
1965
  * })
1433
1966
  * )
1434
1967
  */
1435
- static layerEffect = <R, E>(
1436
- optionsEffect: Effect.Effect<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
1437
- ) => Layer.merge(PrismaClient.layerEffect(optionsEffect), Prisma.Default)
1438
-
1968
+ static layerEffect: <R, E>(
1969
+ optionsEffect: EffectType<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
1970
+ ) => Layer.Layer<Prisma | PrismaClient, E, Exclude<R, Scope.Scope>> = (optionsEffect) => this.Default.pipe(
1971
+ Layer.provideMerge(PrismaClient.layerEffect(optionsEffect))
1972
+ );
1439
1973
  }
1974
+
1440
1975
  `;
1441
1976
  }