@effectify/prisma 0.1.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/src/cli.d.ts +1 -1
  2. package/dist/src/cli.js +9 -9
  3. package/dist/src/commands/init.d.ts +1 -1
  4. package/dist/src/commands/init.js +0 -2
  5. package/dist/src/commands/prisma.d.ts +5 -0
  6. package/dist/src/commands/prisma.js +35 -0
  7. package/dist/src/generators/sql-schema-generator.d.ts +9 -0
  8. package/dist/src/generators/sql-schema-generator.js +50 -58
  9. package/dist/src/services/generator-context.d.ts +6 -0
  10. package/dist/src/services/generator-context.js +3 -0
  11. package/dist/src/services/generator-service.d.ts +14 -0
  12. package/dist/src/services/generator-service.js +121 -0
  13. package/dist/src/services/render-service.d.ts +10 -0
  14. package/dist/src/services/render-service.js +23 -0
  15. package/dist/src/templates/index-custom-error.eta +190 -0
  16. package/{prisma/generated/effect/index.ts → dist/src/templates/index-default.eta} +4 -38
  17. package/dist/src/templates/model.eta +8 -0
  18. package/dist/src/templates/prisma-raw-sql.eta +31 -0
  19. package/{prisma/generated/effect/prisma-repository.ts → dist/src/templates/prisma-repository.eta} +19 -19
  20. package/package.json +25 -18
  21. package/dist/src/commands/generate-effect.d.ts +0 -2
  22. package/dist/src/commands/generate-effect.js +0 -73
  23. package/dist/src/commands/generate-sql-schema.d.ts +0 -2
  24. package/dist/src/commands/generate-sql-schema.js +0 -72
  25. package/dist/src/effect-prisma.d.ts +0 -2
  26. package/dist/src/effect-prisma.js +0 -1771
  27. package/dist/src/generators/prisma-effect-generator.d.ts +0 -1
  28. package/dist/src/generators/prisma-effect-generator.js +0 -446
  29. package/prisma/dev.db +0 -0
  30. package/prisma/generated/client.d.ts +0 -1
  31. package/prisma/generated/client.js +0 -5
  32. package/prisma/generated/default.d.ts +0 -1
  33. package/prisma/generated/default.js +0 -5
  34. package/prisma/generated/edge.d.ts +0 -1
  35. package/prisma/generated/edge.js +0 -141
  36. package/prisma/generated/effect/schemas/enums.ts +0 -6
  37. package/prisma/generated/effect/schemas/index.ts +0 -2
  38. package/prisma/generated/effect/schemas/types.ts +0 -40
  39. package/prisma/generated/index-browser.js +0 -172
  40. package/prisma/generated/index.d.ts +0 -2360
  41. package/prisma/generated/index.js +0 -141
  42. package/prisma/generated/package.json +0 -144
  43. package/prisma/generated/query_compiler_bg.js +0 -2
  44. package/prisma/generated/query_compiler_bg.wasm +0 -0
  45. package/prisma/generated/query_compiler_bg.wasm-base64.js +0 -2
  46. package/prisma/generated/runtime/client.d.ts +0 -3180
  47. package/prisma/generated/runtime/client.js +0 -86
  48. package/prisma/generated/runtime/index-browser.d.ts +0 -87
  49. package/prisma/generated/runtime/index-browser.js +0 -6
  50. package/prisma/generated/runtime/wasm-compiler-edge.js +0 -76
  51. package/prisma/generated/schema.prisma +0 -31
  52. package/prisma/generated/wasm-edge-light-loader.mjs +0 -5
  53. package/prisma/generated/wasm-worker-loader.mjs +0 -5
  54. package/prisma/migrations/20250721164420_init/migration.sql +0 -9
  55. package/prisma/migrations/20250721191716_dumb/migration.sql +0 -49
  56. package/prisma/migrations/migration_lock.toml +0 -3
  57. package/prisma/schema.prisma +0 -31
  58. package/prisma.config.ts +0 -8
  59. package/project.json +0 -48
  60. package/scripts/cleanup-tests.ts +0 -26
  61. package/scripts/generate-test-files.ts +0 -93
  62. package/setup-tests.ts +0 -10
  63. package/src/cli.tsx +0 -23
  64. package/src/commands/generate-effect.ts +0 -109
  65. package/src/commands/generate-sql-schema.ts +0 -109
  66. package/src/commands/init.ts +0 -155
  67. package/src/effect-prisma.ts +0 -1826
  68. package/src/generators/prisma-effect-generator.ts +0 -496
  69. package/src/generators/sql-schema-generator.ts +0 -75
  70. package/test/prisma-model.test.ts +0 -340
  71. package/test/utils.ts +0 -10
  72. package/tsconfig.json +0 -20
  73. package/tsconfig.lib.json +0 -24
  74. package/tsconfig.spec.json +0 -15
  75. package/vitest.config.ts +0 -23
  76. /package/{prisma/generated/effect/prisma-schema.ts → dist/src/templates/prisma-schema.eta} +0 -0
package/dist/src/cli.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env -S pnpm dlx tsx
2
2
  export {};
package/dist/src/cli.js CHANGED
@@ -1,17 +1,17 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env -S pnpm dlx tsx
2
2
  import * as Command from '@effect/cli/Command';
3
3
  import * as NodeContext from '@effect/platform-node/NodeContext';
4
4
  import * as NodeRuntime from '@effect/platform-node/NodeRuntime';
5
- import * as Console from 'effect/Console';
6
5
  import * as Effect from 'effect/Effect';
7
- import { generateEffectCommand } from './commands/generate-effect.js';
8
- import { generateSqlSchemaCommand } from './commands/generate-sql-schema.js';
6
+ import * as Layer from 'effect/Layer';
9
7
  import { initCommand } from './commands/init.js';
10
- // Main CLI command
11
- const prisma = Command.make('prisma', {}, () => Console.log('🚀 prisma CLI - Use --help to see available commands')).pipe(Command.withSubcommands([initCommand, generateEffectCommand, generateSqlSchemaCommand]));
12
- // Run the CLI
13
- const cli = Command.run(prisma, {
8
+ import { prismaCommand } from './commands/prisma.js';
9
+ import { GeneratorService } from './services/generator-service.js';
10
+ import { RenderService } from './services/render-service.js';
11
+ const cli = Command.run(prismaCommand.pipe(Command.withSubcommands([initCommand])), {
14
12
  name: '@effectify/prisma CLI',
15
13
  version: '0.1.0',
16
14
  });
17
- cli(process.argv).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);
15
+ const GeneratorLayer = GeneratorService.Live.pipe(Layer.provide(RenderService.Live), Layer.provide(NodeContext.layer));
16
+ const MainLayer = Layer.mergeAll(GeneratorLayer, RenderService.Live, NodeContext.layer);
17
+ cli(process.argv).pipe(Effect.provide(MainLayer), NodeRuntime.runMain);
@@ -1,4 +1,4 @@
1
1
  import * as Command from '@effect/cli/Command';
2
- export declare const initCommand: Command.Command<"init", never, Error | import("@effect/platform/Error").PlatformError, {
2
+ export declare const initCommand: Command.Command<"init", never, import("@effect/platform/Error").PlatformError | Error, {
3
3
  readonly output: string;
4
4
  }>;
@@ -91,10 +91,8 @@ generator sqlSchema {
91
91
  yield* Console.log(' 1. Set your DATABASE_URL environment variable');
92
92
  yield* Console.log(' 2. Run: @effectify/prisma prisma generate-effect');
93
93
  yield* Console.log(' 3. Run: @effectify/prisma prisma generate-sql-schema');
94
- // Ensure the effect completes and exits
95
94
  yield* Effect.sync(() => process.exit(0));
96
95
  });
97
- // Export the init command
98
96
  export const initCommand = Command.make('init', {
99
97
  output: outputOption,
100
98
  }, ({ output }) => initializePrismaSchema({
@@ -0,0 +1,5 @@
1
+ import * as Command from '@effect/cli/Command';
2
+ import type * as NodeContext from '@effect/platform-node/NodeContext';
3
+ import { GeneratorService } from '../services/generator-service.js';
4
+ import type { RenderService } from '../services/render-service.js';
5
+ export declare const prismaCommand: Command.Command<"prisma", RenderService | GeneratorService | NodeContext.NodeContext, never, {}>;
@@ -0,0 +1,35 @@
1
+ import * as Command from '@effect/cli/Command';
2
+ import generatorHelper from '@prisma/generator-helper';
3
+ import * as Deferred from 'effect/Deferred';
4
+ import * as Effect from 'effect/Effect';
5
+ import * as Layer from 'effect/Layer';
6
+ import * as Runtime from 'effect/Runtime';
7
+ import * as Stream from 'effect/Stream';
8
+ import { GeneratorContext } from '../services/generator-context.js';
9
+ import { GeneratorService } from '../services/generator-service.js';
10
+ export const prismaCommand = Command.make('prisma', {}, () => Effect.gen(function* () {
11
+ const generator = yield* GeneratorService;
12
+ const runtime = yield* Effect.runtime();
13
+ const run = Runtime.runPromise(runtime);
14
+ const events = Stream.async((emit) => {
15
+ generatorHelper.generatorHandler({
16
+ onManifest() {
17
+ return {
18
+ defaultOutput: '../generated/effect',
19
+ prettyName: 'Prisma Effect Generator',
20
+ requiresEngines: [],
21
+ };
22
+ },
23
+ async onGenerate(options) {
24
+ await run(Effect.gen(function* () {
25
+ const deferred = yield* Deferred.make();
26
+ yield* Effect.promise(() => emit.single([options, deferred]));
27
+ yield* Deferred.await(deferred);
28
+ }));
29
+ // Cerrar el stream después de procesar la generación
30
+ emit.end();
31
+ },
32
+ });
33
+ });
34
+ yield* events.pipe(Stream.runForEach(([options, deferred]) => generator.generate.pipe(Effect.provide(Layer.succeed(GeneratorContext, options)), Effect.intoDeferred(deferred))));
35
+ }));
@@ -1 +1,10 @@
1
+ #!/usr/bin/env tsx
2
+ type SqlGeneratorOptions = {
3
+ generator: {
4
+ output?: {
5
+ value: string;
6
+ };
7
+ };
8
+ };
9
+ export declare function generateSqlSchema(options: SqlGeneratorOptions): Promise<void>;
1
10
  export {};
@@ -1,58 +1,50 @@
1
- // #!/usr/bin/env tsx
2
- export {};
3
- // import { execSync } from 'node:child_process'
4
- // import fs from 'node:fs/promises'
5
- // import path from 'node:path'
6
- // import gh from '@prisma/generator-helper'
7
- // const header = `-- This file was generated by sql-schema-generator, do not edit manually.
8
- // -- Generated at: ${new Date().toISOString()}
9
- // `
10
- // // Types for generator options
11
- // interface SqlGeneratorOptions {
12
- // generator: {
13
- // output?: {
14
- // value: string
15
- // }
16
- // }
17
- // }
18
- // // Export the generator function for use in CLI
19
- // export async function generateSqlSchema(options: SqlGeneratorOptions) {
20
- // const outputDir = options.generator.output?.value || '../generated'
21
- // const datasourceUrl = 'prisma/dev.db'
22
- // if (!datasourceUrl) {
23
- // throw new Error('No datasource URL found')
24
- // }
25
- // // Ensure output directory exists
26
- // await fs.mkdir(outputDir, { recursive: true })
27
- // try {
28
- // // Extract the database path from the URL (assuming sqlite)
29
- // let dbPath = datasourceUrl
30
- // if (dbPath.startsWith('file:')) {
31
- // dbPath = dbPath.replace('file:', '')
32
- // }
33
- // // Make path relative to current working directory if needed
34
- // if (!path.isAbsolute(dbPath)) {
35
- // dbPath = path.resolve(process.cwd(), dbPath)
36
- // }
37
- // // Use sqlite3 to dump only the schema (no data)
38
- // const schemaOutput = execSync(`sqlite3 "${dbPath}" ".schema"`, { encoding: 'utf8' })
39
- // // Write the schema to the output file
40
- // const outputPath = path.join(outputDir, 'schema.sql')
41
- // await fs.writeFile(outputPath, header + schemaOutput)
42
- // } catch (error) {
43
- // console.error('❌ Failed to generate schema dump:', error.message)
44
- // throw error
45
- // }
46
- // }
47
- // gh.generatorHandler({
48
- // onManifest() {
49
- // return {
50
- // defaultOutput: '../generated',
51
- // prettyName: 'SQL Schema Generator',
52
- // requiresEngines: [],
53
- // }
54
- // },
55
- // async onGenerate(options) {
56
- // await generateSqlSchema(options)
57
- // },
58
- // })
1
+ #!/usr/bin/env tsx
2
+ // biome-ignore-all lint: Generated code
3
+ import { execSync } from 'node:child_process';
4
+ import fs from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import gh from '@prisma/generator-helper';
7
+ const header = `-- This file was generated by sql-schema-generator, do not edit manually.
8
+ -- Generated at: ${new Date().toISOString()}
9
+
10
+ `;
11
+ export async function generateSqlSchema(options) {
12
+ const outputDir = options.generator.output?.value || '../generated';
13
+ const datasourceUrl = 'prisma/dev.db';
14
+ if (!datasourceUrl) {
15
+ throw new Error('No datasource URL found');
16
+ }
17
+ await fs.mkdir(outputDir, { recursive: true });
18
+ try {
19
+ let dbPath = datasourceUrl;
20
+ if (dbPath.startsWith('file:')) {
21
+ dbPath = dbPath.replace('file:', '');
22
+ }
23
+ if (!path.isAbsolute(dbPath)) {
24
+ dbPath = path.resolve(process.cwd(), dbPath);
25
+ }
26
+ const schemaOutput = execSync(`sqlite3 "${dbPath}" ".schema"`, { encoding: 'utf8' });
27
+ const outputPath = path.join(outputDir, 'schema.sql');
28
+ await fs.writeFile(outputPath, header + schemaOutput);
29
+ }
30
+ catch (error) {
31
+ console.error('❌ Failed to generate schema dump:', error);
32
+ throw error;
33
+ }
34
+ }
35
+ gh.generatorHandler({
36
+ onManifest() {
37
+ return {
38
+ defaultOutput: '../generated',
39
+ prettyName: 'SQL Schema Generator',
40
+ requiresEngines: [],
41
+ };
42
+ },
43
+ async onGenerate(options) {
44
+ await generateSqlSchema({
45
+ generator: {
46
+ output: options?.generator?.output ? { value: options.generator.output.value || '' } : undefined,
47
+ },
48
+ });
49
+ },
50
+ });
@@ -0,0 +1,6 @@
1
+ import type { GeneratorOptions } from '@prisma/generator-helper';
2
+ import * as Context from 'effect/Context';
3
+ declare const GeneratorContext_base: Context.TagClass<GeneratorContext, "GeneratorContext", GeneratorOptions>;
4
+ export declare class GeneratorContext extends GeneratorContext_base {
5
+ }
6
+ export {};
@@ -0,0 +1,3 @@
1
+ import * as Context from 'effect/Context';
2
+ export class GeneratorContext extends Context.Tag('GeneratorContext')() {
3
+ }
@@ -0,0 +1,14 @@
1
+ import * as FileSystem from '@effect/platform/FileSystem';
2
+ import * as Path from '@effect/platform/Path';
3
+ import * as Context from 'effect/Context';
4
+ import * as Effect from 'effect/Effect';
5
+ import * as Layer from 'effect/Layer';
6
+ import { GeneratorContext } from './generator-context.js';
7
+ import { RenderService } from './render-service.js';
8
+ declare const GeneratorService_base: Context.TagClass<GeneratorService, "GeneratorService", {
9
+ readonly generate: Effect.Effect<void, Error, GeneratorContext>;
10
+ }>;
11
+ export declare class GeneratorService extends GeneratorService_base {
12
+ static Live: Layer.Layer<GeneratorService, never, FileSystem.FileSystem | Path.Path | RenderService>;
13
+ }
14
+ export {};
@@ -0,0 +1,121 @@
1
+ import * as FileSystem from '@effect/platform/FileSystem';
2
+ import * as Path from '@effect/platform/Path';
3
+ import * as Context from 'effect/Context';
4
+ import * as Effect from 'effect/Effect';
5
+ import * as Layer from 'effect/Layer';
6
+ import { GeneratorContext } from './generator-context.js';
7
+ import { RenderService } from './render-service.js';
8
+ export class GeneratorService extends Context.Tag('GeneratorService')() {
9
+ static Live = Layer.effect(GeneratorService, Effect.gen(function* () {
10
+ const fs = yield* FileSystem.FileSystem;
11
+ const path = yield* Path.Path;
12
+ const { render } = yield* RenderService;
13
+ const parseErrorImportPath = (errorImportPath) => {
14
+ if (!errorImportPath) {
15
+ return null;
16
+ }
17
+ const [modulePath, className] = errorImportPath.split('#');
18
+ if (!(modulePath && className)) {
19
+ throw new Error(`Invalid errorImportPath format: "${errorImportPath}". Expected "path/to/module#ErrorClassName"`);
20
+ }
21
+ return { path: modulePath, className };
22
+ };
23
+ const addExtension = (filePath, extension) => {
24
+ if (!extension) {
25
+ return filePath;
26
+ }
27
+ const ext = path.extname(filePath);
28
+ if (ext) {
29
+ return filePath;
30
+ }
31
+ return `${filePath}.${extension}`;
32
+ };
33
+ const fixSchemaImports = (outputDir) => Effect.gen(function* () {
34
+ const schemasDir = path.join(outputDir, 'schemas');
35
+ const indexFile = path.join(schemasDir, 'index.ts');
36
+ const exists = yield* fs.exists(indexFile);
37
+ if (!exists) {
38
+ return;
39
+ }
40
+ const content = yield* fs.readFileString(indexFile);
41
+ const fixedContent = content
42
+ .replace(/export \* from '\.\/enums'/g, "export * from './enums.js'")
43
+ .replace(/export \* from '\.\/types'/g, "export * from './types.js'");
44
+ if (content !== fixedContent) {
45
+ yield* fs.writeFileString(indexFile, fixedContent);
46
+ }
47
+ });
48
+ const getClientImportPath = (config) => Array.isArray(config.clientImportPath)
49
+ ? config.clientImportPath[0]
50
+ : (config.clientImportPath ?? '@prisma/client');
51
+ const getErrorImportPath = (config) => Array.isArray(config.errorImportPath) ? config.errorImportPath[0] : config.errorImportPath;
52
+ const getImportFileExtension = (config) => Array.isArray(config.importFileExtension) ? config.importFileExtension[0] : (config.importFileExtension ?? '');
53
+ const getCustomError = (config, options, schemaDir) => {
54
+ const errorImportPathRaw = getErrorImportPath(config);
55
+ const importFileExtension = getImportFileExtension(config);
56
+ let customError = parseErrorImportPath(errorImportPathRaw);
57
+ if (customError?.path.startsWith('.')) {
58
+ const outputDir = options.generator.output?.value;
59
+ if (outputDir) {
60
+ const absoluteErrorPath = path.resolve(schemaDir, customError.path);
61
+ const relativeToOutput = path.relative(outputDir, absoluteErrorPath);
62
+ const normalizedPath = relativeToOutput.startsWith('.') ? relativeToOutput : `./${relativeToOutput}`;
63
+ const pathWithExtension = addExtension(normalizedPath, importFileExtension);
64
+ customError = { ...customError, path: pathWithExtension };
65
+ }
66
+ }
67
+ return customError;
68
+ };
69
+ const getGeneratorConfig = (options, schemaDir) => {
70
+ const { config } = options.generator;
71
+ const clientImportPath = getClientImportPath(config);
72
+ const customError = getCustomError(config, options, schemaDir);
73
+ return { clientImportPath, customError };
74
+ };
75
+ const generatePrismaSchema = (outputDir) => Effect.gen(function* () {
76
+ const content = yield* render('prisma-schema', {});
77
+ yield* fs.writeFileString(path.join(outputDir, 'prisma-schema.ts'), content);
78
+ });
79
+ const generatePrismaRepository = (outputDir, clientImportPath) => Effect.gen(function* () {
80
+ const content = yield* render('prisma-repository', { clientImportPath });
81
+ yield* fs.writeFileString(path.join(outputDir, 'prisma-repository.ts'), content);
82
+ });
83
+ const generateModels = (outputDir, models) => Effect.gen(function* () {
84
+ yield* fs.makeDirectory(path.join(outputDir, 'models'), { recursive: true });
85
+ for (const model of models) {
86
+ const content = yield* render('model', { model });
87
+ yield* fs.writeFileString(path.join(outputDir, 'models', `${model.name}.ts`), content);
88
+ }
89
+ });
90
+ const generateIndex = (outputDir, models, clientImportPath, customError) => Effect.gen(function* () {
91
+ const errorType = customError ? customError.className : 'PrismaError';
92
+ const rawSqlOperations = yield* render('prisma-raw-sql', { errorType });
93
+ const modelExports = models.map((m) => `export * from "./models/${m.name}.js"`).join('\n');
94
+ const templateName = customError ? 'index-custom-error' : 'index-default';
95
+ const content = yield* render(templateName, {
96
+ clientImportPath,
97
+ customError,
98
+ rawSqlOperations,
99
+ modelExports,
100
+ });
101
+ yield* fs.writeFileString(path.join(outputDir, 'index.ts'), content);
102
+ });
103
+ const generate = Effect.gen(function* () {
104
+ const options = yield* GeneratorContext;
105
+ const models = options.dmmf.datamodel.models;
106
+ const outputDir = options.generator.output?.value;
107
+ const schemaDir = path.dirname(options.schemaPath);
108
+ if (!outputDir) {
109
+ return yield* Effect.fail(new Error('No output directory specified'));
110
+ }
111
+ const { clientImportPath, customError } = getGeneratorConfig(options, schemaDir);
112
+ yield* fs.makeDirectory(outputDir, { recursive: true });
113
+ yield* generatePrismaSchema(outputDir);
114
+ yield* generatePrismaRepository(outputDir, clientImportPath);
115
+ yield* generateModels(outputDir, models);
116
+ yield* generateIndex(outputDir, models, clientImportPath, customError);
117
+ yield* fixSchemaImports(outputDir);
118
+ });
119
+ return { generate };
120
+ }));
121
+ }
@@ -0,0 +1,10 @@
1
+ import * as Context from 'effect/Context';
2
+ import * as Effect from 'effect/Effect';
3
+ import * as Layer from 'effect/Layer';
4
+ declare const RenderService_base: Context.TagClass<RenderService, "RenderService", {
5
+ readonly render: (templateName: string, data: Record<string, unknown>) => Effect.Effect<string, Error>;
6
+ }>;
7
+ export declare class RenderService extends RenderService_base {
8
+ static Live: Layer.Layer<RenderService, never, never>;
9
+ }
10
+ export {};
@@ -0,0 +1,23 @@
1
+ import * as path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ import * as Context from 'effect/Context';
4
+ import * as Effect from 'effect/Effect';
5
+ import * as Layer from 'effect/Layer';
6
+ import { Eta } from 'eta';
7
+ export class RenderService extends Context.Tag('RenderService')() {
8
+ static Live = Layer.sync(RenderService, () => {
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ const templatesDir = path.resolve(__dirname, '../templates');
12
+ const eta = new Eta({
13
+ views: templatesDir,
14
+ autoEscape: false,
15
+ });
16
+ return {
17
+ render: (templateName, data) => Effect.try({
18
+ try: () => eta.render(templateName, data),
19
+ catch: (error) => new Error(`Failed to render template ${templateName}: ${error}`),
20
+ }),
21
+ };
22
+ });
23
+ }
@@ -0,0 +1,190 @@
1
+ // This file was generated by prisma-effect-generator, do not edit manually.
2
+
3
+ import { Context, Effect, Exit, Layer } from "effect"
4
+ import { Service } from "effect/Effect"
5
+ import { Prisma as PrismaNamespace, PrismaClient as BasePrismaClient } from "<%= it.clientImportPath %>"
6
+ import { <%= it.customError.className %>, mapPrismaError } from "<%= it.customError.path %>"
7
+ import * as Model from "./prisma-repository.js"
8
+
9
+ // Symbol used to identify intentional rollbacks vs actual errors
10
+ const ROLLBACK = Symbol.for("prisma.effect.rollback")
11
+
12
+ // Type for the flat transaction client with commit/rollback control
13
+ type FlatTransactionClient = PrismaNamespace.TransactionClient & {
14
+ $commit: () => Promise<void>
15
+ $rollback: () => Promise<void>
16
+ }
17
+
18
+ /** Transaction options for $transaction and $isolatedTransaction */
19
+ type TransactionOptions = {
20
+ maxWait?: number
21
+ timeout?: number
22
+ isolationLevel?: PrismaNamespace.TransactionIsolationLevel
23
+ }
24
+
25
+ /**
26
+ * Context tag for the Prisma client instance.
27
+ * Holds the transaction client (tx) and root client.
28
+ */
29
+ export class PrismaClient extends Context.Tag("PrismaClient")<
30
+ PrismaClient,
31
+ {
32
+ tx: BasePrismaClient | PrismaNamespace.TransactionClient
33
+ client: BasePrismaClient
34
+ }
35
+ >() {
36
+ static layer = (
37
+ ...args: ConstructorParameters<typeof BasePrismaClient>
38
+ ) => Layer.scoped(
39
+ PrismaClient,
40
+ Effect.gen(function* () {
41
+ const prisma = new BasePrismaClient(...args)
42
+ yield* Effect.addFinalizer(() => Effect.promise(() => prisma.$disconnect()))
43
+ return { tx: prisma, client: prisma }
44
+ })
45
+ )
46
+
47
+ static layerEffect = <R, E>(
48
+ optionsEffect: Effect.Effect<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
49
+ ) => Layer.scoped(
50
+ PrismaClient,
51
+ Effect.gen(function* () {
52
+ const options = yield* optionsEffect
53
+ const prisma = new BasePrismaClient(options)
54
+ yield* Effect.addFinalizer(() => Effect.promise(() => prisma.$disconnect()))
55
+ return { tx: prisma, client: prisma }
56
+ })
57
+ )
58
+ }
59
+
60
+ // Re-export the custom error type for convenience
61
+ export { <%= it.customError.className %> }
62
+
63
+ // Use the user-provided error mapper
64
+ const mapError = mapPrismaError
65
+
66
+ /**
67
+ * Internal helper to begin a callback-free interactive transaction.
68
+ */
69
+ const $begin = (
70
+ client: BasePrismaClient,
71
+ options?: {
72
+ maxWait?: number
73
+ timeout?: number
74
+ isolationLevel?: PrismaNamespace.TransactionIsolationLevel
75
+ }
76
+ ): Effect.Effect<FlatTransactionClient, <%= it.customError.className %>> =>
77
+ Effect.async<FlatTransactionClient, <%= it.customError.className %>>((resume) => {
78
+ let setTxClient: (txClient: PrismaNamespace.TransactionClient) => void
79
+ let commit: () => void
80
+ let rollback: () => void
81
+
82
+ const txClientPromise = new Promise<PrismaNamespace.TransactionClient>((res) => {
83
+ setTxClient = res
84
+ })
85
+
86
+ const txPromise = new Promise<void>((_res, _rej) => {
87
+ commit = () => _res(undefined)
88
+ rollback = () => _rej(ROLLBACK)
89
+ })
90
+
91
+ const tx = client.$transaction((txClient) => {
92
+ setTxClient(txClient)
93
+ return txPromise
94
+ }, options).catch((e) => {
95
+ if (e === ROLLBACK) return
96
+ throw e
97
+ })
98
+
99
+ txClientPromise.then((innerTx) => {
100
+ const proxy = new Proxy(innerTx, {
101
+ get(target, prop) {
102
+ if (prop === "$commit") return () => { commit(); return tx }
103
+ if (prop === "$rollback") return () => { rollback(); return tx }
104
+ return target[prop as keyof typeof target]
105
+ },
106
+ }) as FlatTransactionClient
107
+ resume(Effect.succeed(proxy))
108
+ }).catch((error) => {
109
+ resume(Effect.fail(mapError(error, "$transaction", "Prisma")))
110
+ })
111
+ })
112
+
113
+ /**
114
+ * The main Prisma service with all database operations.
115
+ * Provides type-safe, effectful access to your Prisma models.
116
+ */
117
+ export class Prisma extends Service<Prisma>()("Prisma", {
118
+ effect: Effect.gen(function* () {
119
+ return {
120
+ $transaction: <R, E, A>(
121
+ effect: Effect.Effect<A, E, R>,
122
+ options?: TransactionOptions
123
+ ) =>
124
+ Effect.flatMap(
125
+ PrismaClient,
126
+ ({ client, tx }): Effect.Effect<A, E | <%= it.customError.className %>, R> => {
127
+ const isRootClient = "$transaction" in tx
128
+ if (!isRootClient) {
129
+ return effect
130
+ }
131
+
132
+ return Effect.acquireUseRelease(
133
+ $begin(client, options),
134
+ (txClient) =>
135
+ effect.pipe(
136
+ Effect.provideService(PrismaClient, { tx: txClient, client })
137
+ ),
138
+ (txClient, exit) =>
139
+ Exit.isSuccess(exit)
140
+ ? Effect.promise(() => txClient.$commit())
141
+ : Effect.promise(() => txClient.$rollback())
142
+ )
143
+ }
144
+ ),
145
+
146
+ $isolatedTransaction: <R, E, A>(
147
+ effect: Effect.Effect<A, E, R>,
148
+ options?: TransactionOptions
149
+ ) =>
150
+ Effect.flatMap(
151
+ PrismaClient,
152
+ ({ client }): Effect.Effect<A, E | <%= it.customError.className %>, R> => {
153
+ return Effect.acquireUseRelease(
154
+ $begin(client, options),
155
+ (txClient) =>
156
+ effect.pipe(
157
+ Effect.provideService(PrismaClient, { tx: txClient, client })
158
+ ),
159
+ (txClient, exit) =>
160
+ Exit.isSuccess(exit)
161
+ ? Effect.promise(() => txClient.$commit())
162
+ : Effect.promise(() => txClient.$rollback())
163
+ )
164
+ }
165
+ ),
166
+ <%~ it.rawSqlOperations %>
167
+ }
168
+ })
169
+ }) {
170
+ static layer = (
171
+ ...args: ConstructorParameters<typeof BasePrismaClient>
172
+ ) => Layer.merge(PrismaClient.layer(...args), Prisma.Default)
173
+
174
+ static layerEffect = <R, E>(
175
+ optionsEffect: Effect.Effect<ConstructorParameters<typeof BasePrismaClient>[0], E, R>
176
+ ) => Layer.merge(PrismaClient.layerEffect(optionsEffect), Prisma.Default)
177
+
178
+ }
179
+
180
+ // ============================================================================
181
+ // Deprecated aliases for backward compatibility
182
+ // ============================================================================
183
+
184
+ export const PrismaClientService = PrismaClient
185
+ export const PrismaService = Prisma
186
+ export const makePrismaLayer = PrismaClient.layer
187
+ export const makePrismaLayerEffect = PrismaClient.layerEffect
188
+
189
+ <%~ it.modelExports %>
190
+
@@ -2,9 +2,7 @@
2
2
 
3
3
  import { Context, Data, Effect, Exit, Layer } from "effect"
4
4
  import { Service } from "effect/Effect"
5
- import { Prisma as PrismaNamespace, PrismaClient as BasePrismaClient } from "@prisma/client"
6
- import * as Model from "./prisma-repository.js"
7
- import { _Todo } from "./schemas/index.js"
5
+ import { Prisma as PrismaNamespace, PrismaClient as BasePrismaClient } from "<%= it.clientImportPath %>"
8
6
 
9
7
  // Symbol used to identify intentional rollbacks vs actual errors
10
8
  const ROLLBACK = Symbol.for("prisma.effect.rollback")
@@ -338,38 +336,7 @@ export class Prisma extends Service<Prisma>()("Prisma", {
338
336
  )
339
337
  }
340
338
  ),
341
-
342
- $executeRaw: (args: PrismaNamespace.Sql | [PrismaNamespace.Sql, ...any[]]): Effect.Effect<number, PrismaError, PrismaClient> =>
343
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
344
- Effect.tryPromise({
345
- try: () => (Array.isArray(args) ? client.$executeRaw(args[0], ...args.slice(1)) : client.$executeRaw(args)),
346
- catch: (error) => mapError(error, "$executeRaw", "Prisma")
347
- })
348
- ),
349
-
350
- $executeRawUnsafe: (query: string, ...values: any[]): Effect.Effect<number, PrismaError, PrismaClient> =>
351
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
352
- Effect.tryPromise({
353
- try: () => client.$executeRawUnsafe(query, ...values),
354
- catch: (error) => mapError(error, "$executeRawUnsafe", "Prisma")
355
- })
356
- ),
357
-
358
- $queryRaw: <T = unknown>(args: PrismaNamespace.Sql | [PrismaNamespace.Sql, ...any[]]): Effect.Effect<T, PrismaError, PrismaClient> =>
359
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
360
- Effect.tryPromise({
361
- try: () => (Array.isArray(args) ? client.$queryRaw(args[0], ...args.slice(1)) : client.$queryRaw(args)) as Promise<T>,
362
- catch: (error) => mapError(error, "$queryRaw", "Prisma")
363
- })
364
- ),
365
-
366
- $queryRawUnsafe: <T = unknown>(query: string, ...values: any[]): Effect.Effect<T, PrismaError, PrismaClient> =>
367
- Effect.flatMap(PrismaClient, ({ tx: client }) =>
368
- Effect.tryPromise({
369
- try: () => client.$queryRawUnsafe(query, ...values) as Promise<T>,
370
- catch: (error) => mapError(error, "$queryRawUnsafe", "Prisma")
371
- })
372
- ),
339
+ <%~ it.rawSqlOperations %>
373
340
  }
374
341
  })
375
342
  }) {
@@ -392,6 +359,5 @@ export const PrismaService = Prisma
392
359
  export const makePrismaLayer = PrismaClient.layer
393
360
  export const makePrismaLayerEffect = PrismaClient.layerEffect
394
361
 
395
- export class TodoModel extends Model.Class<TodoModel>("Todo")({
396
- ..._Todo.fields
397
- }) {}
362
+ <%~ it.modelExports %>
363
+