prisma-generator-effect 0.0.4 → 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 +996 -513
  2. package/package.json +13 -10
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,49 +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
  }
733
1090
 
734
- // ============================================================================
735
- // Deprecated aliases for backward compatibility
736
- // ============================================================================
737
-
738
- /**
739
- * @deprecated Use \`PrismaClient\` instead. Will be removed in next major version.
740
- */
741
- export const PrismaClientService = PrismaClient
742
-
743
- /**
744
- * @deprecated Use \`Prisma\` instead. Will be removed in next major version.
745
- */
746
- export const PrismaService = Prisma
747
-
748
- /**
749
- * @deprecated Use \`PrismaClient.layer()\` instead. Will be removed in next major version.
750
- */
751
- export const makePrismaLayer = PrismaClient.layer
752
-
753
- /**
754
- * @deprecated Use \`PrismaClient.layerEffect()\` instead. Will be removed in next major version.
755
- */
756
- export const makePrismaLayerEffect = PrismaClient.layerEffect
757
-
758
-
759
1091
  `;
760
1092
  }
761
1093
  /**
762
1094
  * Generate service with default tagged error classes.
763
1095
  * Operations have per-operation error types for fine-grained error handling.
764
1096
  */
765
- function generateDefaultErrorService(clientImportPath, rawSqlOperations, modelOperations) {
1097
+ function generateDefaultErrorService(clientImportPath, rawSqlOperations, modelTypeAliases, prismaInterface, modelOperations, enableTelemetry) {
1098
+ const _errorType = "PrismaError";
766
1099
  return `${header}
767
- import { Context, Data, Effect, Exit, Layer } from "effect"
768
- 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"
769
1102
  import { Prisma as PrismaNamespace, PrismaClient as BasePrismaClient } from "${clientImportPath}"
770
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
+
771
1120
  // Symbol used to identify intentional rollbacks vs actual errors
772
1121
  const ROLLBACK = Symbol.for("prisma.effect.rollback")
773
1122
 
@@ -786,42 +1135,28 @@ type TransactionOptions = {
786
1135
 
787
1136
  /**
788
1137
  * Context tag for the Prisma client instance.
789
- * Holds the transaction client (tx) and root client.
790
1138
  *
791
1139
  * Use \`PrismaClient.layer()\` or \`PrismaClient.layerEffect()\` to create a layer.
792
1140
  *
793
1141
  * @example
794
- * // Prisma 6 - all options are optional
795
- * const layer = PrismaClient.layer({ datasourceUrl: "..." })
796
- *
797
1142
  * // Prisma 7 - adapter or accelerateUrl is required
798
1143
  * const layer = PrismaClient.layer({ adapter: myAdapter })
799
1144
  *
800
1145
  * // With transaction options (Prisma uses these as defaults for $transaction)
801
1146
  * const layer = PrismaClient.layer({
802
- * adapter: myAdapter, // or datasourceUrl for Prisma 6
1147
+ * adapter: myAdapter,
803
1148
  * transactionOptions: { isolationLevel: "Serializable", timeout: 10000 }
804
1149
  * })
805
1150
  */
806
- export class PrismaClient extends Context.Tag("PrismaClient")<
807
- PrismaClient,
808
- {
809
- tx: BasePrismaClient | PrismaNamespace.TransactionClient
810
- client: BasePrismaClient
811
- }
812
- >() {
1151
+ export class PrismaClient extends ServiceMap.Service<PrismaClient, BasePrismaClient>()("PrismaClient") {
813
1152
  /**
814
1153
  * Create a PrismaClient layer with the given options.
815
1154
  * The client will be automatically disconnected when the layer scope ends.
816
1155
  *
817
1156
  * Pass options directly - the signature matches PrismaClient's constructor.
818
- * Prisma 6: all options are optional
819
1157
  * Prisma 7: requires either \`adapter\` or \`accelerateUrl\`
820
1158
  *
821
1159
  * @example
822
- * // Prisma 6
823
- * const layer = PrismaClient.layer({ datasourceUrl: process.env.DATABASE_URL })
824
- *
825
1160
  * // Prisma 7 with adapter
826
1161
  * const layer = PrismaClient.layer({ adapter: myAdapter })
827
1162
  *
@@ -831,14 +1166,14 @@ export class PrismaClient extends Context.Tag("PrismaClient")<
831
1166
  * transactionOptions: { isolationLevel: "Serializable" }
832
1167
  * })
833
1168
  */
834
- static layer = (
1169
+ static layer: (
835
1170
  ...args: ConstructorParameters<typeof BasePrismaClient>
836
- ) => Layer.scoped(
1171
+ ) => Layer.Layer<PrismaClient, never, never> = (...args) => Layer.effect(
837
1172
  PrismaClient,
838
1173
  Effect.gen(function* () {
839
- const prisma = new BasePrismaClient(...args)
1174
+ const prisma: BasePrismaClient = new BasePrismaClient(...args)
840
1175
  yield* Effect.addFinalizer(() => Effect.promise(() => prisma.$disconnect()))
841
- return { tx: prisma, client: prisma }
1176
+ return prisma
842
1177
  })
843
1178
  )
844
1179
 
@@ -866,19 +1201,26 @@ export class PrismaClient extends Context.Tag("PrismaClient")<
866
1201
  * })
867
1202
  * )
868
1203
  */
869
- static layerEffect = <R, E>(
870
- optionsEffect: Effect.Effect<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
871
- ) => 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(
872
1207
  PrismaClient,
873
1208
  Effect.gen(function* () {
874
- const options = yield* optionsEffect
875
- const prisma = new BasePrismaClient(options)
1209
+ const options: ConstructorParameters<typeof BasePrismaClient>[0] = yield* optionsEffect
1210
+ const prisma: BasePrismaClient = new BasePrismaClient(options)
876
1211
  yield* Effect.addFinalizer(() => Effect.promise(() => prisma.$disconnect()))
877
- return { tx: prisma, client: prisma }
1212
+ return prisma
878
1213
  })
879
1214
  )
880
1215
  }
881
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
+
882
1224
  export class PrismaUniqueConstraintError extends Data.TaggedError("PrismaUniqueConstraintError")<{
883
1225
  cause: PrismaNamespace.PrismaClientKnownRequestError
884
1226
  operation: string
@@ -1024,8 +1366,9 @@ export type PrismaError =
1024
1366
 
1025
1367
  // Generic mapper for raw operations and fallback
1026
1368
  const mapError = (error: unknown, operation: string, model: string): PrismaError => {
1027
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1028
- switch (error.code) {
1369
+ if (isPrismaClientKnownRequestError(error)) {
1370
+ const knownError = error;
1371
+ switch (knownError.code) {
1029
1372
  case "P2000":
1030
1373
  return new PrismaValueTooLongError({ cause: error, operation, model });
1031
1374
  case "P2002":
@@ -1064,8 +1407,9 @@ const mapError = (error: unknown, operation: string, model: string): PrismaError
1064
1407
 
1065
1408
  // Create, Upsert
1066
1409
  const mapCreateError = (error: unknown, operation: string, model: string): PrismaCreateError => {
1067
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1068
- switch (error.code) {
1410
+ if (isPrismaClientKnownRequestError(error)) {
1411
+ const knownError = error;
1412
+ switch (knownError.code) {
1069
1413
  case "P2000":
1070
1414
  return new PrismaValueTooLongError({ cause: error, operation, model });
1071
1415
  case "P2002":
@@ -1097,8 +1441,9 @@ const mapCreateError = (error: unknown, operation: string, model: string): Prism
1097
1441
 
1098
1442
  // Update
1099
1443
  const mapUpdateError = (error: unknown, operation: string, model: string): PrismaUpdateError => {
1100
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1101
- switch (error.code) {
1444
+ if (isPrismaClientKnownRequestError(error)) {
1445
+ const knownError = error;
1446
+ switch (knownError.code) {
1102
1447
  case "P2000":
1103
1448
  return new PrismaValueTooLongError({ cause: error, operation, model });
1104
1449
  case "P2002":
@@ -1134,8 +1479,9 @@ const mapUpdateError = (error: unknown, operation: string, model: string): Prism
1134
1479
 
1135
1480
  // Delete
1136
1481
  const mapDeleteError = (error: unknown, operation: string, model: string): PrismaDeleteError => {
1137
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1138
- switch (error.code) {
1482
+ if (isPrismaClientKnownRequestError(error)) {
1483
+ const knownError = error;
1484
+ switch (knownError.code) {
1139
1485
  case "P2003":
1140
1486
  return new PrismaForeignKeyConstraintError({ cause: error, operation, model });
1141
1487
  case "P2014":
@@ -1153,8 +1499,9 @@ const mapDeleteError = (error: unknown, operation: string, model: string): Prism
1153
1499
 
1154
1500
  // FindOrThrow
1155
1501
  const mapFindOrThrowError = (error: unknown, operation: string, model: string): PrismaFindOrThrowError => {
1156
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1157
- switch (error.code) {
1502
+ if (isPrismaClientKnownRequestError(error)) {
1503
+ const knownError = error;
1504
+ switch (knownError.code) {
1158
1505
  case "P2024":
1159
1506
  return new PrismaConnectionError({ cause: error, operation, model });
1160
1507
  case "P2025":
@@ -1166,8 +1513,9 @@ const mapFindOrThrowError = (error: unknown, operation: string, model: string):
1166
1513
 
1167
1514
  // Find
1168
1515
  const mapFindError = (error: unknown, operation: string, model: string): PrismaFindError => {
1169
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1170
- switch (error.code) {
1516
+ if (isPrismaClientKnownRequestError(error)) {
1517
+ const knownError = error;
1518
+ switch (knownError.code) {
1171
1519
  case "P2024":
1172
1520
  return new PrismaConnectionError({ cause: error, operation, model });
1173
1521
  }
@@ -1177,8 +1525,9 @@ const mapFindError = (error: unknown, operation: string, model: string): PrismaF
1177
1525
 
1178
1526
  // DeleteMany
1179
1527
  const mapDeleteManyError = (error: unknown, operation: string, model: string): PrismaDeleteManyError => {
1180
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1181
- switch (error.code) {
1528
+ if (isPrismaClientKnownRequestError(error)) {
1529
+ const knownError = error;
1530
+ switch (knownError.code) {
1182
1531
  case "P2003":
1183
1532
  return new PrismaForeignKeyConstraintError({ cause: error, operation, model });
1184
1533
  case "P2014":
@@ -1194,8 +1543,9 @@ const mapDeleteManyError = (error: unknown, operation: string, model: string): P
1194
1543
 
1195
1544
  // UpdateMany
1196
1545
  const mapUpdateManyError = (error: unknown, operation: string, model: string): PrismaUpdateManyError => {
1197
- if (error instanceof PrismaNamespace.PrismaClientKnownRequestError) {
1198
- switch (error.code) {
1546
+ if (isPrismaClientKnownRequestError(error)) {
1547
+ const knownError = error;
1548
+ switch (knownError.code) {
1199
1549
  case "P2000":
1200
1550
  return new PrismaValueTooLongError({ cause: error, operation, model });
1201
1551
  case "P2002":
@@ -1222,6 +1572,52 @@ const mapUpdateManyError = (error: unknown, operation: string, model: string): P
1222
1572
  throw error;
1223
1573
  }
1224
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
+
1225
1621
  /**
1226
1622
  * Internal helper to begin a callback-free interactive transaction.
1227
1623
  * Returns a transaction client with $commit and $rollback methods.
@@ -1234,8 +1630,8 @@ const $begin = (
1234
1630
  timeout?: number
1235
1631
  isolationLevel?: PrismaNamespace.TransactionIsolationLevel
1236
1632
  }
1237
- ): Effect.Effect<FlatTransactionClient, PrismaError> =>
1238
- Effect.async<FlatTransactionClient, PrismaError>((resume) => {
1633
+ ): EffectType<FlatTransactionClient, PrismaError> =>
1634
+ Effect.callback<FlatTransactionClient, PrismaError>((resume) => {
1239
1635
  let setTxClient: (txClient: PrismaNamespace.TransactionClient) => void
1240
1636
  let commit: () => void
1241
1637
  let rollback: () => void
@@ -1287,15 +1683,14 @@ const $begin = (
1287
1683
  * return user
1288
1684
  * })
1289
1685
  *
1290
- * // Run with default layer (Prisma 6)
1291
- * Effect.runPromise(program.pipe(Effect.provide(Prisma.Live)))
1292
- *
1293
- * // Or with custom options
1686
+ * // Run with layer
1294
1687
  * Effect.runPromise(program.pipe(Effect.provide(Prisma.layer({ datasourceUrl: "..." }))))
1295
1688
  */
1296
- export class Prisma extends Service<Prisma>()("Prisma", {
1297
- effect: Effect.gen(function* () {
1298
- return {
1689
+ const makePrismaService = Effect.gen(function* () {
1690
+ const client = yield* PrismaClient;
1691
+
1692
+ const prismaService: IPrismaService = {
1693
+ client,
1299
1694
  /**
1300
1695
  * Execute an effect within a database transaction.
1301
1696
  * All operations within the effect will be atomic - they either all succeed or all fail.
@@ -1303,7 +1698,8 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1303
1698
  * This implementation uses a callback-free transaction pattern that keeps the effect
1304
1699
  * running in the same fiber as the parent, preserving Ref, FiberRef, and Context access.
1305
1700
  *
1306
- * 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\`.
1307
1703
  *
1308
1704
  * @example
1309
1705
  * const result = yield* prisma.$transaction(
@@ -1313,47 +1709,100 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1313
1709
  * return user
1314
1710
  * })
1315
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.
1316
1761
  *
1317
1762
  * @example
1318
1763
  * // Override default isolation level for this transaction
1319
- * const result = yield* prisma.$transaction(myEffect, {
1320
- * isolationLevel: "ReadCommitted"
1321
- * })
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
+ * )
1322
1772
  */
1323
- $transaction: <R, E, A>(
1324
- effect: Effect.Effect<A, E, R>,
1325
- options?: TransactionOptions
1326
- ) =>
1327
- Effect.flatMap(
1328
- PrismaClient,
1329
- ({ client, tx }): Effect.Effect<A, E | PrismaError, R> => {
1330
- // If we're already in a transaction, just run the effect directly (no nesting)
1331
- const isRootClient = "$transaction" in tx
1332
- if (!isRootClient) {
1333
- return effect
1334
- }
1773
+ $transactionWith: ${enableTelemetry ? 'Effect.fn("Prisma.$transactionWith")' : "Effect.fnUntraced"}(function* (effect, options) {
1774
+ const currentTx = yield* Effect.serviceOption(PrismaTransactionClientService);
1335
1775
 
1336
- // Use acquireUseRelease to manage the transaction lifecycle
1337
- // This keeps everything in the same fiber, preserving Ref/FiberRef/Context
1338
- return Effect.acquireUseRelease(
1339
- // Acquire: begin a new transaction
1340
- // Prisma merges per-call options with constructor defaults internally
1341
- $begin(client, options),
1342
-
1343
- // Use: run the effect with the transaction client injected
1344
- (txClient) =>
1345
- effect.pipe(
1346
- Effect.provideService(PrismaClient, { tx: txClient, client })
1347
- ),
1348
-
1349
- // Release: commit on success, rollback on failure/interruption
1350
- (txClient, exit) =>
1351
- Exit.isSuccess(exit)
1352
- ? Effect.promise(() => txClient.$commit())
1353
- : Effect.promise(() => txClient.$rollback())
1354
- )
1776
+ // If already in a transaction, just run the effect
1777
+ if (Option.isSome(currentTx)) {
1778
+ return yield* effect;
1355
1779
  }
1356
- ),
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
+ }),
1357
1806
 
1358
1807
  /**
1359
1808
  * Execute an effect in a NEW transaction, even if already inside a transaction.
@@ -1367,6 +1816,9 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1367
1816
  * ⚠️ WARNING: The isolated transaction can commit while the parent rolls back,
1368
1817
  * or vice versa. Use carefully to avoid data inconsistencies.
1369
1818
  *
1819
+ * Uses default transaction options from PrismaClient constructor.
1820
+ * For custom options, use \`$isolatedTransactionWith\`.
1821
+ *
1370
1822
  * @example
1371
1823
  * yield* prisma.$transaction(
1372
1824
  * Effect.gen(function* () {
@@ -1379,46 +1831,99 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1379
1831
  * })
1380
1832
  * )
1381
1833
  */
1382
- $isolatedTransaction: <R, E, A>(
1383
- effect: Effect.Effect<A, E, R>,
1384
- options?: TransactionOptions
1385
- ) =>
1386
- Effect.flatMap(
1387
- PrismaClient,
1388
- ({ client }): Effect.Effect<A, E | PrismaError, R> => {
1389
- // Always use the root client to create a fresh transaction
1390
- return Effect.acquireUseRelease(
1391
- $begin(client, options),
1392
- (txClient) =>
1393
- effect.pipe(
1394
- Effect.provideService(PrismaClient, { tx: txClient, client })
1395
- ),
1396
- (txClient, exit) =>
1397
- Exit.isSuccess(exit)
1398
- ? Effect.promise(() => txClient.$commit())
1399
- : Effect.promise(() => txClient.$rollback())
1400
- )
1401
- }
1402
- ),
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
+ }),
1403
1902
  ${rawSqlOperations}
1404
1903
 
1405
1904
  ${modelOperations}
1406
- }
1407
- })
1408
- }) {
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
+
1409
1918
  /**
1410
1919
  * Create a complete Prisma layer with the given PrismaClient options.
1411
1920
  * This is the recommended way to create a Prisma layer - it bundles both
1412
1921
  * PrismaClient and Prisma service together.
1413
1922
  *
1414
1923
  * Pass options directly - the signature matches PrismaClient's constructor.
1415
- * Prisma 6: all options are optional
1416
1924
  * Prisma 7: requires either \`adapter\` or \`accelerateUrl\`
1417
1925
  *
1418
1926
  * @example
1419
- * // Prisma 6
1420
- * const MainLayer = Prisma.layer({ datasourceUrl: process.env.DATABASE_URL })
1421
- *
1422
1927
  * // Prisma 7 with adapter
1423
1928
  * const MainLayer = Prisma.layer({ adapter: myAdapter })
1424
1929
  *
@@ -1431,9 +1936,11 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1431
1936
  * // Use it
1432
1937
  * Effect.runPromise(program.pipe(Effect.provide(MainLayer)))
1433
1938
  */
1434
- static layer = (
1939
+ static layer: (
1435
1940
  ...args: ConstructorParameters<typeof BasePrismaClient>
1436
- ) => 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
+ );
1437
1944
 
1438
1945
  /**
1439
1946
  * Create a complete Prisma layer where PrismaClient options are computed via an Effect.
@@ -1458,36 +1965,12 @@ export class Prisma extends Service<Prisma>()("Prisma", {
1458
1965
  * })
1459
1966
  * )
1460
1967
  */
1461
- static layerEffect = <R, E>(
1462
- optionsEffect: Effect.Effect<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
1463
- ) => Layer.merge(PrismaClient.layerEffect(optionsEffect), Prisma.Default)
1464
-
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
+ );
1465
1973
  }
1466
1974
 
1467
- // ============================================================================
1468
- // Deprecated aliases for backward compatibility
1469
- // ============================================================================
1470
-
1471
- /**
1472
- * @deprecated Use \`PrismaClient\` instead. Will be removed in next major version.
1473
- */
1474
- export const PrismaClientService = PrismaClient
1475
-
1476
- /**
1477
- * @deprecated Use \`Prisma\` instead. Will be removed in next major version.
1478
- */
1479
- export const PrismaService = Prisma
1480
-
1481
- /**
1482
- * @deprecated Use \`PrismaClient.layer()\` instead. Will be removed in next major version.
1483
- */
1484
- export const makePrismaLayer = PrismaClient.layer
1485
-
1486
- /**
1487
- * @deprecated Use \`PrismaClient.layerEffect()\` instead. Will be removed in next major version.
1488
- */
1489
- export const makePrismaLayerEffect = PrismaClient.layerEffect
1490
-
1491
-
1492
1975
  `;
1493
1976
  }